Skip to main content

repetitions picoCTF 2023 Solution

Decode a flag that has been hidden behind multiple layers of the same encoding scheme.

Published: April 26, 2023Updated: August 25, 2026

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.

Walk me through it
The fastest browser-based approach is the Base64 & Base32 Decoder in Multi-layer Base64 mode, which unwraps every layer in one pass. For background on Base64 and the rest of the encoding zoo you will meet in CTFs, see the CTF encodings guide.
  1. Step 1Detect how many layers you have
    Observation
    The 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.
    python
    python3 - <<'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}...)')
    PY

    Expected 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.

  2. Step 2Apply the fixed-depth decode chain (once you know N)
    Observation
    The 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.
    bash
    base64 --decode enc_flag | base64 --decode | base64 --decode | base64 --decode | base64 --decode | base64 --decode

    Expected 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_flag reads the file directly (no UUOC) and emits decoded bytes. Each subsequent | base64 --decode peels 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.

  3. Step 3Alternative: CyberChef Magic
    Observation
    CyberChef'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.

Key takeaway

Base64 moves binary data into a printable character set for safe transport, but it hides nothing: the transformation is public and reversible. Recognizing the character set and the padding is enough to undo it with standard tools. Stacking layers slows down a casual reader, but a loop that stops on a sentinel dissolves any fixed depth in seconds.

Related reading

Tools used in this challenge

Where to go next