Decrypt a live encrypted radio broadcast from bunker XR-9 by exploiting a broken stream cipher nonce.
We're given netcat access to a service that offers two options:
Connecting to the service:
The terminal reveals:
0x prefixBroadcast length: 88 bytes (176 hex characters)
Observation: 0x4142 XOR 0x5543 = 0x1401
0x4344 XOR 0x5745 = 0x1401 — same keystream bytes!
This confirms: the nonce never changes. Every encryption uses the same keystream.
The service uses a stream cipher (AES-CTR or similar) where:
ciphertext = plaintext XOR keystream
Because the nonce is fixed ("broken nonce"), the keystream is identical for every encryption.
If we encrypt a known plaintext P, we get:
C = P XOR K (K = keystream)
Rearranging:
K = C XOR P
The simplest known plaintext is all-zero bytes, because:
0x00 XOR K = K
So encrypting null bytes directly reveals the raw keystream.
Encrypt 47 null bytes (the service truncated at 47, so we sent what we could):
Keystream (47 bytes):
14019254631dbbdced974a3e342ccbe9733c778d0361da8ea4168a82f55e1af0a3f8d416081c84f9adbfb00b187488
broadcast = bytes.fromhex(
"4c53ab6e595be999bcaa7b0f1a15f1d3317930c44d5be0cae542cbbfa46f50b"
"c6c99a265144d581f48e88714146eff8ea4b6ce29e76ce2396927e4b2192b88c"
"002ae2fe3a9c126b257da88becbecb39c30d7fd8de0e70d0a5b8"
)
keystream = bytes.fromhex(
"14019254631dbbdced974a3e342ccbe9733c778d0361da8ea4168a82f55e1af0"
"a3f8d416081c84f9adbfb00b187488"
)
plaintext = bytes(a ^ b for a, b in zip(broadcast, keystream))
print(plaintext)
XOR output (first 47 bytes of broadcast decrypted):
XR9::FREQ=11.9::BEGIN::DATA=Q1JLe1N0YXQxY18zY2gwMzVfaDFkZDNuX2IzbjM0dGhfbjAxczN9::END::X
The DATA= field is Base64 encoded!
import base64
print(base64.b64decode("Q1JLe1N0YXQxY18zY2gwMzVfaDFkZDNuX2IzbjM0dGhfbjAxczN9").decode())
Output:
CRK{Stat1c_3ch035_h1dd3n_b3n34th_n01s3}
| Property | Detail |
|---|---|
| Cipher type | Stream cipher (fixed nonce) |
| Attack | Known-plaintext keystream recovery |
| Known plaintext | All-zero bytes → ciphertext = keystream |
| Secondary encoding | Base64 inside the plaintext |
| Ops used | 3 of 10 |