Feb 20, 2026·6 min read·51 visits
Invoking `maskn(0)` on a BigNumber instance corrupts its internal state by setting its length to 0. This causes critical methods like `toString()` to enter an infinite loop, crashing Node.js applications.
A logic error in the widely used 'bn.js' BigNumber library allows for a Denial of Service via state corruption. By invoking the bitwise masking function with a zero length, an attacker can violate internal object invariants, creating a 'ghost' number with a length of zero. This corrupted state forces subsequent operations like serialization or division into infinite loops, freezing the single-threaded Node.js process instantly.
JavaScript has a complicated relationship with numbers. For years, we were stuck with IEEE 754 doubles, meaning anything integer-like above $2^{53}$ was a gamble. Enter bn.js, the unsung hero of the ecosystem. It is the bedrock for elliptic curve cryptography, blockchain implementations, and basically anything in Node.js that needs to count higher than a standard database ID.
But here's the thing about foundational libraries: when they crack, the tremors are felt everywhere. CVE-2026-2739 isn't a buffer overflow or a remote code execution via some complex gadget chain. It's a logic bug. A simple, stupid logic bug that turns a mathematical operation into a weapon of mass thread-destruction.
This vulnerability exploits the internal representation of these big numbers. By asking the library to perform a specific bitwise operation—masking a number with zero bits—we can trick the object into an invalid state. It creates a number that technically exists but has no data. When the library tries to read this 'ghost' number, it panics, loops, and takes your server down with it.
To understand the bug, you have to look under the hood of bn.js. Since JavaScript didn't support 64-bit integers natively for a long time, bn.js implements numbers as an array of 'words' (usually 26-bit chunks) and a length property tracking how many words are in use.
The Invariant:
The library relies on a strict invariant: length must always be at least 1. Even the number 0 is represented as words: [0], length: 1. There is no such thing as a number with no words.
The Glitch:
The vulnerability lies in maskn(bits) (and imaskn). This function is supposed to keep only the lowest n bits of the number. The logic calculates how many words are needed to hold those bits and truncates the rest.
However, the developers forgot a critical edge case: maskn(0). When you ask for the lowest 0 bits, the calculation effectively wipes the array. Prior to version 5.2.3, this operation would set this.length = 0.
Congratulations. You have created a zombie object. It is a BN instance that violates the fundamental laws of its own universe. It has no length. It is a void.
The fix is almost insultingly simple, which is characteristic of the most dangerous bugs. The developers just needed to ensure that if the masking operation wipes the number clean, it resets to the canonical 'zero' state rather than the 'void' state.
Here is the diff from the patch. Note the explicit check for the zero-length condition:
// lib/bn.js
BN.prototype.imaskn = function imaskn(bits) {
// ... existing logic truncating words ...
// The Vulnerable State: words might be empty now.
+ // The Fix: Force it back to a valid Zero.
+ if (this.length === 0) {
+ this.words[0] = 0;
+ this.length = 1;
+ }
return this._strip();
};Without this guard clause, this.length remains 0. The _strip() method, which is supposed to remove leading zero words, assumes there is at least one word to check. When length is 0, the internal logic of downstream functions collapses.
You don't need Metasploit for this. You don't need heap spraying. You just need a Node.js console. The exploitation is trivial because the corruption happens silently. The crash doesn't occur when you create the bad number; it happens when you try to look at it.
The PoC:
const BN = require('bn.js');
// Step 1: Create a perfectly normal number
const target = new BN('123456');
// Step 2: Corrupt it.
// We ask for the lowest 0 bits. Theoretically, this is 0.
// Practically, it creates an invalid object.
target.maskn(0);
// Step 3: Trigger the infinite loop.
// toString() relies on length to iterate.
// With length 0, the loop condition is never met or underflows.
console.log("Goodbye, Event Loop...");
target.toString(); Why toString() hangs:
The toString method usually performs repeated division and modulo operations to convert the number base (e.g., base 10). These algorithms loop while (number > 0). Because the internal state is corrupted (length: 0), the comparison logic fails to report '0', or the division step produces a result that doesn't reduce the problem size, resulting in an infinite while(true) scenario.
In a threaded environment like Java or C++, a CPU-spinning infinite loop is bad, but it usually only kills one worker thread. The rest of the application limps on.
Node.js is single-threaded. It runs on an event loop. If any operation blocks that loop—like a while(true) caused by a corrupt BigNumber—the entire process stops. It stops accepting HTTP requests. It stops processing database callbacks. It stops responding to health checks.
Attack Vectors:
bn.js to handle token amounts. If a user can pass a parameter that results in a bit-mask of 0, they can freeze the processing node.N=0 triggers the DoS.If you are running bn.js < 5.2.3, you are vulnerable. The dependency tree of the average Node project is deep and dark; you might not even know you are using it. elliptic, web3.js, and asn1.js all rely on it.
Remediation:
npm install bn.js@latest.npm audit or yarn audit. You will likely need to regenerate lockfiles to force nested dependencies to grab the patched version.0 to a bitwise mask function.// The monkey-patch band-aid
const BN = require('bn.js');
const originalMaskn = BN.prototype.maskn;
BN.prototype.maskn = function(bits) {
if (bits === 0) return new BN(0);
return originalMaskn.apply(this, arguments);
};This is a classic example of why input validation is hard—sometimes the invalid input (0) looks perfectly harmless.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P| Product | Affected Versions | Fixed Version |
|---|---|---|
bn.js indutny | < 5.2.3 | 5.2.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-835 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 | 6.9 (Medium) |
| Impact | Denial of Service (Infinite Loop) |
| Affected Component | bn.js `maskn`/`imaskn` methods |
| Exploit Status | PoC Available |
Loop with Unreachable Exit Condition ('Infinite Loop')
An access control vulnerability in the SiYuan personal knowledge management platform before version v3.7.4 exposes notebook encryption parameters to unauthenticated remote attackers. When the platform is configured in Publish Mode, specific API endpoints fail to enforce authorization checks. This access failure leaks key-derivation materials, password verifiers, and wrapped database keys to anonymous network clients.
A security vulnerability in the SiYuan local-first personal knowledge management system allows unauthenticated remote attackers to bypass logical boundary controls in publish (read-only) mode. By interacting with endpoints that lack proper publish-access validation, an attacker can disclose the application's internal database schemas and harvest block IDs across both public and private notebooks. This metadata leakage compromises the confidentiality of restricted documents and provides foundational information for targeted extraction.
An information disclosure vulnerability exists in the SiYuan personal knowledge management system versions prior to v3.7.4. The application fails to enforce publish-access filters on block attribute retrieval endpoints. Consequently, unauthenticated remote attackers can bypass document-level protection rules (such as password protection or disabled-publish flags) to retrieve sensitive block-level attributes, including aliases, memos, block names, and custom metadata fields, by querying the API using guessed or known block IDs.
An authentication bypass vulnerability (classified as CWE-288) exists in the publish-mode component of SiYuan, a Go-based note-taking application. This security flaw allows unauthenticated remote attackers to bypass password-protected note boundaries by leveraging auxiliary block endpoints that fail to enforce document access checks. Attackers can exploit this issue by first harvesting document metadata via a public search endpoint and subsequently fetching full rendered document contents using vulnerable block endpoints. This technical analysis explores the root cause, exploitation methodology, and remediation path.
An uncontrolled recursion vulnerability (CWE-674) in the toml-node NPM package (published as toml) prior to version 4.2.0 allows unauthenticated remote attackers to trigger process-wide Denial of Service (DoS) crashes. By submitting TOML payloads with deep bracket or brace nesting, attackers exhaust the V8 runtime stack limit.
CVE-2026-73295 is a DOM-based Cross-Site Scripting (XSS) vulnerability affecting Material for MkDocs versions 7.2.0 through 9.7.6. When the optional 'search.suggest' feature is enabled, the client-side 'mountSearchSuggest' function processes user-controlled inputs from the URL 'q' parameter and writes them directly to the DOM using an unsafe innerHTML sink without sanitization.