CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-92944

CVE-2026-92944: Sandbox Escape in vm2 via Stale V8 PromiseThenLookupChain Protector

Alon Barad
Alon Barad
Software Engineer

Oct 2, 2026·8 min read·3 visits

Executive Summary (TL;DR)

An optimization bug in the V8 engine on Node.js 26 fails to invalidate a promise lookup protector when properties are assigned sequentially, allowing attackers to bypass vm2's promise wrappers and achieve full sandbox escape and remote code execution.

A critical engine-level reachability failure in Node.js 26 running V8 14.6 allows attackers to escape the vm2 sandbox environment. When consecutive prototype properties are modified using sequential assignments, a V8 optimization bug fails to invalidate the PromiseThenLookupChain protector. By calling Promise.prototype.finally, the attacker bypasses the vm2 wrappers, hijacks the promise reaction using a custom constructor, triggers a calibrated stack overflow to capture a host-realm RangeError, and executes arbitrary shell commands on the host.

Vulnerability Overview

CVE-2026-92944 (also tracked via GitHub Security Advisory GHSA-27g9-p43v-cw3v) is a critical sandbox escape vulnerability affecting the vm2 Node.js library in versions 3.10.2 through 3.11.6. The flaw represents an engine-level reachability failure arising from a discrepancy between optimization assumptions in the V8 JavaScript engine and the prototype overriding patterns used by the sandbox. The vulnerability allows an attacker to execute arbitrary code outside the restricted environment on Node.js 26 runtimes running V8 14.6.

To prevent untrusted code from hijacking native objects, the vm2 sandbox implements wrapper functions over prototype methods. However, the introduction of a performance optimization in V8 14.6—named proto_assign_seq_opt—undermines this mechanism. Under specific conditions, sequential property modifications on prototypes fail to invalidate the engine's internal optimization state. This leaves a critical internal protector cell in a stale valid state, opening a bypass channel.

An attacker can exploit this stale state to skip the sandboxed wrapper for Promise.prototype.then during native promise operations. By chaining this bypass with a custom promise constructor that intercepts a native promise reaction, the attacker can trigger a stack overflow. This induces the host realm to construct and throw an error, which the attacker intercepts to extract reference handles to the host's global context, resulting in a complete sandbox escape.

Root Cause Analysis

The root cause lies in V8's optimization of Consecutive Prototype Property Assignments (proto_assign_seq_opt). When a series of properties are modified on an object's prototype, V8 attempts to consolidate these modifications. It groups consecutive assignments into a single bulk pathway named SetPrototypeProperties to reduce overhead and avoid repeatedly regenerating prototype maps.

However, the SetPrototypeProperties implementation in V8 14.6 contains a validation flaw. When modifying an existing data property on the prototype, the execution path flows through the SetDataProperty function. This branch fails to call the internal it.UpdateProtector() method. This omitted update is critical because it is the mechanism responsible for notifying the engine's optimizer that a protected prototype property has been overwritten.

V8 relies on a specific cell known as the PromiseThenLookupChain protector. When this protector is active, the engine assumes that no user-defined modifications have been made to Promise.prototype.then. This assumption enables internal functions, such as Promise.prototype.finally(), to use a fast path (InvokeThen) that directly executes the native implementation of then, bypassing regular prototype chain resolution.

Because the protector cell is never updated or invalidated during the sandbox initialization sequence, the V8 engine is left in an inconsistent state. The Promise.prototype.then property successfully points to the sandbox's custom wrapper, yet the optimization cell remains marked as valid. Consequently, when native code executes internal promise operations, V8 bypasses the property lookup, completely skipping the wrapper protection mechanism.

Code Analysis

The original, vulnerable implementation of the sandbox setup in lib/setup-sandbox.js initialized the wrapper functions using standard JavaScript property assignments.

// Vulnerable assignment pattern
globalPromise.prototype.then = function then(onFulfilled, onRejected) { ... };
globalPromise.prototype.catch = function _catch(onRejected) { ... };

Under normal circumstances, these consecutive assignments would trigger prototype transitions that invalidate any active lookup protectors. However, due to the proto_assign_seq_opt optimization bug in V8 14.6, these sequential assignments are merged. The modifications write the new function references to the prototype properties, but the engine fails to trigger the invalidation logic for PromiseThenLookupChain.

The patch resolved this vulnerability using a dual-layered approach. First, the assignment method was changed from standard assignment to explicit property definition via Reflect.defineProperty:

// Patched assignment pattern
localReflectDefineProperty(globalPromise.prototype, 'then', {
	__proto__: null,
	value: wrappedPromiseThen,
	writable: true,
	enumerable: false,
	configurable: true,
});

Using [[DefineOwnProperty]] semantics instead of standard assignments prevents V8 from grouping these changes under the optimized SetPrototypeProperties path. This forces V8 to execute the standard property definition routine, which correctly invalidates the PromiseThenLookupChain protector.

