Description
After a login POST, the server issues two sequential 302 redirects whose Location headers carry Base64-encoded flag fragments. Capture the intermediate redirects in the browser DevTools Network tab to recover them.
Setup
Open the website, enter username test and password test!, and keep the Network tab open with "Preserve logs" enabled.
Copy the id parameters returned in the response; they are Base64 fragments of the flag.
id=cGlj...VzX2Fsid=bF90aG...YmJhZTlhfQ==Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Capture the responseObservationI noticed the login form triggers a POST that is followed by 302 redirects, which suggested the server was embedding data in the Location header before the final page loaded, making the Network tab with 'Preserve log' enabled the only way to see those intermediate responses.After submitting the test credentials, the POST /login response includes two id query parameters in the redirect URL (302 Location header). Highlight them in the Network tab's Headers pane and copy each id value.What didn't work first
Tried: Checking the Response or Preview sub-panel of the POST /login request for the id values.
The id parameters are not in the response body - they are embedded in the Location header of the 302 redirect. Beginners often scan the Preview and Response tabs first and find them empty, concluding the server sent nothing useful. The Headers sub-panel (specifically the Response Headers section) is where the Location header lives.
Tried: Submitting the form without first enabling Preserve log in the Network tab.
Without Preserve log, the browser clears the Network panel when it follows the 302 redirect, so the intermediate POST /login entry disappears and only the final landing page request remains visible. The fix is to check Preserve log (or Preserve messages) before clicking the login button so all intermediate responses stay in the panel.
Learn more
Browser DevTools Network tab records every HTTP request and response made by the page, including XHR/Fetch calls triggered by form submissions. With "Preserve log" enabled, the history persists across page navigations, which is critical here because the server redirects after login and would otherwise clear the log.
Concretely, in this challenge the login form submits a
POST /loginwithusername=test&password=test!. The server returns302 Foundwith aLocationheader that includes a query string like?id=cGlj...VzX2Fs&id=bF90aG...YmJhZTlhfQ==. Bothidvalues are in the redirect URL itself. The browser will follow the redirect automatically, so without "Preserve log" the intermediate 302 disappears and you only see the final landing page.Clicking on the
POST /loginrequest in the Network tab reveals four sub-panels: Headers (where theLocationheader carrying the ids lives), Payload (POST body you sent), Preview, and Response. Theidparameters in this challenge are not hidden form fields and not in the response body; they are query parameters appended to the redirect URL.Real-world relevance: APIs sometimes return internal identifiers, session tokens, or other sensitive strings in response bodies or redirect URLs that developers intended only for internal use. Tools like Burp Suite automate the capture and replay of these requests for deeper analysis. HTTP redirects (status codes 301, 302, 307, 308) are handled automatically by browsers, meaning the intermediate responses are invisible unless you specifically preserve them.
Step 2
Decode and concatenateObservationI noticed the second id parameter value had trailing '==' padding (a hallmark of Base64 encoding) while the first had none, and that neither decoded fragment alone contained a closing brace, suggesting the flag had been split across two independently encoded chunks that needed to be decoded separately and then joined.Base64-decode each id separately, then join the strings to form the complete picoCTF flag.What didn't work first
Tried: Decoding both id values together as a single concatenated Base64 string before splitting.
Joining the raw Base64 strings before decoding produces garbage because each fragment is independently padded. The second fragment ends with == padding, which marks it as a self-contained Base64 unit; but even the first fragment, which has no trailing = characters, encodes an exact number of bytes that does not align with the second fragment's starting point. Decode each fragment separately, then concatenate the resulting plaintext strings.
Tried: Treating the decoded output of just the first id value as the complete flag.
The first fragment decodes to something that starts with picoCTF{ but ends mid-word, without a closing brace. Beginners sometimes assume a partial decode is a corrupted result and try re-encoding or using a different decoder. The flag is intentionally split across two fields, so both fragments must be decoded and joined in order to get the full picoCTF{...} string.
Learn more
Splitting a secret across multiple fields is a simple obfuscation technique. Each
idvalue decodes to a fragment of the flag; concatenating them in order reconstructs the full string. This mirrors how real applications sometimes split tokens across multiple cookies or headers - a pattern that can leak partial secrets even when individual components look innocuous.To decode Base64 in the terminal:
echo 'cGlj...' | base64 --decode. In Python:import base64; base64.b64decode('cGlj...').decode(). CyberChef's "From Base64" operation handles it visually. Remember that Base64 strings always have a length that is a multiple of 4; padding=characters fill gaps when the input length isn't divisible by 3.The broader lesson: never treat Base64 as a security measure. It is transparent encoding, not encryption. Treat any Base64 data found during reconnaissance as plaintext that simply needs one decoding step.
Recognizing Base64 on sight is a useful habit. Base64 strings use the characters
A-Z,a-z,0-9,+, and/, and are padded with=to make the total length a multiple of 4. URL-safe Base64 replaces+with-and/with_to avoid conflicts with URL syntax. If you see a long string of alphanumerics ending in one or two=characters, Base64 is almost always the right first guess.Splitting secrets across multiple fields or responses is a simple obfuscation pattern that can also appear in malware command-and-control (C2) communications. A C2 server might return a multi-part command spread across multiple HTTP responses, each looking innocuous on its own, to evade pattern-based network detection. Reassembling and decoding these fragments is a standard malware analysis task. The skills developed in this challenge - capturing all responses, extracting and joining fragments, then decoding - map directly to real incident response work.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Base64 & Base32 DecoderDecode Base64 and Base32 strings with auto-detection. Multi-layer mode unwraps nested encodings automatically.
- Recipe ChainStack decoders into a pipeline: Base64, hex, ROT, XOR, Morse, URL, Atbash, Vigenère, and more. Magic mode auto-discovers the chain. Bookmark the URL to save it.
Alternate Solution
Once you collect the Base64 fragments from the network responses, concatenate them and decode the result with the Base64 Decoder on this site - paste the joined string and click decode to reveal the flag without any terminal commands.
Flag
Reveal flag
picoCTF{prox...bae9a}
The fragments must be concatenated before submitting.