Hidden Cipher 1 picoCTF 2026 Solution

Published: March 20, 2026

Description

The flag is only lightly concealed. Download hiddencipher.zip, identify the cipher and key, and peel the text back to plaintext.

Download and extract hiddencipher.zip.

Inspect the included files to identify the cipher and ciphertext.

bash
unzip hiddencipher.zip
bash
ls -la
bash
cat *
  1. Step 1Identify the cipher
    Both the cipher and the key are in the binary itself: ciphertext as a string constant, key as another string constant, transform as a tight loop in main(). Fastest path is strings first, r2 if strings is noisy, Ghidra last when you need the loop body decompiled.
    bash
    strings binary
    bash
    r2 -A binary
    bash
    ghidra binary
    Learn more

    Classical ciphers are simple substitution or transposition schemes that predate modern cryptography. The most common ones in CTF challenges are:

    • Caesar cipher: shifts each letter by a fixed number (e.g., shift 13 = ROT13)
    • Vigenère cipher: a polyalphabetic cipher using a repeating keyword as the key
    • XOR cipher: bytes are XORed with a key byte or key sequence
    • Substitution cipher: each letter is replaced by a fixed different letter
    • Atbash: reverses the alphabet (A↔Z, B↔Y, etc.)

    Recognising which cipher was used is the first step. Ciphertext that contains only letters and spaces suggests an alphabetic cipher (Caesar, Vigenère, substitution). Ciphertext containing arbitrary bytes or hex suggests XOR. The key insight for this challenge is that "hidden" means the cipher type and key are embedded in the provided files (in constants, strings, or the program logic) rather than being something you need to crack.

    Distinguishing XOR from Vigenère in pseudocode is a one-glance test. XOR loops look like out[i] = ct[i] ^ key[i % keylen] with a bitwise XOR (and frequently a & 0xFF mask if the operands are wider than a byte). Vigenère loops look like out[i] = ((ct[i] - 'A') - (key[i % keylen] - 'A')) % 26 + 'A': subtraction of letter offsets, modulo 26, no XOR, and the operands are guaranteed to be alphabetic. If you see a ^ operator or a hex constant XOR, it is XOR; if you see %26 or character arithmetic on 'A', it is Vigenère or a Caesar variant. See the encodings guide for the broader pattern catalogue and Ghidra reverse engineering for tips on decompiling these loops.

  2. Step 2Extract the key
    Look at the data section in r2 or Ghidra: the key is stored next to the ciphertext, often with a label like 'key' or as the second long ASCII constant. The XOR/lookup loop in main() references both addresses.
    bash
    r2 -A binary  # then 'iz' to list strings, 'pdf @ main' to dump main
    bash
    ghidra binary  # decompile main, follow the data refs
    Learn more

    Static analysis extracts information from a binary without executing it. strings finds printable ASCII sequences and is the fastest way to find hardcoded keys, passwords, or meaningful strings. Radare2 (r2) is a powerful open-source reverse engineering framework; -A analyses the binary automatically and identifies functions, cross-references, and strings. Ghidra (developed by the NSA) provides a GUI with decompilation - it translates assembly back into readable C-like pseudocode.

    Keys in CTF binaries are typically stored as: hardcoded string literals (found by strings), integer constants in the assembly, arrays initialised at the start of a function, or values computed from program inputs. When you see an XOR loop in Ghidra output, look for the key value being loaded from a nearby variable or constant. For Vigenère, look for a string being used as a repeating index.

    In real malware analysis, extracting hardcoded encryption keys from binaries is a core skill. Malware families often XOR-encrypt their configuration (C2 server addresses, port numbers, campaign identifiers) with a static key embedded in the binary. Tools like FLOSS (FireEye Labs Obfuscated String Solver) automatically find and decode XOR-encoded strings in malware samples.

  3. Step 3Decrypt the ciphertext
    Apply the identified cipher with the discovered key to decrypt the ciphertext.
    python
    python3 << 'EOF'
    # Example XOR decrypt
    key = b"FOUND_KEY"
    ct  = bytes.fromhex("CIPHERTEXT_HEX")
    pt  = bytes(c ^ k for c, k in zip(ct, key * (len(ct)//len(key)+1)))
    print(pt.decode())
    EOF
    Learn more

    XOR encryption is symmetric: applying the same key twice returns the original plaintext (P XOR K XOR K = P). This makes decryption identical to encryption - the same code that encrypts also decrypts. XOR with a repeated key is also called a Vigenère cipher in binary and has the same weaknesses: if any plaintext byte is known (like the p in picoCTF), you can recover the corresponding key byte.

    The Python expression bytes(c ^ k for c, k in zip(ct, key * N)) decrypts by cycling the key over the ciphertext using zip. The key is repeated with key * N to ensure it is at least as long as the ciphertext. This is a clean, Pythonic one-liner for any XOR cipher regardless of key length.

    For classical alphabetic ciphers, Python's str.translate() and str.maketrans() are the cleanest decryption tools: they define a character mapping table and apply it to the entire string in one call. Online tools like CyberChef("The Cyber Swiss Army Knife") support all common ciphers and encoding schemes and are excellent for rapid prototyping when writing custom code feels like overkill.

Alternate Solution

Once you have identified the key, use the XOR Cipher tool on this site to decrypt without writing any Python. Paste the hex ciphertext, enter the key, and the plaintext flag appears instantly. If the cipher turns out to be a Caesar/ROT variant, use the ROT / Caesar Cipher tool instead.

Flag

picoCTF{h1dd3n_c1ph3r_1_...}

Both the cipher type and the key are hidden in the program - static analysis reveals them.

Want more picoCTF 2026 writeups?

Tools used in this challenge

Related reading

What to try next