The_Last_Dance
Nonce Reuse
This challenge demonstrates a critical cryptographic flaw: Nonce Reuse in a stream cipher. When the same key and nonce are used to encrypt two different messages, the security of the cipher is compromised.
Analysis
The provided source.py script reveals how the encryption is handled:
-
Cipher: ChaCha20 (a stream cipher).
-
The Flaw: Both the
messageand theFLAGare encrypted using the samekeyand the sameiv(nonce). -
Output: The file
out.txtcontains the hex-encoded nonce, the encrypted message, and the encrypted flag.
The Vulnerability
In a stream cipher like ChaCha20, the encryption process generates a “keystream” based on the key and nonce. The ciphertext is produced by:
$$C = P \oplus K$$
Where $C$ is ciphertext, $P$ is plaintext, and $K$ is the keystream.
If the same $K$ is used for two different plaintexts ($P_1$ and $P_2$):
-
$C_1 = P_1 \oplus K$
-
$C_2 = P_2 \oplus K$
By XORing the two ciphertexts together, the keystream cancels out:
$$C_1 \oplus C_2 = (P_1 \oplus K) \oplus (P_2 \oplus K) = P_1 \oplus P_2$$
Because we already know the content of $P_1$ (the intercepted message), we can recover the keystream:
$$K = C_1 \oplus P_1$$
Once we have $K$, we can recover the flag ($P_2$):
$$P_2 = C_2 \oplus K$$
Solution
The solve.py script automates this process:
-
Extract Data: Load the encrypted message and encrypted flag from
out.txt. -
Recover Keystream: XOR the known plaintext message with its corresponding ciphertext.
-
Decrypt Flag: XOR the recovered keystream with the encrypted flag.
from pwn import xor
# Data from out.txt
message_enc = bytes.fromhex("7aa34395a258f5893e3db1822139b8c1f04cfab9d757b9b9cca57e1df33d093f07c7f06e06bb6293676f9060a838ea138b6bc9f20b08afeb73120506e2ce7b9b9dcd9e4a421584cfaba2481132dfbdf4216e98e3facec9ba199ca3a97641e9ca9782868d0222a1d7c0d3119b867edaf2e72e2a6f7d344df39a14edc39cb6f960944ddac2aaef324827c36cba67dcb76b22119b43881a3f1262752990")
flag_enc = bytes.fromhex("7d8273ceb459e4d4386df4e32e1aecc1aa7aaafda50cb982f6c62623cf6b29693d86b15457aa76ac7e2eef6cf814ae3a8d39c7")
message_plain = b"Our counter agencies have intercepted your messages and a lot of your agent's identities have been exposed. In a matter of days all of them will be captured"
# K = C1 ^ P1
key_stream = xor(message_enc, message_plain)
# P2 = C2 ^ K
flag = xor(flag_enc, key_stream)
print(flag.decode())