Randomness and RNG attacks

A surprisingly large proportion of real cryptographic failures trace back to a single root cause: the random numbers were not random enough. Predictable keys, reused nonces, and biased parameters turn a mathematically sound system into an open door.

Everything downstream of the seed

Symmetric keys, asymmetric key pairs, nonces, IVs, salts, session tokens, and DH ephemeral values all require unpredictable random input. A cryptographic algorithm processes whatever numbers it is given; it does not detect or compensate for weak input. An AES key derived from a predictable seed is just as predictable as the seed itself.

DUAL_EC_DRBG

The most famous RNG backdoor. DUAL_EC_DRBG (Dual Elliptic Curve Deterministic Random Bit Generator) was standardised by NIST in 2006 and shown by Shumow and Ferguson in 2007 to permit a backdoor (and later analysed by Bernstein, Lange and Niederhagen in 2015, and confirmed by the Snowden documents): if the discrete logarithm relating the two elliptic curve points in the standard is known, all future output can be predicted from 32 bytes of observed output.

This was the default PRNG in RSA Security’s BSAFE library and was present in Juniper ScreenOS firmware. It illustrates that the threat model for RNG attacks includes not just poor implementations but also deliberate subversion of standards.

Embedded and IoT devices

Embedded devices are the most common victim of RNG failures in practice. At boot time, the entropy pool may be nearly empty: the system has been running for only seconds, the hardware lacks dedicated entropy sources, and the seed file from the previous boot may not exist (first boot, after a firmware flash, or on a device that does not persist state across reboots).

Keys generated at first boot on devices with low-entropy RNGs are often weak. Research (Heninger et al., 2012: “Mining Your Ps and Qs”) scanned the internet’s RSA and DSA public keys and found that a significant fraction shared prime factors: the only explanation is that the keys were generated with insufficient entropy, causing different devices to draw from the same small pool of random state.

Two keys that share a prime factor can both be factored trivially:

from math import gcd

n1 = int("RSA modulus 1 hex here", 16)
n2 = int("RSA modulus 2 hex here", 16)

shared = gcd(n1, n2)
if shared > 1:
    p = shared
    q1 = n1 // p
    q2 = n2 // p
    print(f"p={p}\nq1={q1}\nq2={q2}")

Against embedded devices, collecting the public keys and running GCD checks across the set often pays. factordb.com and RsaCtfTool include tools for this.

Nonce reuse in stream ciphers and AEAD

Stream ciphers and AEAD modes (AES-GCM, ChaCha20-Poly1305) are catastrophically vulnerable to nonce reuse. If the same nonce is used with the same key to encrypt two different plaintexts, the keystream is the same for both. XORing the two ciphertexts gives the XOR of the plaintexts, from which both can be recovered given any partial knowledge of either.

For AES-GCM, nonce reuse also allows forgery: an attacker who observes two encryptions under the same nonce can compute the authentication key H and then forge arbitrary authenticated ciphertext.

Finding nonce reuse requires observing many ciphertext blocks under the same key. In protocols that increment or randomise nonces correctly this does not occur. In systems that generate nonces from a poor RNG, or that reset nonce counters at reconnection, or that have bugs in distributed systems where multiple nodes encrypt under the same key without coordination, nonce reuse appears in practice.

Predictable session tokens

Session tokens, CSRF tokens, and authentication cookies generated from weak RNGs are predictable. PHP’s rand() on older installations used a 32-bit seed; knowing the approximate seed (system time at token generation) reduced the search space dramatically.

Test a target’s session token entropy by collecting many tokens and checking their randomness:

import requests, base64, math, collections

tokens = []
for _ in range(100):
    r = requests.get('https://target.example.com/login')
    token = r.cookies.get('session')
    if token:
        tokens.append(base64.b64decode(token + '=='))

# check byte distribution across all collected tokens
all_bytes = b''.join(tokens)
counts = collections.Counter(all_bytes)
total = len(all_bytes)
entropy = -sum((c/total) * math.log2(c/total) for c in counts.values() if c > 0)
print(f'Byte entropy: {entropy:.2f} bits (max 8.0)')

Entropy below 7.5 bits per byte for what should be random tokens suggests a weak generator or a pattern worth investigating.

ML-assisted bias detection

Machine learning models trained to distinguish truly random sequences from PRNG output can identify weak generators from their output, even when the bias is too subtle for classical statistical tests (NIST SP 800-22).

For red team use, the practical application is: collect a large sample of values from a target system (session tokens, nonces in a custom protocol, key material in a response), train or apply a pre-trained classifier, and use a positive result to justify further investigation. The ML model does not recover the seed; it flags the target for manual follow-up.

DSA/ECDSA nonce bias

DSA and ECDSA signature generation requires a fresh random nonce per signature. If the nonce carries a few bits of bias, collecting enough signatures allows lattice-based private key recovery; a single bit of bias falls instead to the Fourier-analysis approach (LadderLeak), which lattices cannot reach. The PlayStation 3 signing key fell in 2010 to the simplest case: Sony reused a constant nonce, which the two-equation algebra recovers directly.

For targets that expose many ECDSA signatures under the same key (TLS, code signing, JWT with ES256), collecting signatures and running lattice reduction on the nonces is a viable attack path if any bias is suspected.