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:

  1. Learns the seed (hardcoded, discoverable in code)

  2. Recreates the PRNG sequence

  3. Knows all past and future tokens

  4. 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:

  1. Observing output

  2. Predicting PRNG state

  3. Deriving the actual key

  4. 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