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!