PRNG Failures and Predictable Secrets
CWE-330, CWE-331, CWE-332, CWE-334, CWE-335, CWE-336, CWE-337, CWE-338, CWE-340, CWE-342, CWE-1241
Weak randomness vulnerabilities occur when applications use insufficient entropy, predictable random number generators (PRNGs), or improper seeding for security-critical operations. This includes:
When randomness is weak, attackers can predict tokens, keys, session IDs, and other security-critical values, completely bypassing encryption and authentication.
Real-World Attack Scenarios
Scenario 1: Using random.randint() for Cryptography
Python application generates session tokens using built-in random:
import random
def generate_session_token():
# VULNERABLE - Not cryptographically secure
return str(random.randint(0, 999999999999))The vulnerability:
random uses Mersenne Twister PRNG:
Deterministic (predictable with 624 observations)
Not cryptographically secure
Seed can be recovered from output
Produces only ~32 bits of entropy (despite larger numbers)
The attack:
Attacker observes a few session tokens:
They recognize the Mersenne Twister pattern and predict:
Using the predicted token, attacker hijacks any user's session.
Result:
Session hijacking without credentials
Account takeover
Complete authentication bypass
The fix: Use cryptographically secure random:
Finding it: Search for random.randint(), random.choice(), Math.random(). Look for PRNG usage in security context.
Scenario 2: Predictable Seed from Timestamp
Application seeds PRNG with current time:
The vulnerability:
Seeding with timestamp:
Current time is publicly known
Attacker knows seed is within a narrow time window
Can try all possible seeds in seconds
Brute-force attack against weak PRNG
The attack:
Token generated at 2024-01-15 10:23:45.
Attacker knows seed is one of:
With timing information, attacker narrows window to 1-2 seconds = predictable.
Result:
Session token prediction
Account takeover
Authentication bypass
The fix: Use cryptographically secure random (doesn't need explicit seeding):
Finding it: Search for time.time(), datetime.now(), System.nanoTime() used for seeding. Look for seed() calls with predictable values.
Scenario 3: Reusing the Same PRNG Seed
Application initializes PRNG once at startup:
The vulnerability:
Same seed = same PRNG sequence:
All tokens are deterministic
Attacker predicts entire sequence
Every session token can be forged
Affects all users, all time periods
The attack:
Attacker:
Learns the seed (hardcoded, discoverable in code)
Recreates the PRNG sequence
Knows all past and future tokens
Hijacks all sessions ever created
Result:
Complete authentication bypass
All users compromised
Historical sessions exploitable
The fix: Use cryptographically secure random:
Finding it: Look for seed() calls. Check if PRNG initialized once globally. Look for hardcoded seeds.
Scenario 4: Small Randomness Space
Password reset token generated from small space:
The vulnerability:
Only 10,000 possible tokens:
Attacker can brute-force all tokens in seconds
Reset password for any account
No prediction needed, pure brute force
The attack:
One of 10,000 attempts will work (average: 5,000 attempts).
Result:
Complete password reset
Account takeover
Email verification bypassed
The fix: Use large randomness space:
Now ~2^256 possibilities (infeasible to brute-force).
Finding it: Look for small random ranges. Check password reset/2FA code generation. Look for 4-6 digit tokens.
Scenario 5: Sequential or Predictable IDs
Application generates user IDs sequentially:
The vulnerability:
Sequential IDs are trivial to predict:
User 1001, 1002, 1003...
Attacker enumerates all user IDs
Can access data of any user by changing ID in request
No authentication needed to discover users
The attack:
Attacker discovers:
All user accounts and emails
User data
Sensitive information
Result:
User enumeration
Data disclosure
Horizontal privilege escalation
The fix: Use cryptographically random IDs:
Or use UUID:
Finding it: Look for sequential IDs, auto-increment in databases. Check if IDs follow patterns. Test ID enumeration.
Scenario 6: IV Reuse in CBC Mode
Encryption uses static IV:
The vulnerability:
Reusing IV with CBC mode:
Identical plaintext produces identical ciphertext
Attacker learns when same message is sent
Can mount known-plaintext attacks
IV should be random for each encryption
The attack:
Attacker intercepts encrypted messages and recognizes patterns:
With known-plaintext attack:
Attacker guesses message 1 content
Confirms by matching ciphertext of message 3
Result:
Information disclosure
Pattern analysis
Reduced encryption effectiveness
The fix: Use random IV for each encryption:
Finding it: Look for static IVs. Check if IV is regenerated per encryption. Test if identical plaintexts produce identical ciphertexts.
Scenario 7: Weak PRNG in Cryptographic Context
C application uses rand() for encryption:
The vulnerability:
rand() in C:
Weak PRNG (linear congruential generator)
Only ~32 bits of entropy despite 32-byte key
Predictable and biased
Not cryptographically secure
The attack:
Attacker recovers the weak key through:
Observing output
Predicting PRNG state
Deriving the actual key
Decrypting all encrypted data
Result:
Encryption completely broken
All encrypted data exposed
Key derivation fails
The fix: Use cryptographically secure random:
Finding it: Search for rand(), Math.random(), random.random(). Look for PRNG usage in cryptographic functions.
How to Identify Weak Randomness During Testing
1. Check randomness sources
2. Test for predictability
Generate multiple tokens/IDs and analyze:
3. Check seed sources
Look for:
time.time(),System.currentTimeMillis()(predictable)Hardcoded seeds
Global seed initialization
User-controlled seeds
4. Test IDs for enumeration
5. Analyze randomness quality
6. Check IV reuse
Encrypt same plaintext multiple times:
Mitigation Strategies
Always use cryptographically secure randomness
Never use for cryptography:
random.randint(),random.choice()Math.random()rand(),srand()Time-based seeds
Hardcoded seeds
Use random values for each operation
Generate cryptographic IDs
Use random IV/nonce each time
Sufficient entropy for purpose
Session tokens: 128+ bits
Cryptographic keys: 256+ bits
Password reset tokens: 256+ bits
API keys: 256+ bits
IDs: 128+ bits (UUID size)
Test randomness
Last updated
