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-24118
9.8

CVE-2026-24118: Remote Code Execution via Sandbox Escape in vm2

Alon Barad
Alon Barad
Software Engineer

May 4, 2026·7 min read·13 visits

Weaponized

Executive Summary (TL;DR)

vm2 prior to 3.11.0 fails to properly isolate JavaScript realms, allowing attackers to leverage context-confusion, reflective API traps, and V8's ArraySpeciesCreate algorithm to leak the host Function constructor and achieve remote code execution.

A critical vulnerability in the vm2 Node.js sandbox library allows attackers to bypass isolation mechanisms and execute arbitrary code on the host system. The flaw stems from insufficient sanitization of cross-realm object interactions and V8 internal algorithm behaviors.

Vulnerability Overview

The vm2 library is a Node.js module designed to execute untrusted code in a highly restricted sandbox environment. It implements an isolation membrane using JavaScript Proxies to intercept and sanitize cross-realm object interactions between the host environment and the sandbox. This architecture intends to prevent sandbox code from accessing core Node.js APIs or modifying the host context.

Prior to version 3.11.0, this proxy-based membrane contains critical implementation flaws related to edge cases in JavaScript property accessors and native V8 internal algorithms. The vulnerability, tracked as CVE-2026-24118, allows an attacker executing untrusted code within the sandbox to leak direct references to host-realm objects. This constitutes a complete sandbox escape and fundamentally breaks the security model of the library.

Successful exploitation results in arbitrary code execution with the privileges of the Node.js process running the vm2 instance. By leaking the host's Function constructor, an attacker completely escapes all sandbox constraints. They can subsequently require native modules such as child_process to execute arbitrary system commands, compromising the entire host infrastructure.

Root Cause Analysis

The vulnerability relies on a combination of three distinct primitives to break the proxy membrane. The first is a context-confusion primitive. Attackers utilize a host-context function's apply method to invoke __lookupGetter__ on host-realm objects. The vm2 bridge misidentifies the call origin, returning an un-proxied getter for sensitive properties like __proto__. This initial leak provides the foundation for traversing the host's prototype chain.

The second primitive involves bypassing vm2's defensive traps against reflective metadata access. While the library blocks direct access to the .constructor property, attackers use reflective methods such as Object.getOwnPropertyDescriptor and Object.entries. These methods extract the constructor values directly from property descriptors, effectively bypassing standard property-access interception and exposing the host objects to the sandbox context.

The final primitive exploits the behavior of the ArraySpeciesCreate algorithm internal to the V8 engine. Native mutating methods like Array.prototype.map or Array.prototype.slice read this.constructor[Symbol.species] directly on the underlying raw object, circumventing proxy handlers. V8 executes the mapping and stores raw host values directly into the target array via internal C++ methods, bypassing the vm2 bridge sanitization entirely.

Code Analysis and Patch Review

The original vulnerability stems from the fundamental difficulty of intercepting all property accesses in a complex language specification like ECMAScript. The vm2 proxy handler failed to account for native C++ V8 methods that extract values without triggering the standard get handlers. The fix required a multi-layered defense-in-depth approach spanning multiple commits.

Commit f9b700b1c7d9ef2df416666cb24e0b659140cc74 addresses the ArraySpeciesCreate bypass by implementing a neutralize-and-restore pattern. Before every sandbox-to-host function invocation (such as apply or construct traps), the bridge iterates through the context and top-level arguments. For every host array found, it installs constructor = undefined as an own data property. This forces the internal V8 algorithm to fall back to the safe default Array constructor. The original descriptor is subsequently restored in a finally block to preserve legitimate application functionality.

// Conceptual representation of the neutralize-and-restore pattern
try {
  if (Array.isArray(hostObj)) {
    // Neutralize constructor to prevent Symbol.species abuse
    Object.defineProperty(hostObj, 'constructor', { 
      value: undefined, configurable: true 
    });
  }
  // Execute cross-realm call
  return Reflect.apply(target, thisArgument, argumentsList);
} finally {
  // Restore original descriptor
  if (Array.isArray(hostObj)) {
    delete hostObj.constructor;
  }
}

Commit 2b5f3e3a060d9088f5e1cdd585d683d491f990a3 implements descriptor sanitization to neutralize the reflective bypasses. The getOwnPropertyDescriptor trap was modified to enforce strict, recursive sanitization. Any descriptors returned to the sandbox now have their value, get, and set properties thoroughly wrapped by the proxy bridge, preventing the leakage of host-context primitives through metadata inspection.

Exploitation Methodology

Exploitation begins with the attacker harvesting a host object constructor. This is achieved by chaining Buffer.apply and __lookupGetter__. The operation tricks the vm2 context verification logic into identifying the operation as a legitimate host-side action. The attacker traverses the prototype chain until the host Function or Object constructor is isolated.

