Cache
We are presented with a webpage, and we have to provide a URL such that we can eventually read a local file, and the /flag
endpoint. We are given the code for this, an obvious solution would be to simply provide a URL such as http://localhost:1337/flag
, but in the source code we can see that local IP addresses are disallowed.
def cache_web(url):
scheme = urlparse(url).scheme
domain = urlparse(url).hostname
if scheme not in ['http', 'https']:
return flash('Invalid scheme', 'danger')
def ip2long(ip_addr):
return unpack("!L", socket.inet_aton(ip_addr))[0]
def is_inner_ipaddress(ip):
ip = ip2long(ip)
return ip2long('127.0.0.0') >> 24 == ip >> 24 or \
ip2long('10.0.0.0') >> 24 == ip >> 24 or \
ip2long('172.16.0.0') >> 20 == ip >> 20 or \
ip2long('192.168.0.0') >> 16 == ip >> 16 or \
ip2long('0.0.0.0') >> 24 == ip >> 24
try:
if is_inner_ipaddress(socket.gethostbyname(domain)):
return flash('IP not allowed', 'danger')
return serve_screenshot_from(url, domain)
except Exception as e:
return flash('Invalid domain', 'danger')
def is_from_localhost(func):
@functools.wraps(func)
def check_ip(*args, **kwargs):
if request.remote_addr != '127.0.0.1':
return abort(403)
return func(*args, **kwargs)
return check_ip
We can see that this explicitly disallows a local IP as if we try it, we get an error back:
Instead, what we can do is we can provide it a URL that contains a reference to the flag (http://localhost:1337/flag
), as this will pass the initial check it should work (🤞) as the actual URL does not resolve to a local IP address, it just triggers a further request to the local resource. We can do this using Cloudflare Workers as needs to be a publicly accessible server.
All this does is include the contents of http://localhost:1337/flag
in an iframe
.
When we do this, we get the flag displayed in an image!