Technical Disclosure: The CryptoJS Randomness Vulnerability
Our initial Ill Bloom disclosure intentionally withheld the exact software responsible for generating vulnerable recovery phrases. Two months later, we are now disclosing that the affected recovery phrases were generated through software wallets that relied on a weak randomness source from the CryptoJS library.
Instead of producing truly unpredictable 128- or 256-bit entropy, the vulnerable CryptoJS implementation output was limited to effective search spaces of approximately 2³⁹ and 2⁴⁷ possibilities, respectively.
These reduced search spaces made affected recovery phrases computationally feasible to enumerate, allowing attackers to identify funded addresses and drain assets across multiple blockchains.
This article focuses on providing historical context and clarifying which software versions are vulnerable, helping researchers and developers identify affected systems. We are not disclosing exploit details or every possible attack optimization; we’ll leave those for a future article.
How CryptoJS Became a Source of Wallet Entropy
CryptoJS has long been a widely used cryptographic library in the JavaScript ecosystem, commonly used for hashing, encryption, key derivation, and other cryptographic utilities.
As JavaScript applications expanded into environments such as React Native, some developers adopted CryptoJS as a pure-JavaScript alternative for cryptographic operations across platforms. Public discussions show that CryptoJS randomness had been discussed as a possible entropy source for generating BIP39 recovery phrases as early as 2018.
However, the presence of a vulnerable CryptoJS version does not mean that an application is affected. The issue only affects wallet applications that use the vulnerable randomness implementation as a source of entropy for generating BIP39 recovery phrases.
The Entropy Reduction Vulnerability
The underlying vulnerability was in CryptoJS’s random number generator. In vulnerable versions, CryptoJS.lib.WordArray.random() relied on a custom variation of George Marsaglia’s Multiply-With-Carry (MWC) pseudorandom number generator, which is not a cryptographically secure random number generator.
The vulnerable implementation was introduced in June 2014 as part of an attempt to strengthen WordArray.random() in response to GitHub issue #7, “randomBytes is not random enough”, and was implemented in commit ff1f003. It replaced repeated calls to Math.random() with the MWC-based generator described above.
The issue was not that Math.random() appeared somewhere in the implementation. Using a modern JavaScript engine’s PRNG directly would also be unsafe for cryptographic key generation, but this implementation introduced a weaker generator, causing the possible outputs to collapse into a much smaller, enumerable search space.
The 2014 report flagging that Math.random() was never intended for cryptographic use.
The comment proposing the MWC-based generator.
How the Vulnerable Code Reached Wallets
One example of how this vulnerable implementation reached wallets is the ferrumnet/bip39 fork of the bip39 library. The upstream bip39 implementation obtained entropy through the randombytes package, which delegates to crypto.randomBytes on Node.js and crypto.getRandomValues in browsers.
As part of its adaptation for React Native, this fork replaced that dependency with the pure-JavaScript CryptoJS library. The change was made to avoid relying on platform-specific cryptographic APIs and provide a pure-JavaScript implementation compatible with React Native.
The Fix and Version History
The vulnerable MWC-based pseudorandom number generator was introduced in CryptoJS 3.1.2-4 in June 2014 (commit) and remained present in all 3.x releases except 3.2.0 and 3.2.1. Those two releases temporarily replaced the MWC implementation with the platform’s native cryptographically secure random source.
In CryptoJS 3.3.0 (commit) that change was reverted because adopting the platform-native cryptographic random source was considered a breaking change. As a result, projects depending on the CryptoJS 3.x release line could resolve to newer versions that still contained the vulnerable MWC-based implementation.
The platform-native cryptographic random source was restored in February 2020 version 4.0.0 (commit).
At the downstream dependency level, the ferrumnet/bip39 fork remained dependent on the vulnerable CryptoJS 3.x line and received no commits after November 2019.