Predictable Exact Value from Previous Values
Description
Predictable Exact Value from Previous Values is a vulnerability that occurs when an exact value or random number can be precisely predicted by observing previous values generated by the system. This weakness indicates a fundamental flaw in the random number generation scheme where the sequence follows a deterministic pattern that attackers can analyze and exploit. Common manifestations include linear congruential generators with observable output, sequential counters with minor obfuscation, TCP sequence numbers with predictable increments, and other schemes where mathematical relationships between consecutive outputs allow prediction of future values with complete accuracy.
Risk
When exact values can be predicted from previous observations, security mechanisms relying on those values are completely compromised. Predictable TCP initial sequence numbers enable connection hijacking and IP spoofing attacks. Predictable DNS query IDs allow DNS cache poisoning, redirecting victims to malicious servers. Sequential transaction IDs enable transaction forgery and replay attacks. Authentication tokens that follow predictable patterns allow account takeover. The risk is severe because prediction is exact rather than probabilistic - attackers don't need to brute force a range of values but can precisely calculate the next value. Historical attacks on TCP sequence number prediction led to widespread security improvements in operating systems, demonstrating the real-world impact of this vulnerability.
Solution
Use cryptographically secure random number generators that produce outputs with no observable mathematical relationship between consecutive values. Ensure each generated value has at least 128 bits of entropy from proper entropy sources. For protocols requiring sequence numbers, incorporate random components that cannot be inferred from previous values. Implement FIPS 140-2 compliant random number generators. If using PRNGs, ensure proper seeding from hardware entropy sources and implement periodic reseeding. Avoid linear congruential generators and other weak PRNGs for any security-sensitive purpose. Conduct security analysis of number generation schemes to identify potential prediction attacks.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Attackers can predict authentication tokens, session IDs, and sequence numbers, enabling session hijacking, connection spoofing, and unauthorized access. |
| Integrity | Scope: Integrity Predictable transaction or message identifiers enable request forgery, replay attacks, and injection of malicious data into communication streams. |
| Availability | Scope: Availability Predictable sequence numbers in protocols can enable connection reset attacks and other denial of service techniques. |
Example Code
Vulnerable Code (C/Python)
The following examples demonstrate predictable value generation:
// Vulnerable: Predictable sequence numbers
#include <stdio.h>
#include <stdlib.h>
// Vulnerable: Simple incrementing sequence
static unsigned int sequence_number = 0;
unsigned int vulnerable_get_sequence() {
// Vulnerable: Next value is exactly current + 1
return ++sequence_number;
}
// Vulnerable: Linear Congruential Generator (LCG)
static unsigned int lcg_state = 1;
unsigned int vulnerable_lcg_next() {
// Vulnerable: State can be calculated from any observed value
lcg_state = lcg_state * 1103515245 + 12345;
return lcg_state;
}
// Vulnerable: Predictable TCP-like sequence number
static unsigned int tcp_seq = 0;
unsigned int vulnerable_tcp_sequence() {
// Vulnerable: Increment is predictable
tcp_seq += 64000; // Fixed increment
return tcp_seq;
}
// Vulnerable: Time-based with fixed increment
static unsigned int counter = 0;
unsigned int vulnerable_time_counter() {
// Vulnerable: Both time and increment are predictable
counter++;
return (unsigned int)time(NULL) * 1000 + counter;
}
// Vulnerable: XOR with predictable key
static unsigned int obfuscated_counter = 0;
const unsigned int XOR_KEY = 0xDEADBEEF;
unsigned int vulnerable_obfuscated() {
// Vulnerable: XOR is reversible, sequence still predictable
obfuscated_counter++;
return obfuscated_counter ^ XOR_KEY;
}
// Vulnerable: Modular arithmetic sequence
static unsigned int mod_counter = 0;
unsigned int vulnerable_modular() {
// Vulnerable: Next = (current + 7) mod 256
mod_counter = (mod_counter + 7) % 256;
return mod_counter;
}
# Vulnerable: Predictable value generation in Python
import time
# Vulnerable: Simple sequential generator
class VulnerableSequential:
def __init__(self):
self.counter = 0
def next(self):
# Vulnerable: Exactly predictable
self.counter += 1
return self.counter
# Vulnerable: LCG implementation
class VulnerableLCG:
def __init__(self, seed=1):
self.state = seed
def next(self):
# Vulnerable: Standard LCG parameters
# Next state = (a * state + c) mod m
self.state = (self.state * 1103515245 + 12345) & 0x7FFFFFFF
return self.state
def predict_next(self, observed):
# Attack: Can predict next from any observed value
return (observed * 1103515245 + 12345) & 0x7FFFFFFF
# Vulnerable: Alternating increment pattern
class VulnerableAlternating:
def __init__(self):
self.counter = 0
self.increment = 1
def next(self):
# Vulnerable: Pattern is alternating +1, +2, +1, +2...
self.counter += self.increment
self.increment = 3 - self.increment # Toggles between 1 and 2
return self.counter
# Vulnerable: Time-division multiplexed
class VulnerableTimeDivision:
def __init__(self):
self.sequence = 0
def next(self):
# Vulnerable: Time component is observable
self.sequence += 1
timestamp = int(time.time())
return (timestamp << 16) | (self.sequence & 0xFFFF)
# Vulnerable: LFSR (Linear Feedback Shift Register)
class VulnerableLFSR:
def __init__(self, seed=1):
self.state = seed
def next(self):
# Vulnerable: LFSR is mathematically predictable
bit = ((self.state >> 0) ^ (self.state >> 2) ^
(self.state >> 3) ^ (self.state >> 5)) & 1
self.state = (self.state >> 1) | (bit << 15)
return self.state
// Vulnerable: Predictable value generation in Java
public class VulnerablePredictable {
// Vulnerable: Sequential counter
private static long counter = 0;
public static long vulnerableSequential() {
// Vulnerable: Next is exactly current + 1
return ++counter;
}
// Vulnerable: java.util.Random is LCG-based
private java.util.Random rand = new java.util.Random();
public long vulnerableRandom() {
// Vulnerable: Random uses 48-bit LCG internally
// State can be recovered from observed outputs
return rand.nextLong();
}
// Vulnerable: Predictable hash-based sequence
private long hashCounter = 0;
public String vulnerableHashSequence() {
// Vulnerable: Hash of sequential input is still sequential
hashCounter++;
return Integer.toHexString((int)hashCounter);
}
// Vulnerable: Time-based with counter
private int timeCounter = 0;
public long vulnerableTimeCounter() {
// Vulnerable: Both components are predictable
timeCounter++;
return System.currentTimeMillis() * 1000 + timeCounter;
}
// Vulnerable: Fixed offset sequence
private long offsetCounter = 1000000;
public long vulnerableOffset() {
// Vulnerable: Fixed offset doesn't add entropy
return offsetCounter++;
}
}
Fixed Code (C/Python)
// Fixed: Unpredictable value generation
#include <openssl/rand.h>
#include <stdio.h>
// Fixed: Cryptographically random sequence numbers
int secure_sequence(unsigned int *seq) {
// Fixed: Each value is independent and unpredictable
unsigned char bytes[4];
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
return -1;
}
*seq = (bytes[0] << 24) | (bytes[1] << 16) |
(bytes[2] << 8) | bytes[3];
return 0;
}
// Fixed: Random with counter for uniqueness
typedef struct {
unsigned int random_base;
unsigned int counter;
} SecureSequence;
int secure_init(SecureSequence *seq) {
unsigned char bytes[4];
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
return -1;
}
seq->random_base = (bytes[0] << 24) | (bytes[1] << 16) |
(bytes[2] << 8) | bytes[3];
seq->counter = 0;
return 0;
}
unsigned int secure_next(SecureSequence *seq) {
// Fixed: Random base ensures unpredictability
// Counter ensures uniqueness
seq->counter++;
return seq->random_base ^ seq->counter;
}
// Fixed: TCP-like sequence with random increment
int secure_tcp_sequence(unsigned int *current_seq) {
unsigned char random_increment[4];
if (RAND_bytes(random_increment, sizeof(random_increment)) != 1) {
return -1;
}
// Fixed: Random increment between 1 and 2^31
unsigned int increment = (random_increment[0] << 24) |
(random_increment[1] << 16) |
(random_increment[2] << 8) |
random_increment[3];
increment = (increment & 0x7FFFFFFF) | 1; // At least 1
*current_seq += increment;
return 0;
}
// Fixed: Fully random values
int secure_random_value(unsigned char *buffer, size_t len) {
// Fixed: No mathematical relationship between values
return RAND_bytes(buffer, len) == 1 ? 0 : -1;
}
# Fixed: Unpredictable value generation in Python
import secrets
import os
import hashlib
# Fixed: Cryptographically secure random
class SecureRandom:
@staticmethod
def next():
# Fixed: Each value independent and unpredictable
return secrets.randbits(64)
# Fixed: Secure sequence with random base
class SecureSequence:
def __init__(self):
# Fixed: Random base prevents prediction
self.random_base = secrets.randbits(64)
self.counter = 0
def next(self):
self.counter += 1
# Fixed: Even knowing counter, random_base prevents prediction
combined = f"{self.random_base}:{self.counter}"
return hashlib.sha256(combined.encode()).hexdigest()
# Fixed: Secure TCP-like sequence
class SecureTCPSequence:
def __init__(self):
self.sequence = secrets.randbits(32)
def next(self):
# Fixed: Random increment
increment = secrets.randbelow(2**31) + 1
self.sequence = (self.sequence + increment) & 0xFFFFFFFF
return self.sequence
# Fixed: Token generation with no predictability
class SecureTokenGenerator:
@staticmethod
def generate():
# Fixed: Pure random, no sequential component
return secrets.token_hex(32)
# Fixed: UUID-based identifiers
import uuid
class SecureIdentifier:
@staticmethod
def generate():
# Fixed: UUID4 uses secure random
return str(uuid.uuid4())
# Fixed: HMAC-based sequence
class SecureHMACSequence:
def __init__(self):
import hmac
self.key = os.urandom(32)
self.counter = 0
def next(self):
self.counter += 1
# Fixed: HMAC ensures unpredictability without key
import hmac
h = hmac.new(self.key, str(self.counter).encode(), hashlib.sha256)
return h.hexdigest()
// Fixed: Unpredictable value generation in Java
import java.security.SecureRandom;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class SecurePredictable {
private static final SecureRandom secureRandom = new SecureRandom();
// Fixed: Cryptographically secure random
public static long secureRandom() {
// Fixed: SecureRandom provides unpredictable values
return secureRandom.nextLong();
}
// Fixed: Secure sequence with random base
private long randomBase;
private long counter;
public SecurePredictable() {
this.randomBase = secureRandom.nextLong();
this.counter = 0;
}
public long secureNext() {
counter++;
// Fixed: Random base prevents prediction
return randomBase ^ counter ^ secureRandom.nextLong();
}
// Fixed: UUID for identifiers
public static String secureIdentifier() {
// Fixed: UUID4 uses SecureRandom
return UUID.randomUUID().toString();
}
// Fixed: HMAC-based sequence
private byte[] hmacKey;
public void initHMAC() {
hmacKey = new byte[32];
secureRandom.nextBytes(hmacKey);
}
public String secureHMACNext() throws Exception {
counter++;
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(hmacKey, "HmacSHA256");
mac.init(keySpec);
byte[] result = mac.doFinal(Long.toString(counter).getBytes());
return bytesToHex(result);
}
// Fixed: Secure TCP-like sequence
private int tcpSequence;
public int secureTcpNext() {
// Fixed: Random increment
int increment = secureRandom.nextInt(Integer.MAX_VALUE) + 1;
tcpSequence += increment;
return tcpSequence;
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
The fix uses cryptographically secure random generation where values have no mathematical relationship to previous outputs.
Exploited in the Wild
Predictable TCP Sequence Numbers (CVE-2002-1463)
A firewall generated predictable TCP initial sequence numbers, enabling attackers to inject packets into established connections or hijack sessions.
DNS Query ID Prediction (CVE-2000-0335)
DNS resolvers using predictable query IDs allowed attackers to spoof DNS responses, redirecting victims to malicious servers.
Tools to Test/Exploit
-
Scapy — Network packet manipulation for testing sequence number predictability.
-
Statistical analysis tools — Analyze sequences for mathematical patterns.
-
Sequence prediction scripts — Test LCG and other predictable sequences.
CVE Examples
-
CVE-2002-1463 — Firewall predictable TCP sequence numbers.
-
CVE-1999-0077 — Predictable TCP sequences enabling spoofing.
-
CVE-2000-0335 — Predictable DNS query IDs.
References
-
MITRE Corporation. "CWE-342: Predictable Exact Value from Previous Values." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/342.html
-
Bellovin, S. "Defending Against Sequence Number Attacks." RFC 1948. https://tools.ietf.org/html/rfc1948
-
NIST. "Recommendation for Random Number Generation." SP 800-90A. https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final