Second, the patch wraps the finally method itself to provide structural defense-in-depth:

const wrappedPromiseFinally = typeof globalPromiseFinally === 'function'
	? function _finally(onFinally) {
		resetPromiseSpecies(this);
		return apply(globalPromiseFinally, this, [onFinally]);
	}
	: undefined;

This second layer ensures that even if the optimizer protector were to fail in some other manner, calling finally() directly on a promise will execute resetPromiseSpecies(). This completely neutralizes custom species constructors prior to invoking the native finally logic.

Exploitation Methodology

To execute a successful sandbox escape, an attacker must carry out a multi-stage process that leverages the stale optimization cell to bypass wrappers, intercept a promise reaction, and force a cross-realm error. The execution must take place on Node.js 26 running V8 14.6. No specialized privileges or prior authentication are required.

First, the attacker creates a custom class (Evil) that acts as a promise species constructor. This class intercepts the executor function passed during a promise reaction. Within this constructor, the attacker intercepts the native promise rejection callback:

class Evil {
    constructor(executor) {
        executor(v => {}, hostErr => {
            // Capture the raw host-realm RangeError
            // err.constructor.constructor is the host Function constructor
            const hostProcess = hostErr.constructor.constructor('return process')();
            hostProcess.mainModule.require('child_process').execSync('id');
        });
    }
}

Next, the attacker creates an engine-realm promise, typically by executing a basic asynchronous expression. The attacker then defines a custom constructor on this promise instance, mapping its Symbol.species property to the Evil class. Because the object is a standard promise, V8 expects it to follow the fast lookup route.

const p = (async () => 1)();
Object.defineProperty(p, 'constructor', { 
    value: { [Symbol.species]: Evil }, 
    configurable: true 
});

Finally, the attacker calls p.finally(). Because the PromiseThenLookupChain protector cell remains valid, the V8 engine bypasses the vm2 wrapper for then and immediately invokes the native then method. This passes the promise reaction to the host-realm engine without stripping the species constructor. By recursively deep-nesting the execution inside the constructor, the attacker triggers a stack overflow. The resulting RangeError is instantiated in the host realm but caught by the sandboxed Evil constructor's rejection handler, allowing access to the host global scope.

Impact Assessment

The security impact of CVE-2026-92944 is critical, carrying a CVSS v3.1 base score of 9.8 and a CVSS v4.0 base score of 9.3. Successful exploitation permits full, unauthenticated remote code execution (RCE) on the host operating system running the sandbox environment. The attacker gains the security context of the parent Node.js process, which can lead to complete system compromise.

Because vm2 is frequently utilized to run untrusted, user-submitted code in multi-tenant SaaS environments, serverless computing platforms, and automated test runners, the fallout of a sandbox escape in these contexts is severe. An attacker can read sensitive environmental variables, access internal databases, modify local files, or pivot to make unauthorized network calls to other services in the internal infrastructure.

Although the statistical EPSS score is 0.00841 (reflecting a low likelihood of wide-scale opportunistic scans, as it requires a specific V8 version), targeted exploitation is highly reliable. In environments hosting untrusted JavaScript executables on Node.js 26, the exploit executes deterministically with zero user interaction.

Remediation and Mitigation

The primary remediation strategy is upgrading the vm2 package to version 3.11.7 or higher. This version corrects the prototype assignment calls to ensure correct optimizer invalidation and introduces explicit wrappers around Promise.prototype.finally. These modifications completely close the escape vector on vulnerable V8 runtimes.

However, users must be aware that the vm2 library was officially deprecated in 2023. Pure JavaScript-based sandboxes that rely on prototype isolation are fundamentally fragile due to the constant introduction of performance-oriented optimizations in modern JavaScript engines. It is strongly recommended to migrate away from vm2 to process-level isolation frameworks, such as isolated-vm (which executes code in separate V8 isolates), or container-based runtimes.

If an immediate upgrade of vm2 is not possible, a runtime workaround exists. Administrators can launch the Node.js application with a specific command-line argument that disables the faulty V8 optimization:

node --no-proto-assign-seq-opt your-app.js

By supplying the --no-proto-assign-seq-opt flag, the engine is forced to evaluate prototype assignments using the classic, unoptimized pathways. This guarantees that any changes to prototype methods immediately and correctly invalidate the PromiseThenLookupChain protector, maintaining the security boundary of the sandbox.

Official Patches

patriksimekGitHub Security Advisory and Patched Version Announcement

Fix Analysis (3)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.84%
Top 44% most exploited

Affected Systems

vm2 running on Node.js 26 (V8 14.6)

Affected Versions Detail

