idekCTF 2022* Writeup
In this CTF we (ISwearIGoogledIt) achieve 99 position.
SimpleFileServer [web][98 solves]
Keywords: flask, cookie
This web application challenge involves a website in which we are able to upload zip files and view the files inside the zip. If we check the Dockerfile
we can see the files and folders that are involved in the challenge.
In the Dockerfile
we can also see that there is a flag binary with SUID activated in order to exfiltrate the flag. This behavior is also shown in the app.py
file. One of the first approaches to analyze this type of challenges is to check where the flag is and how the challenge is prepared to be solved. In this case, there is a /flag
route in which we need to set admin
to True in our cookie to solve the chall. Flask applications use SECRET_KEY
to sign the cookie, so we need to figured out how this variable is generated.
This application allow us to upload one zip of less than 2MB. After that, the zip is unzipped inside /tmp/uploads/{uuid}/
folder. In this moment, symlinks
appeared in my mind and I found a ctf writeup of this type of vulnerabilities (link). So, we just need to create a symbolic link ln -s /tmp/server.log bubu.link
and create a zip that preserves symlinks zip --symlink bubu.zip bubu.link
. Using this technique, we are able to exfiltrate any file but flag, because of the 600 permission.
In this moment, my idea was to exfiltrate the SECRET_KEY
. For exfiltrating SECRET_KEY
, I tried using the environment in which the SECRET_KEY
was declared using files such as /proc/self/environ
. After trying that approach for some time, I remember server.log
file and config.py
in which SERVER_KEY
was created. In the following screenshots first entries of server.log
and the code of config.py
are shown.
After downloading the real config.py
, we have the snippet code where SECRET_KEY
is generated. If we want to generate the same key generated with random, we just need to use the same seed. In this creation, the only value that we don’t know is the time.time()
. This is when server.log
comes to the scene. In server.log
file we can found the time when the server was started so we just need to bruteforce several seconds before this time. In others words, the time.time()
is used before the first entry time shown in server.log
. SECRET_KEY
is generated few seconds before server started up.
In the final exploit, I bruteforced the time in miliseconds to generate the exact seed that generated the same SECRET_KEY
. In my case, to verify the SECRET_KEY
I used the code of flask-unsign repository. After finding the exact time for random.seed
we signed our new cookie with admin=True
and we navigated to /flag
endpoint. The final exploit snippet code was:
import random
import sys
from session import verify, decode, sign
session = "eyJhZG1pbiI6bnVsbCwidWlkIjoiYSJ9.Y8NJdQ.t-Orpm8NJN1OcTRqzI1SJsx_hks"
# Check verify method
#print(verify("eyJsb2dnZWRfaW4iOnRydWV9.XDuW-g.cPCkFmmeB7qNIcN-ReiN72r0hvU", "CHANGEME"))
# Bruteforce
start = 1673737312
# 1673737412
end = 1673737415
SECRET_OFFSET = -67198624
while start < end:
start = round(start,3)
random.seed(round((start + SECRET_OFFSET) * 1000))
key = "".join([hex(random.randint(0, 15)) for x in range(32)]).replace("0x", "")
print(start)
if verify(session, key) == True:
#key = "e897071bf3d5dc6ff7882fc0b64ece5c"
print("==="*20)
print(sign({'admin':True, 'uid': 'a'}, key))
sys.exit(0)
start += 0.001
Finally, we just need to replace the cookie with our admin cookie and the flag appears. Tachan!!
Thanks for reading!