A slot machine that only has 500 billion combinations pretending to have trillions — and someone already figured out the pattern
CVE-2026-71851 affects the CryptoJS.lib.WordArray.random() function in crypto-js versions ≥ 3.1.2-4 and < 4.0.0. These versions use a custom Multiply-With-Carry PRNG seeded by Math.random() instead of a cryptographically secure source. Requests for 128 or 256 bits of randomness yield effective search spaces of only ~2^39 and ~2^47 possibilities respectively — trivially enumerable on commodity hardware. The fix landed in version 4.0.0 in February 2020, over six years ago. The library is now discontinued; NodeJS and modern browsers ship native crypto modules that supersede it entirely.
The vendor's CRITICAL/9.0 rating reflects the *worst-case* outcome — complete private key recovery when the function is used for wallet seed generation, as proven by the Ill Bloom campaign that drained $5.7 million from five crypto wallet apps. However, the CVSS vector's own AC:H already signals that exploitation requires specific conditions. For enterprise defenders managing 10,000 hosts, the real question is whether any internal or customer-facing application calls WordArray.random() to generate session tokens, API keys, or HMAC secrets. Merely depending on crypto-js 3.x in package-lock.json is not sufficient to be exploitable — the application must actively invoke the vulnerable function for security-sensitive output. This condition significantly narrows the exploitable population, making the CRITICAL label overweight for most enterprise environments.
4 steps from start to impact.
Identify target application using crypto-js < 4.0.0
- Target application uses crypto-js ≥ 3.1.2-4 and < 4.0.0
- Application calls
WordArray.random()for security-sensitive value generation
- Most modern Node.js apps use native
crypto.randomBytes()— crypto-js random is rarely the entropy source - The vulnerable function is one of dozens in the library; most consumers use AES/SHA functions with externally-supplied keys
- The library has been abandoned since 2021; security-conscious teams have migrated
CryptoJS.lib.WordArray.random( calls.Harvest security-sensitive output
- Access to the output derived from WordArray.random() — either public (blockchain) or obtainable via network position
- Enterprise session tokens are typically transmitted over TLS and expire quickly
- API keys generated at provisioning time may have been rotated since the vulnerable version was in use
- Attacker needs to know or guess that the target value was generated by the weak PRNG
Offline brute-force enumeration of PRNG state
Math.random() seeds the generator, the attacker enumerates seed values from 0 to 2^32−1 and simulates the MWC output for each seed. For 128-bit requests this produces ~2^39 candidates; for 256-bit requests, ~2^47. On a modern GPU rig or cloud instance, 2^47 iterations complete in hours to days. Tools like crypto-bruteforce-seed-generator and custom MWC reconstruction scripts exist publicly on GitHub.- Commodity compute resources (GPU or cloud — estimated cost < $500 for full enumeration)
- Knowledge of how many bytes were requested in the original
WordArray.random()call
- If the application applied additional entropy mixing (e.g., concatenating
crypto.randomBytes()output), brute force becomes infeasible - Enumeration time increases if the attacker doesn't know exact parameters
Recover secret and exploit
- Successful match from step 3
- Target secret has not been rotated since generation
- Enterprise key rotation policies may have invalidated old keys
- Session tokens with short TTLs (< 24h) are impractical targets
- MFA or IP-binding on sessions blocks replay even with a valid token
WordArray.random() calls — Run grep -r 'WordArray.random\|CryptoJS.lib.WordArray.random' . across all Node.js codebases and bundled assets. Any hit in a security-sensitive context (token generation, key derivation, nonce creation) is a confirmed exposure. Prioritize this audit within the noisgate mitigation SLA of 30 days.WordArray.random() was used for session secrets, API keys, HMAC keys, or any authentication material, rotate those secrets immediately — upgrading crypto-js alone does not fix already-generated weak values. This is the most critical compensating control.crypto.randomBytes() or the Web Crypto API crypto.getRandomValues(). If immediate replacement is impractical, pin to 4.0.0. Deploy within the 30-day noisgate mitigation SLA.- WAF / network-level controls — this is an offline cryptographic weakness; there is no network payload to inspect or block.
- Rate limiting on authentication endpoints — the brute-force happens offline against the PRNG state, not against the target service's login endpoint.
- Upgrading crypto-js without rotating secrets — already-generated weak keys remain weak regardless of the library version. The entropy deficit is baked into the output at generation time.
The supporting signals.
| In-the-Wild Exploitation | Confirmed. Coinspect's *Ill Bloom* investigation documented two drain waves (May 27 and May 30–Jul 13, 2026) totaling $5.7M from 953 compromised accounts across five crypto wallet apps. |
|---|---|
| Proof-of-Concept | Multiple brute-force seed enumeration tools exist on GitHub (e.g., DjamaGuelleh/crypto-bruteforce-seed-generator). Coinspect demonstrated the full chain: enumerate MWC outputs → derive BIP39 phrases → check addresses against blockchain. |
| EPSS Score | 0.00317 (low percentile) — reflects the narrow exploitability condition requiring specific function usage, not just dependency presence. |
| KEV Status | Not listed as of 2026-08-09. |
| CVSS Vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H — 9.0. The AC:H is justified: exploitation requires the target to use the specific vulnerable function. The S:C (Changed scope) reflects cross-boundary impact (e.g., wallet funds on a different trust boundary). |
| Affected Versions | crypto-js ≥ 3.1.2-4, < 4.0.0. Versions 3.2.0 and 3.2.1 briefly used native crypto but 3.3.0 reverted to the weak MWC generator. |
| Fixed Version | 4.0.0 (released February 2020). The library is now discontinued — no further releases expected. |
| Scanning / Exposure | npm reports ~10M weekly downloads and 15,583 dependents for crypto-js. However, most dependents use AES/SHA functions, not WordArray.random(). Socket.dev and Snyk flag all < 4.0.0 installs. |
| Disclosure Timeline | Weak PRNG introduced June 2014. Fixed in v4.0.0 Feb 2020. Ill Bloom exploitation discovered May 2026. Coinspect public disclosure Aug 5, 2026. CVE published Aug 7, 2026. |
| Researcher / Org | Coinspect (CEO Juliano Rizzo) discovered and coordinated disclosure. GHSA-rg76-677x-56q9 published by maintainer Evan Vosberg. |
Sources.
Why this verdict
- Narrow trigger condition: The vulnerability requires the application to call
CryptoJS.lib.WordArray.random()for security-sensitive values. Most crypto-js consumers use it for AES encryption or SHA hashing with externally-provided keys — the vulnerable function is not in the hot path for typical usage. - Six-year-old fix: Version 4.0.0 shipped in February 2020. Any application still on 3.x has had over six years of remediation runway. This narrows the exploitable population to unmaintained or legacy codebases.
- Offline attack, not RCE: This is not a remotely-triggerable code execution flaw. The attacker must first obtain or observe the output of the weak PRNG, then perform offline brute-force enumeration. There is no shell, no lateral movement, no privilege escalation inherent to the vulnerability itself.
- Role multiplier: crypto-js is an npm library embedded in application code. It does not run on domain controllers, hypervisors, or network edge appliances. In *typical enterprise roles* (Node.js API servers, frontend builds), the blast radius is confined to the secrets generated by that single application — session tokens, API keys, or HMAC signing keys. The worst plausible high-value role is a backend service that generates authentication tokens for a customer-facing SaaS platform; compromise of those tokens could enable account takeover across the tenant. This outcome is serious but bounded to one application's trust boundary, not fleet-scale. For crypto wallet applications (a narrow but real deployment role), the blast radius is total fund loss — proven by $5.7M in Ill Bloom drains — but wallet apps represent a tiny fraction of enterprise crypto-js deployments.
- Active exploitation prevents MEDIUM: Despite all friction, Coinspect documented $5.7M in real losses. The attack tooling is public. If an enterprise app does use the vulnerable function, the exploitation cost is < $500 in cloud compute.
Why not higher?
The CRITICAL rating assumes every crypto-js 3.x installation is exploitable, but the advisory itself states that merely depending on the library is insufficient — the application must actively use WordArray.random() for secret generation. This is a narrow subset of the installed base. The vulnerability provides no RCE, no lateral movement, and no privilege escalation; it is a cryptographic weakness whose blast radius is bounded to the secrets generated by one application. The fix has been available for six years.
Why not lower?
Active in-the-wild exploitation is confirmed with $5.7M in documented losses. Public brute-force tooling makes exploitation accessible to low-skill attackers for < $500. The EPSS score of 0.00317 is low but does not account for the proven Ill Bloom campaign. With ~10M weekly downloads and 15,583 dependents, the probability that at least one application in a 10,000-host enterprise uses the vulnerable function for token or key generation is non-trivial. Already-generated weak secrets cannot be retroactively strengthened by upgrading — they must be identified and rotated.
Crowdsourced verification payload.
Run this script on any host with Node.js installed to check whether a project's node_modules contains a vulnerable crypto-js version. Execute from the project root directory: bash check_cryptojs.sh /path/to/project. No elevated privileges required.
#!/usr/bin/env bash
# check_cryptojs.sh — Detect vulnerable crypto-js versions (CVE-2026-71851)
# Usage: bash check_cryptojs.sh /path/to/project
# Requires: node (any version), or just grep/jq
set -euo pipefail
PROJECT_DIR="${1:-.}"
RESULT="UNKNOWN"
if [ ! -d "$PROJECT_DIR" ]; then
echo "ERROR: Directory $PROJECT_DIR does not exist."
exit 2
fi
# Check for crypto-js in node_modules
PKG_JSON="$PROJECT_DIR/node_modules/crypto-js/package.json"
if [ ! -f "$PKG_JSON" ]; then
# Also check lock files for transitive deps
if grep -q '"crypto-js"' "$PROJECT_DIR/package-lock.json" 2>/dev/null || \
grep -q 'crypto-js@' "$PROJECT_DIR/yarn.lock" 2>/dev/null; then
echo "INFO: crypto-js found in lock file but not installed. Run npm install first."
RESULT="UNKNOWN"
else
echo "PATCHED — crypto-js is not a dependency of this project."
exit 0
fi
else
VERSION=$(node -e "console.log(require('$PKG_JSON').version)" 2>/dev/null || echo "")
if [ -z "$VERSION" ]; then
VERSION=$(grep '"version"' "$PKG_JSON" | head -1 | sed 's/.*"version".*"\(.*\)".*/\1/')
fi
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse crypto-js version."
exit 2
fi
echo "INFO: Found crypto-js version $VERSION"
# Parse major version
MAJOR=$(echo "$VERSION" | cut -d. -f1)
if [ "$MAJOR" -ge 4 ] 2>/dev/null; then
echo "PATCHED — crypto-js $VERSION is >= 4.0.0 (not affected by CVE-2026-71851)."
exit 0
else
echo "VULNERABLE — crypto-js $VERSION is < 4.0.0 (affected by CVE-2026-71851)."
# Additional check: see if WordArray.random is actually called
echo ""
echo "=== Checking for WordArray.random() usage in source code ==="
HITS=$(grep -r -l 'WordArray\.random\|CryptoJS\.lib\.WordArray\.random' "$PROJECT_DIR/src" "$PROJECT_DIR/lib" "$PROJECT_DIR/app" 2>/dev/null | head -20 || true)
if [ -n "$HITS" ]; then
echo "WARNING: WordArray.random() calls found in application code:"
echo "$HITS"
echo ""
echo "VULNERABLE — confirmed usage of the weak PRNG function."
else
echo "NOTE: No WordArray.random() calls found in src/lib/app directories."
echo "VULNERABLE — version is affected, but exploitability depends on whether"
echo " WordArray.random() is called for security-sensitive generation."
echo " Check all code paths including bundled/transpiled files."
fi
exit 1
fi
fi
echo "$RESULT"
exit 2