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.
Solution
- Step 1Capture the Flask session cookieLog in to the site (any username works). Open browser DevTools > Application > Cookies and copy the value of the 'session' cookie. Flask session cookies are base64-encoded JSON payloads followed by an HMAC signature, separated by a dot.
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. - Step 2Brute-force the secret key with flask-unsignInstall 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.pip install flask-unsignflask-unsign --unsign --wordlist cookie-names.txt --cookie "<your_session_cookie_value>"
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).You can also use the wordlist from the challenge source (if provided) or try common wordlists. The key "snickerdoodle" is a type of cookie -- the challenge theme is literal.
- Step 3Forge an admin session cookieOnce 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.flask-unsign --sign --secret 'snickerdoodle' --cookie "{'very_auth':'admin'}"# Then set this value as your session cookie in the browser
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.
Flag
picoCTF{...}
Flask session cookies are HMAC-signed with the SECRET_KEY -- a weak or guessable key lets anyone forge a valid session with arbitrary contents.