Overview
Security research firm Coinspect has identified the root cause of the Ill Bloom wallet drain series: a weak random number generator (RNG) built into the CryptoJS JavaScript cryptography library that has existed, largely undisturbed, for over 12 years.
The function CryptoJS.lib.WordArray.random() — used by wallet applications to generate cryptographically sensitive random values — provides insufficient entropy, making generated recovery phrases predictable enough to brute-force under certain conditions. Five cryptocurrency wallet applications that relied on this function for seed phrase generation were affected, with total losses exceeding $5.7 million.
The Flaw: CryptoJS Weak Entropy
CryptoJS is one of the most widely used JavaScript cryptography libraries, with tens of millions of npm downloads per month. The library's WordArray.random() function was implemented over a decade ago and does not use a cryptographically secure pseudo-random number generator (CSPRNG) in all environments where it runs.
Technical Root Cause
In browser environments, CryptoJS.lib.WordArray.random() does call window.crypto.getRandomValues() — the browser's CSPRNG. However, in non-browser JavaScript environments (Node.js scripts, build toolchains, some hybrid mobile runtimes), the function falls back to Math.random() — a non-cryptographic PRNG explicitly documented by its specification as unsuitable for security-sensitive use.
// Simplified illustration of the CryptoJS fallback behavior
WordArray.random = function(nBytes) {
var words = [];
// In browsers: uses window.crypto.getRandomValues (secure)
// In non-browser environments: falls back to Math.random() (INSECURE)
for (var i = 0; i < nBytes; i += 4) {
words.push(cryptoSecureRandomInt()); // This path is environment-dependent
}
return new WordArray.init(words, nBytes);
};Math.random() is not seeded from an OS entropy source and produces output that is predictable when the seed or internal state can be inferred. For wallet seed phrase generation, where the security of private keys depends entirely on the unpredictability of the random values, this fallback is catastrophic.
The Ill Bloom Drain Incidents
The Ill Bloom drain series affected wallets generated by five applications between [timeframe]. Attackers were able to reconstruct private keys by systematically brute-forcing the reduced entropy space created by the weak RNG.
Attack Methodology (Reconstructed)
1. Attacker identifies wallet applications using CryptoJS for seed generation
2. Determines which wallet creation code paths execute outside browser (weak RNG path)
3. Characterizes the entropy space: Math.random() seed possibilities for target time window
4. Systematically generates candidate seed phrases from the reduced entropy space
5. Checks each candidate against known wallet addresses for balance > 0
6. Matching addresses: private key derived, funds sweptThe attack is offline and parallelizable. With cloud compute, sweeping a constrained entropy space can complete in hours to days depending on the window size.
Financial Impact
| Metric | Value |
|---|---|
| Total estimated losses | $5.7 million |
| Affected wallet applications | 5 (names per Coinspect disclosure) |
| Root cause library | CryptoJS (npm: crypto-js) |
| Vulnerable function | CryptoJS.lib.WordArray.random() |
| Vulnerable environment | Non-browser JavaScript runtimes |
| Flaw age | ~12 years |
Affected Scope
Coinspect's research specifically addresses the five wallet applications in the Ill Bloom incident. However, the underlying flaw is broader:
Any application that:
- Uses
CryptoJS.lib.WordArray.random()for cryptographic key material, seed generation, or nonce generation - Runs in a non-browser JavaScript environment (Node.js, React Native with JSC, Electron renderer without browser crypto bridge, etc.)
...is potentially vulnerable to similar attacks.
This includes a wide class of DeFi tooling, wallet generators, key management scripts, and hybrid mobile applications that were built using CryptoJS before this flaw was understood.
Remediation
For Wallet Application Developers
Replace CryptoJS.lib.WordArray.random() immediately for any security-sensitive random value generation.
Node.js (recommended):
const { randomBytes } = require('crypto'); // Node.js built-in CSPRNG
const secureBytes = randomBytes(32); // 256 bits of secure entropyBrowser:
const secureBytes = new Uint8Array(32);
window.crypto.getRandomValues(secureBytes); // Browser CSPRNGUniversal (using the @noble/hashes or @noble/secp256k1 ecosystem):
import { randomBytes } from '@noble/hashes/utils';
const secureBytes = randomBytes(32);Avoid: CryptoJS.lib.WordArray.random(), Math.random(), or any RNG that does not explicitly document CSPRNG properties.
For Users With Affected Wallets
If you used one of the five affected wallet applications to generate a seed phrase:
- Assume your seed phrase is compromised — treat it as already known to attackers
- Generate a new seed phrase using a trusted, audited wallet (hardware wallet recommended)
- Transfer all funds to the new wallet address immediately
- Do not reuse the compromised wallet for any future transactions
- Check wallet history for any unauthorized transfers to understand your exposure window
For Developers Using CryptoJS
Audit your codebase for any use of CryptoJS.lib.WordArray.random():
# Search your codebase for the vulnerable call
grep -r "WordArray.random\|CryptoJS\.lib\.WordArray\.random" --include="*.js" --include="*.ts" .
# Also check for general CryptoJS random usage
grep -r "CryptoJS\.random\|require.*crypto-js" --include="*.js" --include="*.ts" .Replace all security-sensitive uses with a platform-appropriate CSPRNG as shown above.
Broader Implications
This incident highlights several recurring patterns in cryptographic security:
1. Library vs. Implementation Correctness
CryptoJS is not fundamentally broken — in browser environments it behaves correctly. The flaw is in the runtime-conditional fallback behavior. Cryptographic library consumers must understand the full range of execution environments their code will run in.
2. Entropy Source Assumptions
Cryptographic code must never assume a particular entropy source without verifying it at runtime. Environment-conditional behavior that silently degrades security is a systemic risk pattern.
3. Long Tail of Legacy Cryptographic Debt
A 12-year-old flaw in a widely used library affecting millions of applications demonstrates the persistence of cryptographic vulnerabilities. Many applications written years ago with then-current best practices now carry latent cryptographic risk.
4. Wallet Generation as Critical Path
Seed phrase generation is among the most security-critical operations in cryptocurrency software. It warrants isolated code review, use of well-audited dedicated libraries, and explicit testing across all target runtime environments.
References
- The Hacker News — CryptoJS Weak RNG Behind $5.7 Million in Drains Affects Five Crypto Wallet Apps
- Coinspect — Ill Bloom Wallet Drain Research
- CryptoJS npm package