Contest Source: COMP6[84]41 CTF
Note that all flags have been replaced with “COMP6841{REDACTED}”. This is to discourage you from just blindly submitting the final answer, and to encourage you to follow along and learn something along the way.
We’re given app.py
, a script that runs a Flask web server. Opening it up, we see
#!/usr/bin/env python3
from flask import Flask, request
from requests import get
import os
= Flask(__name__)
app
@app.route("/api/list_files")
def list_files():
= request.args.get('directory')
d return '\n'.join(os.listdir(d))
@app.route("/api/read_file")
def read_file():
# To prevent people from the Internet reading files :D
if request.remote_addr != '127.0.0.1':
return 'Uk9UMTM6dWdnY2Y6Ly9qamoubGJoZ2hvci5wYnovam5ncHU/aT1xRGo0ajlKdEtwRA=='
= request.args.get('path')
p with open(p, 'r') as f:
return f.read()
@app.route("/api/get_request")
def get_request():
= request.args.get('url')
u return get(u).text
if __name__ == '__main__':
=False, host='0.0.0.0', port=1337) app.run(debug
We’re also given a link to http://13.210.180.94:10000/ which shows us a “URL Not Found” page. This makes sense, since this isn’t one of the routes specified in app.py
.
Let’s now take a look at each of these routes in detail:
/api/list_files
/api/read_files
127.0.0.01
, it just gives us that random base64 message.
ROT13:uggcf://jjj.lbhghor.pbz/jngpu?i=qDj4j9JtKpD
/api/get_request
We now have all the information we need to run an SSRF attack
SSRF stands for Server Side Request Forgery. Servers request resources all the time when displaying webpages. For example, it may request an image to be displayed, and the image is stored at another server. SSRF means making the server request something on the user’s behalf, which can have unintended consequences. For example, in this case, we can make the server request flag.txt
via it’s own get_request
api.
First the URL we are trying to access is http://13.210.180.94:10000/api/read_file?path=flag.txt
. Using a HTML URL Encoder, we get http%3A%2F%2F13.210.180.94%3A10000%2Fapi%2Fread_file%3Fpath%3Dflag.txt
. Putting this as an argument into the GET request, we have
Unfortunately, accessing this gives the same base64 message. It seems that since we used the public IP address to access the server, the server also used it’s own public IP address as the remote address, thus triggering the message to be returned.
Let’s change our URL now to be localhost:1337/api/read_file?path=flag.txt
. Note that we change the port to 1337
as defined in app.py
instead of the previous 10000
. This is because the server has port forwarding setup. The app is actually running on port 1337. However, from the outside connecting in, they connect to port 10000
, which is then forwarded to our app. If we’re connecting from the inside, we should use the correct port. Putting this together gives us the URL
and accessing it gets us our flag!