CTR Nonce Reuse — when the counter mode keystream repeats

verified · provenanceused 0× by assistantsblock-cipher

CTR mode turns a block cipher into a stream cipher via keystream = E_k(nonce‖counter), c = p ⊕ keystream; reusing a (nonce, counter) pair under the same key reproduces the same keystream and collapses the scheme into a Stream-Cipher Keystream Reuse — the umbrella break behind every reused-keystream challenge / many-time-pad break — NIST SP 800-38A states plainly that a counter value must never repeat under the same key or "all security is lost."

The signal that betrays it

- Ciphertext length equals plaintext length, byte for byte — no padding, no block-size rounding. That alone rules out CBC/ECB and says "stream-like mode" before you've read a line of code. - A fixed or hardcoded nonce/IV in the source (nonce = b"\x00"*16, a nonce read from a constant, or a nonce that's only the message counter without a random component) is the single most common real-world instance. Grep for where the nonce is generated, not just where it's used. - Two or more ciphertexts of comparable length, ideally from the same endpoint/service call encrypted moments apart — a strong prior that the (nonce, counter) space was reused rather than advanced. - A short nonce field (32- or 64-bit) fed by a PRNG rather than a counter. Even a *correctly random* nonce that short collides by the birthday bound: at 64 bits, expect a collision after roughly 2^32 messages under one key — see Birthday Attack — the square-root speedup that governs any collision search. This is a spec-level design flaw, not a coding bug, and it's worth flagging separately from "programmer reused a constant." - Don't confuse this with ECB detection. In CTR, the counter differs per block *within* one message, so you will not see repeated ciphertext blocks inside a single message the way you do with ECB Detection & Cut-and-Paste — the identical-block signal and how to weaponize it. The repetition to look for is *across* messages/ciphertexts that share the same starting nonce — compare whole ciphertexts (or aligned prefixes), not intra-message blocks.

How to exploit it

1. Confirm reuse. Either the nonces are literally identical (check the transport/protocol for where the nonce travels — often prepended to the ciphertext), or c1 ⊕ c2 produces a byte stream with visible ASCII/English structure instead of noise — that structure is p1 ⊕ p2. 2. Two ciphertexts, no known plaintext: apply the many-time-pad method — XOR the ciphertexts and crib-drag a probable word across the result. See One-Time-Pad / Many-Time-Pad Reuse — the crib-drag break when a pad is used twice for the crib-dragging mechanics and the space-XOR (0x20) trick for bootstrapping key bytes when you have many samples, not just two. 3. One known plaintext, unknown others: recover the keystream directly and reuse it — ```python def xor(a, b): return bytes(x ^ y for x, y in zip(a, b))

def decrypt_with_known_plaintext(c_known, p_known, c_target): ks = xor(c_known, p_known) # keystream for the shared (nonce, counter) return xor(c_target, ks) `` Works block-by-block: if only the first block's plaintext is known, you recover the keystream for that block and can decrypt only the corresponding block of every other ciphertext under the same nonce — advancing counters desync the keystream, so partial knowledge only buys you partial recovery unless the counters also line up. 4. **Bit-flipping, independent of the above.** Because CTR is c = p ⊕ E_k(nonce‖counter), flipping ciphertext bit i flips plaintext bit i` directly — no need to even find a nonce collision to tamper with a message you can intercept. This is the same primitive as CBC Bit-Flipping — controlled tampering with no oracle needed but *stronger*: CBC bit-flipping only lets you control the *next* block and garbles the block you flip, while CTR lets you flip any bit, in any block, precisely, with zero collateral damage to the rest of the message. Any CTR deployment without a MAC is fully malleable this way regardless of nonce reuse. 5. If the mode is GCM, escalate. Nonce reuse in GCM is categorically worse than in plain CTR: the authentication tag is built from GHASH, a function evaluated at the secret subkey H, and reusing a nonce exposes H to attack — this is Ferguson's attack on GCM under nonce reuse, where each forged/verification attempt against the reused nonce leaks information about H progressively rather than yielding it outright from a single pair of samples; recovering enough of H typically requires multiple ciphertext/tag pairs plus oracle access to a verifier (to test forgeries), not a closed-form solve from two observations. Once H is sufficiently recovered the attacker can forge valid tags for arbitrary ciphertexts, not just read plaintext XORs. Treat *any* GCM nonce collision as a serious threat to both confidentiality and integrity, not just a many-time-pad problem — but don't overstate the data requirement: it's not "two samples and you're done."

What does NOT work

- Looking for repeated ciphertext blocks inside one message, ECB-style. CTR's counter guarantees each block within a message uses a distinct keystream block even under a fixed nonce, so intra-message block repetition tells you nothing — the reuse signal is only visible *across* messages/sessions. - Assuming a padding oracle applies. CTR (like any stream cipher) has no padding to validate, so CBC Padding Oracle — leak the intermediate decryption value one byte at a time techniques are a dead end here — don't waste time probing for padding-error responses on a CTR endpoint. - Expecting to recover the underlying block-cipher key from the reused keystream. The many-time-pad break only recovers *plaintext* (or the keystream for that specific nonce), never the key k itself — the key stays as hard to recover as ever. If the challenge wants the key, keystream reuse is the wrong avenue; look for a separate weakness (weak key schedule, oracle, etc.). - Crib-dragging alone on very short ciphertexts or with only two samples. A short crib can match by coincidence, and with only two messages a wrong guess is hard to distinguish from a right one. This is where the field notes disagree with the "just crib-drag" write-ups: with three or more ciphertexts under the same nonce, cross-check candidate fragments against *all* pairs, or switch to the statistical space-XOR keystream-recovery approach in One-Time-Pad / Many-Time-Pad Reuse — the crib-drag break when a pad is used twice, which converges even when no single crib is confidently placeable. - Treating a colliding random nonce as "their bug, not exploitable." Even a textbook-correct random nonce generator is exploitable once enough messages accumulate under one key, per the birthday bound above — the fix is a wider nonce or a counter component, not "more randomness."

Related

Stream-Cipher Keystream Reuse — the umbrella break behind every reused-keystream challenge One-Time-Pad / Many-Time-Pad Reuse — the crib-drag break when a pad is used twice Block Cipher Modes of Operation — the mode carries the CTF vulnerability, not the primitive CBC Bit-Flipping — controlled tampering with no oracle needed ECB Detection & Cut-and-Paste — the identical-block signal and how to weaponize it CBC Padding Oracle — leak the intermediate decryption value one byte at a time Birthday Attack — the square-root speedup that governs any collision search

Verified against

33 claims checked against these sources · 1 refuted and removed

Source: Sinapsi — verified compositional memory, queryable by LLMs. Query this wiki live from your assistant over MCP, or build your own verified wiki (public, or private for your team). CC BY 4.0 — reuse with attribution to Sinapsi.