Aug 7, 2026·7 min read·0 visits
Legacy versions of crypto-js generate weak keys using Math.random() in a custom MWC generator, enabling attackers to systematically brute-force and drain cryptocurrency wallets.
A severe, twelve-year-old cryptographic weakness in crypto-js (versions < 4.0.0) generated pseudorandom numbers using a custom Multiply-With-Carry (MWC) algorithm seeded from the non-secure Math.random(). This reduces 128-bit and 256-bit key spaces to just 2^39 and 2^47 possibilities, allowing offline brute-force attacks.
The crypto-js library is a widely deployed JavaScript implementation of cryptographic standards, providing developers with hashing, cipher, and key derivation utilities. In versions preceding 4.0.0, the library exposed a critical vulnerability within its pseudorandom number generator (PRNG) implementation, specifically located in CryptoJS.lib.WordArray.random(). This component is commonly utilized by downstream applications to generate cryptographic keys, initialization vectors, and mnemonic seed phrases.\n\nThe underlying weakness stems from the implementation of a custom Multiply-With-Carry (MWC) algorithm seeded by the non-cryptographically secure function Math.random(). This design deviates from standard cryptographic engineering practices, which dictate the use of native, operating-system-level cryptographically secure PRNGs (CSPRNGs). Consequently, any cryptographic secret generated using these versions of the library suffers from a severe lack of entropy.\n\nThe vulnerability is cataloged as CVE-2026-71851 and carries a CVSS v3.1 base score of 9.0. The compromised entropy allows remote, unauthenticated attackers to systematically reconstruct generated secrets. This poses a threat to decentralized applications, cryptocurrency wallets, and authentication systems relying on historical installations of the library.
The root cause of CVE-2026-71851 is the implementation of a custom variation of George Marsaglia's Multiply-With-Carry (MWC) algorithm in src/core.js. This custom generator was introduced to synthesize random words for CryptoJS.lib.WordArray.random(). Rather than sourcing security-hardened entropy, the implementation initializes the generator state using the standard JavaScript Math.random() function.\n\nThe seed initialization relies on the expression Math.random() * 0x100000000. JavaScript performs bitwise operations on 32-bit signed integers, which causes any precision beyond 32 bits to be truncated. This mathematical constraint bounds the initial starting entropy of the seed state, m_w, to a maximum of 32 bits. The state is then updated deterministically inside a loop, meaning the generator does not integrate fresh, independent source entropy during subsequent iterations.\n\nFor each subsequent word generated, the only dynamic input is a single-bit sign conditional: Math.random() > 0.5 ? 1 : -1. Consequently, a 128-bit key (requiring four 32-bit words) receives 32 bits of initial seed entropy plus 8 bits of sign-choice entropy, culminating in approximately 40 bits of total entropy. For a 256-bit key, the generator yields roughly 48 bits of entropy, reducing the effective keyspace to 2^47 states, which is trivial to brute-force on modern hardware.
The original vulnerable random generation logic was implemented as follows in crypto-js prior to version 4.0.0:\n\njavascript\nrandom: function (nBytes) {\n var words = [];\n var r = (function (m_w) {\n var m_w = m_w;\n var m_z = 0x3ade68b1;\n var mask = 0xffffffff;\n return function () {\n m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask;\n m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask;\n var result = ((m_z << 0x10) + m_w) & mask;\n result /= 0x100000000;\n result += 0.5;\n return result * (Math.random() > .5 ? 1 : -1);\n }\n });\n for (var i = 0, rcache; i < nBytes; i += 4) {\n var _r = r((rcache || Math.random()) * 0x100000000);\n rcache = _r() * 0x3ade67b7;\n words.push((_r() * 0x100000000) | 0);\n }\n return new WordArray.init(words, nBytes);\n}\n\n\nAn intermediate patch attempted to secure this via a wrapper (secureRandom) but introduced precision loss and collisions. The wrapper converted high-entropy buffers to strings and parsed them back to floats, collapsing multiple distinct seeds into identical values (e.g., '0.1', '0.10', and '0.100' all evaluate to the float 0.1).\n\nThe robust patch in version 4.0.0 completely eliminated the custom generator in favor of platform-native cryptographic primitives. It routes entropy collection directly to crypto.randomBytes() in Node.js environments and window.crypto.getRandomValues() in browser environments. This preserves full 32-bit entropy per generated word.\n\nmermaid\ngraph LR\n Start["Math.random()"] -->|Truncation to 32-bit| Seed["m_w Seed (32 bits)"]\n Seed -->|Loop Iterations| Gen["MWC State Machine"]\n Gen -->|Sign Choices (+1 bit/word)| Out["Output WordArray"]\n Out -->|128-bit Key| Ent1["40 Bits Total Entropy"]\n Out -->|256-bit Key| Ent2["48 Bits Total Entropy"]\n
Exploitation of CVE-2026-71851 does not require an active, interactive network payload. Instead, attackers execute passive, offline brute-force attacks against target public keys or addresses harvested from public blockchain ledgers. The primary prerequisite is that the target account was generated using a vulnerable version of crypto-js (typically bundled in unmaintained hybrid mobile apps or browser-based wallets).\n\nThe attack methodology involves reconstructing the custom MWC state generator. Threat actors seed the simulated generator with values starting from 0 to 2^32 - 1. For each seed, the actor simulates the sign-conditional choices, producing candidates for the 12-word or 24-word BIP39 mnemonic phrases.\n\nOnce the candidate mnemonic phrases are generated, they are programmatically derived into cryptographic public addresses using standard derivation paths (such as BIP44 for Ethereum or Bitcoin). If a derived address matches an active address on the blockchain, the corresponding private key is exposed, enabling the attacker to sign transactions and transfer assets. The entire keyspace can be exhaustively scanned in a highly parallelized manner using graphics processing units (GPUs).
The impact of this cryptographic failure is severe, leading to unauthorized asset transfer and full compromise of secret key material. In cryptocurrency contexts, the generation of low-entropy private keys renders wallet recovery phrases guessable. Since the public ledger reveals all active addresses, attackers can verify candidate keys offline without triggering any network security controls or rate-limiting mechanisms.\n\nFor non-blockchain applications, the vulnerability compromises session tokens, password reset tokens, and API credentials generated via WordArray.random(). If an application uses these keys for HMAC signatures, an attacker can forge signatures and bypass authentication entirely. Because the underlying entropy is bound to a maximum of 48 bits, traditional high-strength algorithms (such as AES-256) offer no real security, as the key space is restricted to a fraction of its intended strength.\n\nThe CVSS v3.1 score of 9.0 reflects the critical nature of the compromise. Although the attack complexity is classified as High due to the requirement of offline brute-forcing and blockchain state verification, the impact on confidentiality, integrity, and availability is maximum (High). There is documented evidence of active exploitation of this vulnerability in the wild under the security campaign name 'Ill Bloom'.
Remediation of CVE-2026-71851 requires a multi-phase approach encompassing software upgrades, code audits, and secret rotation. The primary action is to upgrade crypto-js to version 4.0.0 or higher. Developers must audit their transitive dependency trees using package manager tools like npm ls crypto-js or yarn why crypto-js to ensure older version ranges (such as 3.x) are completely purged from application bundles.\n\nIf upgrading the package is not immediately viable, developers must override the default random generator. This can be achieved by writing a secure polyfill that overrides CryptoJS.lib.WordArray.random with a custom function utilizing window.crypto.getRandomValues or Node.js's native crypto.randomBytes. A sample remediation snippet for browser environments is detailed below:\n\njavascript\nCryptoJS.lib.WordArray.random = function (nBytes) {\n var words = [];\n var r = new Uint32Array(Math.ceil(nBytes / 4));\n window.crypto.getRandomValues(r);\n for (var i = 0; i < nBytes; i += 4) {\n words.push(r[i / 4]);\n }\n return new CryptoJS.lib.WordArray.init(words, nBytes);\n};\n\n\nImportantly, software patches do not retroactively secure keys, secrets, or mnemonic phrases that were generated using the vulnerable code. Any cryptographic asset, API token, or cryptocurrency seed phrase generated under crypto-js versions prior to 4.0.0 must be treated as permanently compromised. Users and administrators must generate new secrets using a cryptographically secure generator and migrate all associated assets and configurations immediately.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
crypto-js brix | >= 3.1.2-4, < 4.0.0 | 4.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-338 |
| Attack Vector | Network |
| CVSS v3.1 Score | 9.0 (Critical) |
| Exploit Status | Active In-The-Wild |
| Primary Impact | Private Key Compromise and Asset Theft |
| Component | CryptoJS.lib.WordArray.random() |
The product uses a pseudorandom number generator (PRNG) in a security context, but the PRNG's algorithm is not cryptographically strong.
A session data exposure vulnerability in the Hono web application framework (hono/jsx module) allows consecutive users to receive cached HTML outputs containing private data. When JSX components wrapped in `memo()` are rendered on the server, the caching mechanism utilizes a module-level closure that persists across independent HTTP requests. When subsequent requests occur with matching props, the components are not re-evaluated, and cached HTML is served. If these components read request-scoped or session-specific data via ambient APIs, the data of the first user is exposed to subsequent users.
An uncontrolled resource consumption vulnerability (CWE-400) exists in pypdf prior to version 6.15.0. When extracting text from a specially crafted PDF document, the parser fails to restrict token lengths within /ToUnicode CMap streams, causing unbounded memory allocation and process termination via Out-of-Memory (OOM) crashes.
A Denial of Service (DoS) vulnerability exists in pypdf prior to version 6.15.0. When parsing maliciously crafted PDF files containing excessively large CID font width ranges, the library suffers from CPU starvation and memory exhaustion due to unconstrained loop expansion.
A vulnerability in Netty's Redis codec allows remote unauthenticated attackers to cause a memory-pinning Denial of Service (DoS) due to the failure to release partial aggregate state when specific error conditions occur in RedisArrayAggregator. When processing Redis Serialization Protocol (RESP) messages, the aggregator fails to clear internal queues and release retained direct byte buffers on exception paths triggered by exceeded maxElements or invalid length properties. If the pipeline does not explicitly tear down the connection upon detecting a decoder error, subsequent elements continue utilizing the stale context, allowing memory blocks to remain indefinitely pinned.
CVE-2026-54164 is a class/type confusion vulnerability (CWE-843) in API Platform Core. When processing relationships via Internationalized Resource Identifiers (IRIs) in write requests, the framework's normalizer fails to verify if the resolved resource matches the expected type. For PHP applications utilizing untyped properties, the mismatched object is silently assigned, breaking domain logic and data integrity.
An unsafe option guard bypass vulnerability exists in GitPython before version 3.1.58. When keyword arguments are passed to Git commands with split_single_char_options=False, GitPython's argument validation helper fails to inspect the combined short-option value. This discrepancy allows short-option token smuggling or clustering manipulation, enabling remote attackers to bypass option blocklists and execute arbitrary system commands.