Cookie Monster Secret Recipe picoCTF 2025 Solution

Published: April 2, 2025

Description

Cookie Monster's login page sets a secret_recipe cookie that already contains the flag. Harvest the cookie and decode it from Base64.

Submit username=test, password=test (or any pair). The page accepts and sets cookies regardless, which proves auth isn't enforced.

Open DevTools and copy the secret_recipe cookie value (Chrome/Edge: Application > Cookies; Firefox: Storage > Cookies; Safari: Develop > Web Inspector > Storage).

URL-decoding gotcha: cookie values in DevTools are URL-encoded (%20, %3D). URL-decode before Base64-decoding. One-liner: python3 -c 'import urllib.parse; print(urllib.parse.unquote(...))'.

bash
curl -i http://verbal-sleep.picoctf.net:64848/ -d 'username=a&password=a' | grep -i set-cookie

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Dump the cookie
    Observation
    I noticed the challenge name references a 'secret_recipe' cookie and the login page sets cookies regardless of credentials, which suggested the flag was stored directly in a cookie value rather than behind any authentication gate.
    Look for secret_recipe. Its value is Base64, often URL-encoded so the trailing == becomes %3D%3D. Substitute the %3D back to = (or feed through urllib.parse.unquote) before decoding.
    What didn't work first

    Tried: Looking in the Network tab request headers instead of the Application tab cookie storage.

    The Network tab shows the Cookie request header (what the browser sends) and the Set-Cookie response header (what the server sets), but the values there are harder to copy cleanly because they are buried in header walls. The Application tab (Chrome/Edge) or Storage inspector (Firefox) lists each cookie as a named row with its decoded key and raw value, which is what you want to copy and then decode.

    Tried: Pasting the cookie value straight into a Base64 decoder without removing the URL encoding first.

    If the value contains %3D instead of =, a Base64 decoder sees an invalid character and either errors out or silently truncates the output. The %3D is URL encoding for the = padding that Base64 uses at the end of its output. You must URL-decode the string first (replace %3D with = and %2B with +) before feeding it to a Base64 decoder.

    Learn more

    HTTP cookies are key-value pairs set by the server via a Set-Cookie response header and automatically sent by the browser in subsequent requests to the same origin. Cookies are visible to anyone who can inspect network traffic or browser developer tools - they are not secret by default. Sensitive information should never be stored directly in a cookie unless it is encrypted and signed.

    URL encoding (also called percent-encoding) is used in cookies because some characters like =, +, and / have special meaning in HTTP headers and URLs. The %3D sequence represents the equals sign =, which is the Base64 padding character. A browser or curl will decode percent-encoding automatically; command-line tools like echo need you to substitute %3D with = first (or use python3 -c "import urllib.parse; print(urllib.parse.unquote(...))").

    In the browser's DevTools Application tab (Chrome/Edge) or Storage inspector (Firefox), you can see all cookies for the current domain, their values, expiry, security flags (HttpOnly, Secure, SameSite), and domain scope. This is the fastest way to inspect cookie values during web security challenges or reconnaissance. The Cookie and JWT CTF guide covers signed-cookie tampering, JWT alg=none, and other cookie attacks beyond plain decoding.

  2. Step 2
    Decode the blob
    Observation
    I noticed the cookie value contained only alphanumeric characters with URL-encoded padding (%3D at the end), which are the signatures of a Base64-encoded string and indicated that URL-decoding followed by Base64 decoding would reveal the flag.
    Either paste into CyberChef or pipe through base64 -d to reveal picoCTF{...}.
    What didn't work first

    Tried: Trying to decode the value directly in the terminal with echo 'value' | base64 -d before URL-decoding, resulting in garbled output or an error.

    The shell echo command passes the percent-encoded string literally to base64, which chokes on % characters since they are not valid Base64 alphabet characters. The fix is to URL-decode first - either with python3 urllib.parse.unquote, or by manually swapping %3D back to = and %2F back to / before running base64 -d.

    Tried: Assuming the decoded output needs further decoding because it looks like random text at first glance.

    Base64 output sometimes includes forward slashes, plus signs, and uppercase letters that look scrambled until you read it carefully. If the decoded string starts with picoCTF{ then you already have the flag and no extra step is needed. Applying a second round of Base64 decoding to an already-decoded string produces garbage.

    Learn more

    Storing data as Base64 in a cookie is a pattern seen in many web frameworks for session management and state passing. Flask's default session cookie, for example, stores a Base64-encoded JSON object signed with a secret key. Without the signature, reading the cookie requires only decoding - it is not encrypted. This challenge demonstrates an even simpler case: no signing at all, just raw Base64.

    When performing web application security assessments, inspecting every cookie for Base64-encoded content is a standard early step. Tools like Burp Suite automatically detect and decode Base64 in requests and responses. The Burp Suite for picoCTF guide covers the Decoder shortcut and the Repeater loop you would use to re-send a tampered cookie back to the server. CyberChef's "Magic" recipe can identify the encoding automatically and chain decoding operations.

    The secure alternative is to store only an opaque, cryptographically random session ID in the cookie, and keep all sensitive data server-side in a session store. Frameworks like Django, Rails, and Spring all do this by default. Putting sensitive data client-side requires authenticated encryption (like AES-GCM) to prevent reading and tampering. See CTF Encodings for a quick reference of how to spot Base64, URL-encoded, hex, and ROT13 values at a glance.

Interactive tools
  • Flask Session DecoderDecode Flask / itsdangerous session cookies. Splits payload, decompresses zlib, parses JSON, and verifies the HMAC signature when given the secret.
Alternate Solution

Once you copy the cookie value, decode it instantly with the Base64 Decoder and then the URL Encoder / Decoder on this site - both tools run in the browser with no install required. Decode the percent-encoding first (to restore any %3D padding), then Base64-decode the result.

Flag

Reveal flag

picoCTF{...}

No login bypass is necessary; the secret is literally in the cookie jar.

Key takeaway

HTTP cookies are transmitted to the browser and stored in plain sight; encoding their contents with Base64 provides no confidentiality because Base64 is a reversible transform with no key. Any web application that stores sensitive data directly in a cookie, rather than a server-side session keyed by an opaque token, exposes that data to anyone with browser DevTools or a network proxy. The same pattern appears wherever client-controlled storage (localStorage, URL parameters, hidden form fields) is mistaken for a security boundary.

How to prevent this

Base64 is encoding, not encryption. Treat anything sent to the client as readable.

  • Never put secrets, internal IDs, or user state directly in a cookie. Store only an opaque random session ID; keep the actual data server-side in a session store (Redis, database, signed JWT with a server-only key).
  • If client-side state is unavoidable, sign it (HMAC) so tampering is detected, and encrypt it (AES-GCM) so reading is blocked. Most frameworks (Django, Rails, Express cookie-session) ship this out of the box.
  • Set HttpOnly, Secure, and SameSite=Lax (or Strict) on every auth cookie so JS can't read it and CSRF attacks are blunted.

Related reading

Want more picoCTF 2025 writeups?

Tools used in this challenge

Do these first

What to try next