Description
Can you get sense of this code file and write the function that will decode the given encrypted file content. Find the encrypted file here flag_info and code file might be good to analyze and get the flag.
Setup
Download enc_flag and custom_encryption.py locally.
Inspect the script to understand the generator parameters, XOR key ("trudeau"), and how the cipher list was produced.
wget https://artifacts.picoctf.net/c_titan/18/enc_flag && \
wget https://artifacts.picoctf.net/c_titan/18/custom_encryption.pySolution
- Step 1Rebuild the shared keycustom_encryption.py prints the values of a and b during test(). Plug them into generator(g, x, p) to recover the same shared key that encrypt() used.
Learn more
Diffie-Hellman key exchange is a foundational cryptographic protocol that allows two parties to establish a shared secret over an insecure channel. The
generator(g, x, p)function computesg^x mod p- modular exponentiation. When two parties computeg^a mod pandg^b mod pand exchange results, each can raise the received value to their own private exponent to arrive at the same shared secretg^(ab) mod p.In this challenge, the parameters
a,b,g, andpare all printed or provided, so the "key exchange" is trivially reversible. In real Diffie-Hellman security, the values ofaandbare kept secret - onlyg^a mod pandg^b mod pare shared publicly. The security rests on the discrete logarithm problem: giveng,p, andg^x mod p, findingxis computationally infeasible for large enough parameters.Modern Diffie-Hellman is used in TLS handshakes, Signal protocol, and WireGuard VPN. The elliptic curve variant (ECDH) provides equivalent security with much smaller key sizes and is preferred in modern systems.
- Step 2Invert encrypt()Write a decrypt() that divides each cipher entry by key * 311. This yields the "semi_cipher" string prior to the dynamic XOR stage.
Learn more
The encryption multiplies each character's ordinal by
key * 311. Decryption simply divides - this works because multiplication by a non-zero constant is invertible over the integers (unlike modular arithmetic, where you need the modular inverse). The result is the "semi-cipher": an intermediate state that still needs XOR reversal to yield the plaintext.The value
311is a hard-coded constant in the encryption function - a common pattern in custom ciphers where the designer adds extra "complexity" through multiplication. In practice, such constants provide no additional security if the multiplier is known (which it is, since the script is provided). This is called security through obscurity and is explicitly not recommended as a cryptographic primitive.Recognizing the layered structure of this cipher - DH key exchange, then multiplication, then XOR - is important. Decryption must undo each layer in reverse order: XOR first (innermost), then division, then the DH key is already known. This compositional approach to cipher design and analysis is fundamental to understanding block cipher modes and authenticated encryption schemes.
- Step 3Reverse dynamic_xor_encryptCreate dynamic_xor_decrypt that runs the text_key XOR three times in reverse order (mirroring the encrypt function). Applying it to semi_cipher with key "trudeau" reveals the flag.
python3 solver.py # uses decrypt + dynamic_xor_decryptLearn more
XOR encryption with a repeating key is a simple stream cipher. Each character of the plaintext is XORed with the corresponding character of the key (cycling if the key is shorter than the message). The critical property of XOR is that it is its own inverse: if
cipher = plain XOR key, thenplain = cipher XOR key.The "dynamic" aspect of this XOR function refers to applying the key multiple times or in a transformed manner. If the function applies XOR three times with the key in some sequence, reversal means applying XOR in the reverse sequence - since each XOR application is its own inverse, reversing the order is sufficient to undo the effect.
The key
"trudeau"is a weak key: it's a short dictionary word. In real XOR stream ciphers (like RC4, ChaCha20), the key is much longer and pseudorandomly expanded. A short repeating XOR key is trivially broken by frequency analysis if the key length can be guessed - a technique codified in the Kasiski examination and index of coincidence methods used to break Vigenere ciphers.This challenge is excellent practice for reading unfamiliar Python code, identifying the encryption's structure, and writing the inverse systematically. In real CTF and malware analysis, reverse-engineering custom encryption is a common task - attackers often implement weak custom ciphers hoping to deter analysis.
Alternate Solution
Once you have recovered the semi_cipher and know the repeating XOR key is "trudeau", use the XOR Cipher tool on this site to apply the final XOR step - paste the semi_cipher bytes and enter the key to reveal the flag without writing any additional Python.
Flag
picoCTF{custom_d2cr0pt6d_751a...}
The decrypted semi_cipher plus the reversed XOR routine yields the flag above.