CWE-329: Generation of Predictable IV with CBC Mode
Overview
CBC (Cipher Block Chaining) mode requires an Initialization Vector (IV) that an attacker cannot predict before the plaintext of that message is chosen. Static, sequential, and timestamp-derived IVs all fail that requirement. An attacker who can have chosen data encrypted under the same key works out what the next IV will be, submits a block crafted around it, and compares the ciphertext block that comes back against one captured earlier: a match confirms a guess about the earlier plaintext. Repeated, that recovers a secret a guess at a time without ever attacking the key - the BEAST attack on TLS 1.0 is exactly this weakness.
What makes that practical matters when you rate a finding: the attacker also needs to control a prefix of the encrypted message, so they can shift the secret across a block boundary until exactly one unknown byte falls inside the block being guessed. With that control, recovery costs a couple of hundred queries per byte. Without it, confirming a whole 16-byte block means guessing all of it at once, which is infeasible unless the block's contents are drawn from a small set.
Relationship to Other CWEs
- CWE-329 (this page) - a CBC-mode IV that an attacker can predict before the plaintext is chosen: static, sequential, timestamp-derived, or otherwise guessable.
- CWE-1204 (Generation of Weak Initialization Vector (IV)) - the parent this page specializes: an IV that fails the unpredictability or uniqueness its mode requires, in any mode rather than CBC alone. MITRE also lists CWE-573 (Improper Following of Specification by Caller) as a parent.
- CWE-330 (Use of Insufficiently Random Values) - the class above CWE-1204, and the page that routes the rest of the randomness family.
- CWE-338 (Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)) - where the IV is generated per message but from a non-cryptographic generator, so it is still predictable. MITRE records no direct relationship between the two; both sit under CWE-330, on different branches.
- CWE-323 (Reusing a Nonce, Key Pair in Encryption) - the uniqueness failure this page's Risk section separates from the unpredictability one, and the right entry for a repeated GCM or CTR nonce. MITRE lists no relationship between it and CWE-329.
OWASP Classification
A04:2025 - Cryptographic Failures
Risk
High: A predictable IV turns CBC into a guess-confirmation oracle for anyone who can have chosen data encrypted under the same key, so session identifiers, authentication cookies and other short secrets are recoverable a byte at a time wherever the attacker also controls a prefix, as the Overview describes. Repeating an IV under one key is a separate and weaker leak: two CBC ciphertexts sharing an IV are byte-identical for as many leading blocks as their plaintexts are, so an attacker learns which messages start the same way.
This is not the stream-cipher failure, and treating it as one leads to the wrong fix. XORing two CBC ciphertexts encrypted under the same key and IV does not yield the XOR of the plaintexts - that property belongs to CTR, ChaCha20 and GCM, where a repeated nonce is catastrophic on its own. A CBC finding needs the unpredictability fix; a GCM nonce finding needs the uniqueness fix. Similarly, tampering with a stored IV flips bits in the first decrypted block, which is a malleability problem rather than a way to read plaintext - but it is solved by authenticating the IV together with the ciphertext, not the ciphertext alone. An HMAC computed over the ciphertext by itself still verifies after the IV has been modified, and the record then decrypts with its first block rewritten.
Remediation Steps
Core Principle: CBC requires a fresh unpredictable IV for each encryption under the same key. AEAD/nonce-based modes such as GCM require nonce uniqueness and should normally use the mode's recommended nonce size.
Locate the predictable IV usage
- Start from the file and line in the finding and identify the code that encrypts with CBC mode
- Work out how the IV is produced: a static byte array, a sequential counter, a timestamp, or a value derived from the plaintext
- Check whether the same IV is reused across encryptions under the same key
- Follow the encryption flow to see where the IV is created and where it is consumed
Use random IV for each encryption (Primary Defense)
- Generate the IV from the platform CSPRNG for every CBC encryption: SecureRandom (Java), os.urandom or secrets (Python), crypto.randomBytes (Node.js)
- Make the IV the cipher's block size: 16 bytes (128 bits) for AES
- Store or transmit the IV with the ciphertext, normally by prepending it. The IV is not secret and can travel in the clear
Use authenticated encryption instead, where the mode is yours to change
This is not a competing defense - it removes the weakness rather than mitigating it, because an AEAD mode has no CBC IV to predict. Where the mode is fixed by a protocol, a stored format or an interoperability requirement, the random IV above is the fix.
- AES-GCM is the usual choice. It provides confidentiality and integrity together, so a modified ciphertext or manipulated nonce is detected rather than decrypted
- ChaCha20-Poly1305 is an alternative to AES-GCM and is faster on systems without AES hardware acceleration
- Many APIs require the caller to supply the GCM nonce. Unless your library manages nonces for you, use a 96-bit nonce and never repeat one under the same key
Never reuse or predict IVs
- Do not reuse an IV across encryptions under the same key; each operation gets a fresh one
- Do not use a counter as a CBC IV. The IV must be unpredictable before the plaintext is chosen. NIST SP 800-38A does permit deriving an IV by encrypting a unique nonce, such as a counter, under the same key, but that is a deliberate construction, not a plain counter IV
- Do not derive the IV from the plaintext; it must be independent of the data being encrypted
Proper IV handling
- Lay the stored record out as
[IV][ciphertext]or[IV_length][IV][ciphertext] - Do not encrypt or hide the IV. It is not secret, and encrypting it under a chaining mode needs an IV of its own, which is the same problem one layer down. A reader given the encrypted IV can still recover it; the reason not to is that the effort protects nothing
- Check the IV is 16 bytes for AES before using it
- Authenticate the IV along with the ciphertext: flipping bits in a stored IV flips the same bit positions in the first decrypted block, and leaves every later block untouched. That gives an attacker an arbitrary XOR over the first block - so wherever they can predict what that block holds, such as a fixed
role=user;header, they can set it to whatever they like. An AEAD mode covers the nonce automatically; for CBC it takes encrypt-then-MAC overIV || ciphertext, because a MAC over the ciphertext alone still verifies when only the IV was changed
Test the IV randomness fix
A re-scan tells you the static IV is gone. It cannot tell you the replacement decrypts, and the usual way to fail this fix is to generate a fresh IV correctly and then never store it. Assert each of these:
- Encrypting the same plaintext twice under the same key produces two different ciphertexts, and the first 16 bytes of the two differ. Identical leading bytes mean the IV is being cached or the generator reseeded, not that the encryption failed
- Round-tripping through a fresh object in a separate call returns the original plaintext byte for byte: encrypt, keep only the stored bytes, then decrypt by reading the IV back out of those bytes. Decrypting with an IV still held in a variable from the encrypt call passes against an implementation that would fail on the next process restart
- Decrypting a stored value written before the fix still works if you kept a legacy path. Do not assert that it fails cleanly if you did not - it usually does not fail at all. A reader that takes the first 16 bytes as the IV consumes the old record's first ciphertext block instead, and CBC's chaining then returns every remaining block decrypted correctly but shifted one block early, with intact padding and no error:
CARD=4111111111111111;NAME=A Smith;CVV=123comes back as11111;NAME=A Smith;CVV=123. Only a single-block record raises anything at all, and what it raises depends on the runtime: Node reports a block-length error, Python a padding error, and the JVM raises nothing whatever -doFinalreturns an empty array and the caller is handed an empty string. Assert instead that an old-format value is recognised as old - by a version marker or a length check - because the failure this migration actually produces is a plausible-looking truncated secret that then gets re-encrypted and stored over the original - Truncating a stored value to fewer than 16 bytes raises an error rather than slicing an empty or short IV out of it
- IV/nonce generation follows the mode's own requirement: unpredictability for CBC, uniqueness for GCM. Statistical randomness tests cannot prove either, so assert the source is the platform CSPRNG rather than testing the output distribution
- Re-scan with the security scanner to confirm the finding is resolved
Common Vulnerable Patterns
static byte[] IV = {0,0,0,0...}- Using same IV for all encryptions
- Sequential/counter IVs
- Deriving IV from timestamp or plaintext
- Empty/zero IV
Additional Resources
- CWE-329: Generation of Predictable IV with CBC Mode
- NIST SP 800-38A: Recommendation for Block Cipher Modes of Operation - section 5.3 carries the CBC unpredictability requirement, section 6.2 adds that the IV's integrity should be protected, and Appendix C gives the two approved ways to generate a conforming IV
- OWASP Cryptographic Storage Cheat Sheet
- OWASP Top 10 2025
- OWASP Top 10 2025 A04: Cryptographic Failures