Product
Affected Versions
Fixed Version
vm2
patriksimek
>= 3.10.2, <= 3.11.63.11.7
AttributeDetail
CWE IDCWE-693
Attack VectorNetwork
CVSS Score9.8
EPSS Score0.00841
ImpactArbitrary Code Execution / Sandbox Escape
Exploit StatusPoC
KEV StatusNo

MITRE ATT&CK Mapping

T1562Impair Defenses
Defense Evasion
T1059Command and Scripting Interpreter
Execution
CWE-693
Protection Mechanism Failure

The product does not use or incorrectly implements a protection mechanism, which leaves it vulnerable to attacks.

Known Exploits & Detection

GitHubAdvisory containing the concept payload and details of the escape

Vulnerability Timeline

Security patch and regression tests committed
2026-08-22
CVE-2026-92944 assigned and vm2 3.11.7 released
2026-09-17
NVD processed and published CVSS metrics
2026-09-19

References & Sources

  • [1]GitHub Security Advisory GHSA-27g9-p43v-cw3v
  • [2]VulnCheck Advisory for CVE-2026-92944
  • [3]CVE-2026-92944 Record

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•3 minutes ago•CVE-2026-92945
4.2

CVE-2026-92945: Sandbox Escape and Module Allowlist Bypass via Path Prefix Matching in vm2

A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.

Amit Schendel
Amit Schendel
0 views•6 min read
•about 1 hour ago•CVE-2026-92941
10.0

CVE-2026-92941: Sandbox Escape and Process-Wide TLS Trust Store Manipulation in vm2

CVE-2026-92941 is a critical sandbox-escape and trust-manipulation vulnerability in the vm2 library (versions 3.11.3 to 3.11.6). This security flaw allows untrusted code executing within a NodeVM sandbox environment to compromise the global TLS trust store of the host Node.js process. By leveraging a design flaw where the host's native 'tls.setDefaultCACertificates' can be executed via a proxy wrapper, combined with a bridge unwrapping bypass in the 'url' module, an attacker can modify the process-wide default root Certificate Authorities. Consequently, all subsequent outbound TLS/HTTPS clients running on the host thread are forced to trust attacker-signed certificates, facilitating transparent Man-in-the-Middle (MitM) attacks. The vulnerability was resolved in version 3.11.7 of vm2 by introducing built-in member-level sanitization before applying read-only proxy wrappers.

Amit Schendel
Amit Schendel
1 views•10 min read
•about 3 hours ago•CVE-2026-92939
9.9

CVE-2026-92939: Critical Sandbox Escape via Host Crypto setEngine Native Code Execution in vm2

A critical sandbox escape vulnerability in the vm2 library allows sandboxed JavaScript code to bypass containment and execute arbitrary native code on the host process. This occurs when the host's builtin crypto module is exposed to the NodeVM environment. Although vm2 implements a read-only proxy layer to restrict direct modifications to host properties, it does not prevent invocation of host-level functions. By calling the crypto.setEngine API with a path to a malicious native library on disk, an attacker can trigger OpenSSL's dynamic module loader. The host's operating system loader immediately runs the dynamic library's initializers/constructors before verifying engine compatibility, leading to remote code execution in the context of the host process.

Amit Schendel
Amit Schendel
4 views•5 min read
•about 4 hours ago•CVE-2026-92938
9.9

CVE-2026-92938: Remote Code Execution in vm2 via node:sqlite DatabaseSync Sandbox Escape

CVE-2026-92938 is a critical sandbox escape vulnerability in the vm2 library (versions 3.11.3 through 3.11.6) that allows arbitrary native code execution on the host when the node:sqlite built-in module is loaded inside a sandboxed NodeVM environment.

Amit Schendel
Amit Schendel
6 views•8 min read
•about 5 hours ago•CVE-2026-92937
10.0

CVE-2026-92937: Sandbox Escape leading to Remote Code Execution via Promise Indirection in vm2

CVE-2026-92937 is a critical sandbox escape vulnerability in the `vm2` Node.js library. Due to a logical failure in checking direct invocation targets inside the Proxy bridge, an attacker can register Promise callbacks using `Function.prototype.call` or `Function.prototype.apply` indirection. This bypasses the error sanitization wrappers, delivering raw host error objects directly to sandboxed callbacks and allowing the attacker to escape the sandbox and execute arbitrary shell commands on the host.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 6 hours ago•CVE-2026-92935
9.5

CVE-2026-92935: Remote Code Execution via Array-Shaped Require Config in vm2 NodeVM Sandbox

CVE-2026-92935 is a critical sandbox escape and remote code execution vulnerability in the vm2 library. By supplying an array or exotic object to the require property of NodeVM while nesting is enabled, attackers can bypass security checks, load the host vm2 module, and run arbitrary shell commands on the hosting server.

Alon Barad
Alon Barad
5 views•7 min read