Listen to this Post
The `re2` npm package provides Node.js bindings for Google’s RE2 regular expression engine. Prior to version 1.25.2, the library contained a critical flaw in its handling of the user-settable `lastIndex` property on global or sticky regex instances.
The root cause lies in a type confusion between UTF-8 byte length and UTF-16 code-unit count. When a subject string is prepared, the `StrVal` wrapper stores the subject’s UTF-8 byte length in `StrVal::length` (via `utf8Length` at lib/addon.cc:200). The `setIndex` method then validates the provided `lastIndex` against this byte length. However, the subsequent buffer walk uses `lastIndex` as a UTF-16 code-unit offset, not a byte offset, and performs no bounds checking.
For any non-ASCII subject, the UTF-8 byte length is always greater than the true UTF-16 character count. An attacker can set `lastIndex` to a value between these two lengths, passing the validation check while pointing past the end of the allocated buffer. The `getUtf16PositionByCounter` function then reads `data
` and advances by the UTF-8 character size without verifying `from` against the buffer size. This results in an out-of-bounds heap read. With a sufficiently large subject, the read operation marches into unmapped memory, causing the process to terminate with SIGABRT or SIGSEGV. This is an uncatchable native crash — `try/catch` blocks in JavaScript cannot prevent termination — leading to a denial of service for any Node.js worker or process that executes the match. In some limited cases, adjacent heap bytes may be copied into the returned value, resulting in a bounded, best-effort information disclosure. The vulnerability is reachable through <code>exec</code>, <code>test</code>, <code>String.prototype.match</code>, <code>replace</code>, and `split` on any global or sticky RE2 instance. It is triggered solely on non-ASCII subjects; ASCII subjects are unaffected because their byte length equals their UTF-16 length. This issue is distinct from the previously fixed GHSA-8hcv-x26h-mcgp (global `replace()` output-amplification abort) and remains present in version 1.25.1. The fix, implemented in version 1.25.2, validates `lastIndex` against the subject's true UTF-16 length and adds bounds checking to the buffer walk. Users are strongly advised to upgrade immediately or, as a workaround, clamp `lastIndex` to `str.length` for non-ASCII subjects. <h2 style="color: blue;">DailyCVE Form:</h2> Platform: Node.js re2 Version: < 1.25.2 Vulnerability: OOB heap read Severity: Moderate date: 2026-07-30 <h2 style="color: blue;">Prediction: 2026-07-07</h2> <h2 style="color: blue;">What Undercode Say:</h2> <h2 style="color: blue;">Analytics:</h2> <ul> <li>Affected methods: <code>exec</code>, <code>test</code>, <code>match</code>, <code>replace</code>, `split` - Trigger condition: `lastIndex` between UTF-8 byte length and UTF-16 length on non-ASCII subject</li> <li>Crash type: SIGABRT/SIGSEGV (uncatchable)</li> <li>Fixed in: `[email protected]` </li> </ul> <h2 style="color: blue;">Bash commands and codes:</h2> [bash] Check installed version npm list re2 Upgrade to patched version npm install [email protected] Verify upgrade npm list re2
Proof of concept (ASAN):
const RE2 = require('re2');
const re = new RE2('a', 'y');
re.lastIndex = 3; // 3 <= byteLen(4) passes guard; only 2 real chars
re.exec('éé'); // U+00E9 = 2 bytes each → OOB read
Real-world crash (large subject):
const RE2 = require('re2');
const s = '中'.repeat(40000000); // UTF-16 40M, UTF-8 120M
const re = new RE2('a', 'y');
re.lastIndex = Buffer.byteLength(s) - 1; // passes byte-length guard
re.exec(s); // SIGSEGV (exit 139)
Exploit:
An attacker can trigger the vulnerability by:
- Identifying a Node.js application that uses a global or sticky `RE2` instance on user-supplied non-ASCII input.
- Setting `lastIndex` to a value greater than the string’s UTF-16 length but less than or equal to its UTF-8 byte length.
- Invoking any of the affected methods (
exec,test,match,replace,split). - Causing the Node.js process to crash with an uncatchable native fault.
The exploit requires no authentication and can be performed remotely wherever the application accepts attacker-influenced `lastIndex` values (e.g., resuming a scan or pagination from a client-supplied offset). The crash terminates the entire worker/process, enabling denial-of-service attacks.
Protection:
- Immediate: Upgrade to `[email protected]` or later.
- Workaround (if unable to upgrade): Never assign `lastIndex` from untrusted input. For non-ASCII subjects, clamp `lastIndex` to the subject’s true string length (
str.length) before calling any affected method. - Defense in depth: Validate and sanitize all user-supplied offsets before using them with `RE2` instances. Consider using ASCII-only subjects where possible, as the vulnerability does not affect them.
Impact:
- Denial of Service (Primary): An uncatchable native crash that terminates the Node.js process or worker. Remotely exploitable without authentication.
- Information Disclosure (Secondary, Best-Effort): Out-of-bounds heap bytes may be copied into returned values (e.g., leading segment of a `replace` result). This is bounded and unreliable due to
calloc-allocated zero-filled buffers and the variable over-read distance.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

