Manifest Collision
Challenge Description
Two encrypted manifests were generated using the same internal stream key. An exhausted warehouse engineer tried to reconstruct the original messages before the system wiped itself - but only managed to save the ciphertexts. You've intercepted both.
Flag: Raptor{otp_reuse_shakespeare_classic}
The Setup
Two XOR-encrypted ciphertexts, same key, plus a hint that one plaintext begins with a Shakespeare quote. This is a textbook two-time pad scenario, one of the oldest and most reliable attacks in symmetric cryptography.
The fundamental property of XOR: if you encrypt two messages with the same key K:
c1 = p1 XOR K
c2 = p2 XOR K
XOR-ing the ciphertexts together cancels the key entirely:
c1 XOR c2 = p1 XOR p2
You're left with the two plaintexts XOR'd together, no key involved.
Step 1: XOR the Ciphertexts
xored = bytes([c1[i] ^ c2[i] for i in range(len(c1))])
Result is p1 XOR p2. The key is gone. Now we need to peel apart the two plaintexts.
Step 2: Identifying P2 via Single-Byte XOR Brute Force
With p1 XOR p2 in hand and a hint that P1 starts with a Shakespeare quote, XOR brute forcing c1 XOR c2 against single bytes should reveal structure. At 0x0a:
(c1 XOR c2) XOR 0x0a → "To be, or not to be, that is the question:..."
The Shakespeare quote surfaces, almost perfectly. The logic here:
(p1 XOR p2) XOR 0x0a = p1
→ p2 XOR 0x0a = 0
→ p2 = 0x0a (repeated newlines)
P2 is almost entirely 0x0a bytes (newlines), with something at the end that broke the pattern. That something is almost certainly the flag.
Step 3: Known-Plaintext Key Recovery
With P1 identified as the Shakespeare quote, we can recover the keystream directly:
# key = c1 XOR p1
p1_known = """To be, or not to be, that is the question:
Whether 'tis nobler in the mind to suffer
The slings and arrows of outrageous fortune,
Or to take arms against a sea of troubles,
And by opposing end them?"""
key = [c1[i] ^ ord(p1_known[i]) for i in range(len(p1_known))]
As many bytes of the key as we have plaintext for are now recovered: 173 bytes covering the full length of the Shakespeare quote.
Step 4: Decrypt P2
p2 = bytes([c2[i] ^ key[i] for i in range(len(key))])
b'\n\n\n\n\n\n\n\n...\n\nRaptor{otp_reuse_shakespeare_classic}'
136 newlines, then the flag.
Step 5: Verify P1
Decrypting C1 with the recovered key gives back the Shakespeare quote, confirming the key was correct and the attack was clean.
To be, or not to be, that is the question:
Whether 'tis nobler in the mind to suffer
The slings and arrows of outrageous fortune,
Or to take arms against a sea of troubles,
Why This Works
A one-time pad is theoretically unbreakable, but only if the key is truly random, at least as long as the message, and used exactly once. The moment a key is reused, the scheme collapses. XOR-ing two ciphertexts cancels the key, leaving you with p1 XOR p2. Any known plaintext in either message is then enough to recover the keystream byte-by-byte, which decrypts both messages completely.
The 0x0a brute force was the insight that cracked open the structure. P2 being composed almost entirely of newlines was an unusual design choice by the challenge author, but it made the single-byte XOR check instantly reveal P1. From there the crib drag was just arithmetic.
Historically this attack broke Lorenz cipher traffic in WW2 (the "Tunny" machine) when German operators sent the same message twice with the same key settings. The pattern is ancient, and it still shows up in CTFs and occasionally in real systems where key management is poorly implemented.
Key Takeaways
Never reuse a stream cipher key or XOR keystream. Even a single reuse with any known plaintext in either message compromises both completely. In practice this means: use authenticated encryption with randomly generated nonces, and if building anything XOR-based, ensure nonce uniqueness is enforced at the protocol level.
For CTF purposes: two ciphertexts, same key, XOR cipher → always start by XOR-ing the ciphertexts together. If you have any known plaintext in either message (flag format, file headers, a quoted phrase) the rest is crib dragging.