Next, the attacker initiates the host array acquisition and mutation phase. Using the leaked host constructor, a host-realm array is minted. The attacker then defines a malicious sandbox constructor specifically tailored to self-return via Symbol.species. This malicious constructor is assigned to the host array's .constructor property, preparing the data structure for the V8 internal bypass.

// Step 1: Harvest Host Object
const g = ({}).__lookupGetter__;
const a = Buffer.apply;
const p = a.apply(g, [Buffer, ['__proto__']]);
const op = p.call(p.call(p.call(p.call(Buffer.of()))));
const ho = op.constructor; 
 
// Step 2 & 3: Array Minting and Mutation (Conceptual)
// The attacker triggers r.map() causing V8 to read the malicious species
// and write raw host data back to the array, bypassing the proxy.

The trigger execution occurs when the attacker invokes a mutating function like map() on the prepared array. The V8 engine executes the internal mapping algorithm, which ignores the vm2 proxy handlers and directly writes raw host values into the attacker-controlled structure. The attacker then extracts the host Function constructor from this mutated array and spawns a child_process instance to execute system commands.

Impact Assessment

The security impact of CVE-2026-24118 is total system compromise. Successful exploitation transitions the attacker from a restricted JavaScript runtime boundary to the host operating system. The attacker achieves arbitrary command execution under the security context of the user running the Node.js application. This negates the primary purpose of the vm2 library entirely.

The vulnerability carries a CVSS v3.1 base score of 9.8 (CRITICAL). The attack vector is Network (AV:N), as an attacker can typically submit the malicious payload via any network interface that accepts user scripts. It requires no privileges (PR:N) and no user interaction (UI:N). The impact on confidentiality, integrity, and availability is universally High (C:H/I:H/A:H).

Organizations using vm2 for multi-tenant isolation, automated code grading, Serverless functions, or plugin evaluation face severe operational risks. Exploitation exposes all internal environment variables, database credentials, and file system contents accessible to the Node.js process. The host server can also be pivoted to attack adjacent systems within the internal network infrastructure.

Remediation and End-of-Life Notice

The immediate remediation step is updating the vm2 dependency to version 3.11.0. This patched version incorporates the necessary proxy handler hardening, the neutralize-and-restore pattern for array operations, and comprehensive descriptor sanitization. No alternative workarounds or configuration changes exist to secure vulnerable versions of the library.

Despite the release of version 3.11.0, the vm2 project has been officially deprecated by its maintainers. The maintainers cited the inherent, unresolvable architectural difficulty of securing a proxy-based JavaScript membrane against the complex and evolving V8 engine. The continuous discovery of similar sandbox escapes demonstrates that pure JavaScript virtualization within a shared V8 isolate is fundamentally unsound for security boundaries.

Security teams and software developers must implement a migration strategy to replace vm2. Recommended alternatives prioritize hardware-level or memory-safe boundaries. Options include running untrusted code in separate processes with restrictive operating system privileges, utilizing containerization technologies like Docker or gVisor, or adopting WebAssembly (Wasm) runtimes designed specifically for secure execution.

Execution Flow

The following sequence illustrates the exploitation chain from initial sandbox entry to the execution of host-level system commands. The flow demonstrates how the proxy membrane is bypassed via internal engine mechanisms.

The diagram highlights that the core failure is not a single missing check, but rather the interaction between the host environment, the proxy wrapper, and the lower-level C++ runtime execution model.

Official Patches

patriksimekRelease 3.11.0
GitHub Advisory DatabaseOfficial Advisory GHSA-grj5-jjm8-h35p

Fix Analysis (2)

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

Affected Systems

Any Node.js application utilizing the vm2 library prior to version 3.11.0 for untrusted code execution.

Affected Versions Detail

Product
Affected Versions
Fixed Version
vm2
patriksimek
< 3.11.03.11.0
AttributeDetail
CWE IDCWE-693
Attack VectorNetwork
CVSS Base Score9.8 CRITICAL
EPSS ScoreN/A
ImpactRemote Code Execution (Sandbox Escape)
Exploit StatusWeaponized PoC Available
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-693
Protection Mechanism Failure

Protection Mechanism Failure / Sandbox Escape

Vulnerability Timeline

Fix for Array species self-return escape committed
2026-04-23
Coverage for descriptor-based bypasses committed
2026-04-27
Vulnerability publicly disclosed, CVE assigned, and vm2 version 3.11.0 released
2026-05-04

References & Sources

  • [1]GHSA-grj5-jjm8-h35p Advisory
  • [2]Patch Commit: f9b700b1c7d9ef2df416666cb24e0b659140cc74
  • [3]Patch Commit: 2b5f3e3a060d9088f5e1cdd585d683d491f990a3
  • [4]Release v3.11.0
  • [5]CVE-2026-24118 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.