Description
Mmm, I wonder what's inside this cookie. The server is baked with Flask.
Setup
Navigate to the challenge URL and log in to obtain a session cookie.
Confirm flask-unsign is installed before brute-forcing.
# Open the challenge URL and note the session cookie in DevToolspip show flask-unsignSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Capture the Flask session cookie
ObservationThe description says the server is baked with Flask, so authentication rests on Flask's signed session cookies. Inspecting the raw cookie value reveals the payload structure and the field names that control access.Log in to the site (any username works). Open DevTools > Application > Cookies and copy the 'session' cookie. A captured value looks like a base64-ish blob with two dots separating three segments (payload.timestamp.signature).Learn more
Flask session cookies are signed with HMAC-SHA1 using the app's
SECRET_KEY. The format isbase64(json_payload).base64(timestamp).hmac_signature. Without the secret key, an attacker cannot forge a valid signature. But if the secret key is weak or guessable, the cookie can be cracked and reforged with arbitrary contents.Inspect the payload first. Take the first segment (before the first dot), URL-safe base64 decode it, and you get the JSON. That is how you discover the field name
very_authand its current value (e.g.,guest):echo '<first_segment>' | base64 -d. Knowing the exact field name matters because the server checks a specific key, not just any field that says "admin".Step 2Brute-force the secret key with flask-unsign
ObservationThe server source keeps a list of cookie names and picks its SECRET_KEY from that same list. So run flask-unsign against a wordlist built from those names, rather than a generic password list.Install flask-unsign and run it against the session cookie with a wordlist. The secret key is 'snickerdoodle' - a cookie name that appears in the server's list of cookies.bashpip install flask-unsignbashflask-unsign --unsign --wordlist cookie-names.txt --cookie "<your_session_cookie_value>"Expected output
[*] Valid secret key found: snickerdoodle
What didn't work first
Tried: Running flask-unsign with a generic password wordlist like rockyou.txt instead of a cookie-names wordlist
rockyou.txt holds passwords, not cookie names, so it contains none of the names the server lists. The source picks the SECRET_KEY from its own cookie-name array, so only a wordlist drawn from that array will hit. rockyou.txt finds nothing and burns time on millions of irrelevant candidates.
Tried: Trying to decode the full cookie manually with base64 -d without using flask-unsign to verify the signature
Decoding the payload shows the JSON structure, the 'very_auth' field included, but it does not hand you the secret key. Without that key you cannot produce a valid HMAC signature, so the server rejects any hand-crafted cookie on a signature mismatch. flask-unsign both cracks the key and re-signs the new payload.
Learn more
flask-unsign is a tool specifically designed for attacking Flask session cookies. It tries each word in a wordlist as the potential
SECRET_KEY, verifying whether it produces a valid HMAC signature for the given cookie. The wordlist here should be a list of cookie names (the challenge hints at this - the server uses a cookie name as its secret key).Where the wordlist comes from. View the challenge source (the linked Flask app, usually a
server.py) and find the array of valid cookie names presented on the login page. Copy the list line-for-line intocookie-names.txt, one per line. That is your wordlist; the secret key is one of those names verbatim. The key "snickerdoodle" is a type of cookie - the challenge theme is literal.Step 3Forge an admin session cookie
ObservationDecoding the original session payload shows a 'very_auth' field set to 'guest'. With the recovered secret key in hand, sign a new cookie with that field set to 'admin'.Once you have the secret key, use flask-unsign to sign a new session cookie with admin privileges. Replace your browser cookie with the forged value and refresh the page to see the flag.bashflask-unsign --sign --secret 'snickerdoodle' --cookie "{'very_auth':'admin'}"bash# Then set this value as your session cookie in the browserWhat didn't work first
Tried: Signing the cookie with the field name 'admin' set to true instead of using the exact key 'very_auth' with the value 'admin'
The server reads one specific key, 'very_auth', out of the session dict. A cookie carrying 'admin' or 'is_admin' instead matches nothing, so the page still treats you as a guest. Read the decoded payload for the exact field name before forging anything.
Tried: Setting the forged cookie value in the request headers manually using curl instead of replacing it in the browser
This can work, but it is easy to forget the URL-encoding, or to wrap the value in quotes wrongly, and the server then sees a malformed cookie and drops the session. Safer to paste the forged value into DevTools under Application and Cookies, where the browser handles encoding on the next load.
Learn more
With the secret key known, you can sign any JSON payload and the server will accept it as authentic. The
very_authfield controls access level - setting it toadmingrants privileged access. This is why Flask's documentation strongly warns against using guessable values forSECRET_KEY: it should be at least 24 bytes of high-entropy random data.The correct fix is to generate a strong random key:
python3 -c "import secrets; print(secrets.token_hex(32))"and store it in an environment variable, never hardcoded in source.
Interactive tools
- Flask Session DecoderDecode Flask / itsdangerous session cookies. Splits payload, decompresses zlib, parses JSON, and verifies the HMAC signature when given the secret.
Flag
Reveal flag
picoCTF{pwn_4ll_th3_cook1E5_...}
Flask session cookies are HMAC-signed with the SECRET_KEY - a weak or guessable key lets anyone forge a valid session with arbitrary contents.