Most Cookies picoCTF 2021 Solution

Published: April 2, 2026

Description

Mmm, I wonder what's inside this cookie. The server is baked with Flask.

Remote

Navigate to the challenge URL and log in to obtain a session cookie.

Confirm flask-unsign is installed before brute-forcing.

bash
# Open the challenge URL and note the session cookie in DevTools
bash
pip show flask-unsign

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Capture the Flask session cookie
    Observation
    I noticed the challenge description says the server is baked with Flask, which suggested that the authentication mechanism relies on Flask's signed session cookies and that inspecting the raw cookie value would reveal the payload structure and field names controlling 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 is base64(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_auth and 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".

  2. Step 2
    Brute-force the secret key with flask-unsign
    Observation
    I noticed the server source code stores a list of cookie names and derives its SECRET_KEY from that same list, which suggested using flask-unsign with a targeted wordlist of those cookie names to crack the weak, guessable key 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.
    bash
    pip install flask-unsign
    bash
    flask-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 contains passwords, not cookie names, so it will not contain 'snickerdoodle' or any of the other cookie names the server lists. The server's source code explicitly picks the SECRET_KEY from its own cookie-name array, so only a wordlist sourced from that array will find it. Using rockyou.txt produces no match and wastes significant 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 segment reveals the JSON structure (like the 'very_auth' field), but that does not give you the secret key. Without the secret key you cannot produce a valid HMAC signature for a forged payload, so the server will reject any manually crafted cookie with a signature mismatch error. flask-unsign is needed to both crack the key and re-sign 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 into cookie-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.

  3. Step 3
    Forge an admin session cookie
    Observation
    I noticed that decoding the original session payload revealed a 'very_auth' field set to 'guest', and with the recovered secret key 'snickerdoodle' in hand, this suggested signing a new cookie with 'very_auth' set to 'admin' to gain elevated access.
    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.
    bash
    flask-unsign --sign --secret 'snickerdoodle' --cookie "{'very_auth':'admin'}"
    bash
    # Then set this value as your session cookie in the browser
    What 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 checks the specific key 'very_auth' in the session dict. Sending a cookie like {'admin': true} or {'is_admin': 1} does not match what the server reads, so the page still treats you as a guest. You must inspect the original decoded payload to learn the exact field name before forging.

    Tried: Setting the forged cookie value in the request headers manually using curl instead of replacing it in the browser

    This can work but a common mistake is forgetting to URL-encode the cookie value or wrapping it incorrectly with quotes, causing the server to see a malformed cookie and reject the session entirely. The safer path for this challenge is to paste the forged value directly in DevTools > Application > Cookies so the browser handles encoding automatically on the next page load.

    Learn more

    With the secret key known, you can sign any JSON payload and the server will accept it as authentic. The very_auth field controls access level - setting it to admin grants privileged access. This is why Flask's documentation strongly warns against using guessable values for SECRET_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.

Key takeaway

HMAC signatures on session tokens are only as strong as the secret key used to produce them. A weak or guessable SECRET_KEY (a common word, a project name, a default placeholder) allows offline brute-force: the attacker iterates candidate keys against the captured token until the HMAC matches, then reforges the token with any payload. This pattern appears in Flask apps, Django signed cookies, and JWT tokens using HS256, making secret-key strength a critical deployment concern across the web ecosystem.

Related reading

Want more picoCTF 2021 writeups?

Tools used in this challenge

What to try next