C3

Published: April 3, 2024

Description

This is the Custom Cyclical Cipher! Download the ciphertext here. Download the encoder here. Enclose the flag in our wrapper for submission. If the flag was "example" you would submit "picoCTF{example}".

Python scripts

Download ciphertext and convert.py to the same directory.

Run the provided script locally with Python 3.

wget https://artifacts.picoctf.net/c_titan/47/ciphertext && \
wget https://artifacts.picoctf.net/c_titan/47/convert.py

Solution

This custom cipher challenge requires reversing a Python encryption script. For another custom cryptography challenge, see Custom encryption, which involves Diffie-Hellman and dynamic XOR operations.
  1. Step 1Implement the inverse
    Translate convert.py into a decryptor: swap lookup1/lookup2 roles, replace (cur - prev) with (cur + prev), and keep prev synced with the decrypted index.
    python3 decrypt.py ciphertext > decrypted.txt
    Learn more

    The C3 cipher is a cyclical differential cipher - a custom substitution scheme where each character's encoding depends on the previously encoded character. This "chaining" property means you cannot decrypt any character without first knowing all the characters that came before it, making it a simple form of cipher feedback.

    To reverse such a cipher, you need to invert every operation in reverse order. If encryption computed cur = (plaintext_index + prev) % alphabet_size, then decryption must compute plaintext_index = (cur - prev) % alphabet_size. The key insight is that modular arithmetic is always reversible: addition is undone by subtraction, and multiplication by a coprime is undone by multiplication by its modular inverse.

    The lookup table swap is equally important: during encryption, the plain character is looked up in lookup1 to get an index, which is then looked up in lookup2 to get the cipher character. Reversal means: look up the cipher character in lookup2 to get the index, then look up that index in lookup1 to get the plain character.

    In real cryptography, this style of "cipher feedback" is formalized in stream cipher modes like CFB (Cipher Feedback Mode) used with AES. The vulnerability of simple cyclical ciphers is that if the alphabet is small, an attacker can mount a known-plaintext attack by just trying all possible starting states.

  2. Step 2Run the extraction script
    Reuse the provided snippet that prints chars at i == n^3. Port it to Python 3 so it prints the hidden phrase that becomes the flag body.
    python3 cubic_extract.py decrypted.txt
    Learn more

    Embedding a secret by sampling at cubic indices (positions 1, 8, 27, 64, 125...) is a steganographic technique - the message is hidden within a larger body of text, with only specific positions carrying meaningful data. Anyone reading the full decrypted text sees innocuous content; only someone who knows to sample at i = n³ recovers the hidden flag.

    Steganography (hiding the existence of a message) differs from cryptography (hiding the content of a message). This challenge combines both: the outer cipher is cryptographic, while the cubic sampling is steganographic. Real-world malware sometimes uses similar index-based encoding to embed command-and-control instructions inside seemingly normal traffic.

    In Python 3, iterating with enumerate() and checking if i is a perfect cube (by computing round(i**(1/3))**3 == i) is the idiomatic approach. Alternatively, you can precompute the cubic indices up to the file length. The key lesson is: always read the provided scripts carefully - the extraction logic is already given to you, just needing a Python 2 to Python 3 port.

  3. Step 3Wrap the flag
    Enclose the final string in picoCTF{...} for submission.
    Learn more

    The picoCTF{...} flag format is a standard wrapper used across all picoCTF challenges. This convention follows the CTF flag format pattern used by most major competitions (e.g., CTF{...}, flag{...}, HTB{...}), which allows automated scoring systems to detect valid flag submissions by regex matching.

    When a challenge says "enclose the flag in our wrapper," it usually means the raw output of your decryption is the inner content - the competition infrastructure prepends the prefix and wrapping braces for display purposes. Always verify your wrapper format against the challenge instructions, as some challenges expect the entire string including the prefix.

    This step also reinforces the habit of double-checking your answer before submission. Many CTF players lose points by submitting answers with trailing whitespace, incorrect case, or missing wrapper components. In competitive CTF play, a wrong submission rarely has a penalty, but it's good practice to verify thoroughly before submitting.

Related guides

Base64, Hex, and Common CTF Encodings Explained

This challenge uses a custom substitution cipher. The encodings guide covers classical ciphers, Caesar/ROT variants, and the identification techniques that help you recognize what you are dealing with.

Flag

picoCTF{adl...}

The cubic sampling script prints the final flag body.

Want more picoCTF 2024 writeups?

Useful tools for Cryptography

Related reading

Do these first

What to try next