Description
A mysterious file named enc_flag hides nested encodings. Your task is to unwrap each layer until the plain-text flag appears.
Fetch enc_flag from a shell where you can chain decoding utilities.
Detect how deep the nesting goes: try CyberChef Magic, or loop base64 -d until picoCTF{ appears.
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Detect how many layers you have
ObservationThe file looks like Base64 at a glance, and one decode produces another Base64-looking blob. The same encoding was applied several times over, so loop until a sentinel tells you the depth.Loop base64 decoding and stop when the output contains picoCTF{. That confirms the depth without guessing.pythonpython3 - <<'PY' import base64, pathlib data = pathlib.Path('enc_flag').read_bytes().strip() for i in range(1, 20): try: data = base64.b64decode(data) except Exception as e: print(f'layer {i}: decode failed ({e})') break if b'picoCTF{' in data: print(f'layer {i}: found flag -> {data.decode(errors="replace")}') break print(f'layer {i}: still encoded ({data[:32]!r}...)') PYExpected output
layer 1: still encoded (b'VjFSQ2EyTXlSblJUV0dSVllrWmFWRmx0'...) layer 2: still encoded (b'V1RCa2MyRnRTWGRVYkZaVFltNVNjRmRX'...) layer 6: found flag -> picoCTF{base64_n3st...e523f49}What didn't work first
Tried: Run 'base64 -d enc_flag' once and assume the result is plaintext.
A single decode emits another Base64-looking blob, not the flag. The loop output shows at least six layers; stopping after one leaves five layers of encoding still in place. The sentinel-driven loop exists precisely to discover the real depth rather than assuming it.
Tried: Pipe the file through 'strings enc_flag' to find the flag without decoding.
Base64 characters are all printable ASCII, so 'strings' returns the raw encoded blob unchanged - it has no way to detect or reverse the encoding. The flag is not present as a literal string at any intermediate layer; it only appears after all layers are decoded.
Learn more
Nested Base64 encoding means the data has been Base64-encoded several times in a row. Each layer wraps the previous output, so to recover the original you decode exactly as many times as it was encoded. Use the self-terminating loop above to learn the exact count for your instance rather than assuming a fixed number.
The loop above is more robust than counting layers by eye: it stops as soon as
picoCTF{appears and tells you how many decodes that took. If the output ever stops looking like Base64 (mixed case + digits +=padding) before the flag shows, the next layer is probably hex, gzip, or a different encoding instead.Step 2Apply the fixed-depth decode chain (once you know N)
ObservationThe detection loop reports a specific layer count. Build a flat pipeline of exactly that many decode stages and the flag comes out in one command.Once the loop tells you the depth N, a flat shell pipeline reads enc_flag once and pipes through N-1 more decodes. The example below shows six decodes; adjust the number of stages to the N your loop reported.bashbase64 --decode enc_flag | base64 --decode | base64 --decode | base64 --decode | base64 --decode | base64 --decodeExpected output
picoCTF{base64_n3st...e523f49}What didn't work first
Tried: Hard-code six decode stages without first running the detection loop, then get no output.
The layer count is not guaranteed to be six on every instance. At a different depth the pipeline either errors out or hands back a partly decoded blob that still is not the flag. That is what the detection loop in step 1 is for.
Tried: Use 'base64 -d' (BSD/macOS default) and see 'invalid input' errors mid-pipeline.
BSD base64 is stricter about whitespace and newlines than GNU base64, so it can reject an intermediate decode. Use GNU base64 (Linux, or coreutils on macOS) or the Python loop from step 1 instead.
Learn more
The first
base64 --decode enc_flagreads the file directly (no UUOC) and emits decoded bytes. Each subsequent| base64 --decodepeels another layer. After N total decodes (six in this example) you land on plain ASCII containing the flag; if it has not appeared, add or remove a stage.Want a sanity-check sentinel inside the pipeline? Append
| grep -o "picoCTF{[^}]*}". If the flag has not surfaced, grep prints nothing and you know the depth was off by at least one.Step 3Alternative: CyberChef Magic
ObservationCyberChef's Magic operation fingerprints an encoding and cascades decoders on its own. For straightforward multi-layer Base64 it is a one-click alternative to writing a script.CyberChef's Magic operation auto-detects multi-layer Base64 and decodes until the output stops looking encoded.Learn more
CyberChef ships a Magic operation that fingerprints the input and tries cascades of decoders until the output stops looking encoded. For pure multi-layer Base64 it nails this challenge in one click.
Magic's limits matter though: it does not handle hex-or-compressed layers cleanly, so if a layer mid-stack is gzipped, XOR'd with a key, or reversed, Magic gives up. In those cases drop into the recipe panel and chain operations manually.
Interactive tools
- Base64 & Base32 DecoderDecode Base64 and Base32 strings with auto-detection. Multi-layer mode unwraps nested encodings automatically.
- Cipher Identifier & Auto-DecoderPaste any ciphertext and the tool auto-runs every common decoder (base64, hex, Morse, ROT, Atbash, Bacon, binary, decimal, URL) and ranks the results by English-likeness.
Flag
Reveal flag
picoCTF{base64_n3st...e523f49}
Each Base64 decode peels a layer; run the self-terminating loop until picoCTF{ appears (the depth is fixed per instance) to reveal the flag.