CBC-MAC Forgery — splice a new message onto a known tag without the key
Raw CBC-MAC (AES-CBC, tag = the final ciphertext block, IV = 0/fixed) is provably secure only when every message has the *same fixed length* — the moment lengths vary, the tag doubles as a valid mid-computation chaining value, which lets an attacker splice a second known (message, tag) pair onto a first without ever touching the key.
The signal that gives it away
- The MAC is described as "AES in CBC mode, keep the last ciphertext block as the tag" — no separate finalization step, no second key.
- The IV is fixed/zero and not part of the tag, or worse, it's transmitted and attacker-controlled.
- The messages the oracle will MAC vary in length (not all padded or truncated to exactly one block).
- You have, or can get, an oracle that returns MAC(m) for chosen m — i.e. at least two known (message, tag) pairs.
- Contrast check: if the scheme is named CMAC or "encrypt-last-block", stop — that's precisely the fix for this weakness (see below); the naive splice below does not apply.
How to exploit it
The core fact: CBC-MAC's tag *is* the raw CBC chaining value, nothing more. That means t1 = MAC(m1) is a valid "fake previous-ciphertext-block" for continuing the CBC computation into any suffix, exactly as if encryption had paused there.
Splice forgery from two known pairs — one forgery, no extra oracle queries beyond the two pairs you already have:
```python def xor(a, b): return bytes(x ^ y for x, y in zip(a, b))
# given (m1, t1) and (m2, t2), both known valid pairs forged_msg = m1 + xor(m2[:16], t1) + m2[16:] forged_tag = t2 # valid under raw CBC-MAC, IV = 0 ```
Why it works: the first 16 bytes of the appended part are XORed against t1 before being re-XORed against the CBC chaining value inside the cipher, which cancels the correction — from that block onward, encryption proceeds identically to a fresh computation of MAC(m2) starting mid-stream. It reconverges onto t2 exactly and matches it bit for bit at the end.
IV-as-input variant. If the IV is transmitted *and* attacker-controlled (not fixed to zero), the primitive is even stronger: choosing the IV lets you cancel or inject any value into the first block before encryption, forging tags for chosen first-block plaintext without needing a second known pair at all. Treat any transmitted, mutable IV field as a straight break, never as hardening.
Structural relative. This is the block-cipher cousin of Hash Length Extension — forge H(secret‖msg‖extra) from a digest alone — same root cause (the tag/digest *is* the raw internal chaining state, with no finalization step mixing in anything extra), different primitive underneath. Recognize the "tag = internal state, no finalization" pattern once and both attacks fall out of the same insight.
Cost-wise this is the cheapest forgery in the whole MAC family when it applies: two queried pairs, one XOR, no factoring, no brute force, no lattice work.
What does NOT work
- Trying this against CMAC or ECBC-MAC (encrypt-last-block, typically under a second derived key). The splice works only because the tag equals the raw chaining value the *next* block of encryption would consume as its "previous ciphertext" input. CMAC/ECBC-MAC deliberately break that equivalence — a final re-encryption or XOR against a derived subkey means the tag can never be fed back in as if it were mid-computation state. "CBC-MAC" is used loosely in casual writeups, so confirm which variant is actually implemented before spending time on this.
- Splicing against a fixed-length-only message space. If every message the oracle will ever MAC is exactly one block (or is padded/truncated to a single fixed length before the MAC is computed), raw CBC-MAC is the textbook-secure PRF case for that domain — there's no trailing space to append a suffix into, and the two-known-pairs trick has nothing to splice onto. The weakness is strictly about variable-length message spaces, not about CBC-MAC being broken in general.
- Skipping the XOR correction on the boundary block. Forgetting to XOR the first block of m2 against t1 before appending it produces a message that looks structurally like a valid splice but carries the wrong tag — the entire trick lives in that one XOR; omit it and the forgery fails verification every time, silently, with no error to debug against.
- Assuming a random (non-zero, secret, not transmitted) IV behaves like IV = 0. It doesn't automatically fail the same way. In practice, implementations that bother to randomize the IV per-message and keep it secret have usually also addressed the underlying length problem elsewhere, or the IV ends up transmitted anyway (the other variant above). Check which case is actually in front of you rather than assuming the splice generalizes for free.
Related
MAC Forgery — the construction, not the primitive, decides the attack Hash Length Extension — forge H(secret‖msg‖extra) from a digest alone HMAC Fundamentals — the nested MAC that tells you when to STOP trying length-extension Merkle–Damgård Construction — the digest IS the internal state Block Cipher Modes of Operation — the mode carries the CTF vulnerability, not the primitive
Verified against
17 claims checked against these sources
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.