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
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. |
noisgate verdict.
The single most decisive factor is the narrow exploitability condition: only applications that actively call WordArray.random() for security-sensitive generation are vulnerable — merely depending on crypto-js 3.x is not sufficient, and the vast majority of the 15,583 dependents use it for AES/SHA operations with externally-supplied keys. Active in-the-wild exploitation of crypto wallets prevents further downgrade below HIGH.
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.
What to do — in priority order.
- Audit all applications for
WordArray.random()calls — Rungrep -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. - Rotate all secrets generated by vulnerable code paths — If audit confirms
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. - Pin or upgrade crypto-js to ≥ 4.0.0 or replace with native crypto — Since crypto-js is abandoned, the best path is replacing all usage with Node.js built-in
crypto.randomBytes()or the Web Crypto APIcrypto.getRandomValues(). If immediate replacement is impractical, pin to 4.0.0. Deploy within the 30-day noisgate mitigation SLA. - Add SCA policy to block crypto-js < 4.0.0 in CI/CD — Configure Snyk, Socket.dev, or npm audit to fail builds that include crypto-js < 4.0.0. This prevents reintroduction via transitive dependencies.
- 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.
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 2If you remember one thing.
WordArray.random() is called for session tokens, API keys, HMAC secrets, or any cryptographic material. If it is, rotate those secrets now — upgrading the library does not fix already-generated weak values. Under the noisgate mitigation SLA for HIGH, deploy compensating controls (upgrade or replace crypto-js, add CI/CD block rule, rotate affected secrets) within 30 days. Complete full remediation (library replacement and secret rotation verified across all environments) within the 180-day noisgate remediation SLA. If you discover any application generating authentication tokens via the vulnerable function, treat that specific application as an emergency — the exploitation cost is under $500 in cloud compute and public tooling exists.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.