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!