Introduction
A sandbox breakout in the vm2 Node.js package allows any attacker who can submit code to a vm2 sandbox to escape it entirely and execute arbitrary commands on the underlying host system. With over 1.3 million weekly downloads on npm and 898 dependent packages, vm2 is deeply embedded in the Node.js ecosystem, making CVE-2026-24118 a critical concern for any organization running untrusted code through this library.
vm2 is an open source virtual machine and sandbox environment for Node.js, designed to let developers safely execute untrusted JavaScript in an isolated context. It is widely used in low code platforms, serverless function runners, template engines, and developer tooling where user supplied code needs to be evaluated without granting access to the host operating system. Its 4,000 GitHub stars and broad adoption make it one of the most prominent sandboxing solutions in the Node.js world.
Technical Information
Root Cause: Context Bridging and __lookupGetter__
The core of this vulnerability lies in how vm2 bridges objects between the sandbox (guest) context and the host context. The __lookupGetter__ method has a unique behavior in vm2: it switches between the host and sandbox versions when passed across context boundaries. This bridging inconsistency is the entry point for the entire exploit chain.
An attacker operating within the sandbox can exploit this by using Buffer.apply to access the host's apply method. From there, the attacker calls the host version of __lookupGetter__ with Buffer and __proto__ as arguments. This returns the prototype lookup method from the host context rather than the sandbox context.
Exploit Flow
The attack proceeds through the following stages:
-
Host method access: The attacker uses
Buffer.applyfrom within the sandbox to obtain a reference to the host'sapplyfunction. -
Prototype chain traversal: By invoking
a.apply(g, [Buffer, ['__proto__']])(whereais the host__lookupGetter__andgis a crafted object), the attacker walks the prototype chain until reaching the host'sObject.prototype. -
Constructor acquisition: From
Object.prototype, the attacker accesses the.constructorproperty to obtain the hostObjectconstructor, and then.constructoragain to reach the hostFunctionconstructor. -
Arbitrary code execution: With the host
Functionconstructor in hand, the attacker can create and execute arbitrary functions in the host context. The publicly documented exploit uses this to callrequire('child_process')and execute system commands.
Bypass of Prior Defenses
An earlier fix attempted to block this class of attack by checking whether the descriptor value name was 'Function' in the bridge logic. This was insufficient. Attackers bypassed it by using Object.getOwnPropertyDescriptor to obtain the constructor property directly, sidestepping the name check entirely.
The fix shipped in version 3.11.0 takes a more comprehensive approach with a two layer defense. It blocks Array species self-return sandbox escapes by ensuring that when the bridge invokes a host function, no host realm array used as context or as an argument can have an attacker controlled constructor property visible to internal V8 processes. This addresses the root cause rather than attempting to filter specific property names.
CVSS Breakdown
| Metric | Value |
|---|---|
| Base Score | 9.8 Critical |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | None |
| Confidentiality Impact | High |
| Integrity Impact | High |
| Availability Impact | High |
The vulnerability is classified under CWE-94 (Improper Control of Generation of Code, or Code Injection) and CWE-693 (Protection Mechanism Failure).
Detection Methods
As of this writing, no formal Sigma, YARA, Suricata, or Snort detection signatures have been published specifically for CVE-2026-24118. Because this vulnerability is an application layer sandbox breakout rather than a network protocol flaw, detection focuses on source code analysis, dependency auditing, and behavioral heuristics rather than traditional network IDS rules.
Version Based Detection (Software Composition Analysis)
The most immediate and reliable detection method is checking whether your environment uses a vulnerable version of the vm2 npm package. Any version prior to 3.11.0 is affected. Security teams should use SCA tools (Snyk, npm audit, Dependabot, Socket, etc.) to flag installations of vm2 below this version. Organizations should also monitor their package-lock.json, yarn.lock, and pnpm-lock.yaml files for the presence of vm2 at versions below 3.11.0. GitHub's Dependabot and npm's built in audit system should surface this advisory automatically via GHSA-grj5-jjm8-h35p.
Heuristic Detection of Exploit Patterns in Sandbox Code
The advisory and the project's ATTACKS.md document provide detailed detection heuristics for identifying exploitation attempts in code being submitted to a vm2 sandbox. These fall into several recognizable pattern families.
The __lookupGetter__ + Buffer.apply Prototype Walk
The exploit's entry point uses a distinctive chain to walk from the sandbox realm to the host realm. Detection should flag any sandbox code that accesses ({}).__lookupGetter__ in combination with Buffer.apply. Any code accessing __lookupGetter__ or __lookupSetter__ on prototype objects, or using Buffer.apply with unexpected arguments, should be treated as highly suspicious.
Array Species Self Return Pattern (Category 18)
The core exploitation primitive is the Array species self return. The ATTACKS.md Category 18 detection rules call out these specific indicators:
- A pattern of
r.constructor = xwherexhas aSymbol.speciesproperty - Self referential species declarations like
x[Symbol.species] = x - Functions used as constructors that return an existing object (e.g.,
function x() { return r; }serving as a species constructor) - Use of
ho.assign(r, {constructor: ...}), theObject.assignvariant that bypasses the proxysettrap by writing directly on the host object - Calls to
.map(),.filter(),.slice(),.concat(), or.splice()on arrays that have had theirconstructorproperty tampered with, as these all invoke V8's internalArraySpeciesCreatealgorithm ho.entries({})orObject.entries()used to create host realm arrays destined for species manipulation
Constructor Chain Traversal (Category 1)
Since the ultimate goal is to reach the host Function constructor, monitoring should flag:
- Any access to
.constructor.constructoron any object - Patterns resembling
Function("return process")()or equivalent dynamic code evaluation strings - Access to
.constructorinsidecatchblocks where the error may carry host prototype references
Property Descriptor Value Extraction (Category 15)
Earlier exploit variants used Object.getOwnPropertyDescriptor and Object.entries() to extract the host Function constructor from descriptor values. Detection should flag:
Object.getOwnPropertyDescriptor(hostPrototype, 'constructor')extracting the descriptor for the constructor property- Chained
getOwnPropertyDescriptorscalls building deep nesting Object.entries(descriptor)orObject.values(descriptor)used to unpack raw values from descriptors
Runtime Behavioral Monitoring
For environments executing untrusted code in vm2 sandboxes, runtime monitoring can detect exploitation artifacts:
- Unexpected child process spawning: If a vm2 sandbox escape succeeds, the attacker typically calls
require('child_process').execSync(...). Process monitoring (via audit logs, seccomp-bpf, or container level syscall filtering) for child process creation by the Node.js process running the sandbox is a strong post exploitation indicator. - Host
processobject access: EDR or APM tools that instrument Node.js process access can flag unexpected reads ofprocess.mainModuleor calls toprocess.mainModule.require()from within sandbox execution contexts. VMErrorexceptions: The fix in v3.11.0 throwsVMErrorwith messages such as "Unsafe array constructor cannot be neutralized" or "Unsafe non-extensible array passed across bridge" when an active exploitation attempt is detected. Logging and alerting on these specific error messages in production provides a strong signal that an attack was attempted and blocked by the patched version.
Affected Systems and Versions
All versions of the vm2 npm package prior to version 3.11.0 are affected. This includes versions up to and including 3.10.4. The vulnerability was patched in version 3.11.0, which was published on May 1, 2026. The latest available release at the time of writing is version 3.11.2.
Any application or service that uses vm2 to execute user supplied or untrusted JavaScript code is potentially vulnerable. This includes low code platforms, serverless function runners, template engines, and any custom tooling that relies on vm2 for code isolation.
Vendor Security History
The vm2 project has a notable history of sandbox breakout vulnerabilities. The release of version 3.11.0 was a coordinated security release that closed 13 separate advisories, including multiple Remote Code Execution primitives and Denial of Service vectors. This pattern reflects the inherent difficulty of building an airtight in process sandbox using JavaScript's dynamic features.
The maintainers are transparent about these limitations. They explicitly warn users that new bypasses will likely be discovered in the future and that vm2 should not be the only line of defense for untrusted code execution. For completely untrusted code, the vendor recommends stronger isolation alternatives:
| Solution | Approach | Performance | Trade offs |
|---|---|---|---|
| isolated vm | Separate V8 isolates | Fast | In maintenance mode; requires manual V8 updates |
| Separate process or Worker | child process or Worker threads | Medium | Higher IPC overhead; data must be serialized |
| Containers or VMs | Docker or gVisor or Firecracker | Slow | Startup overhead; resource heavy |
| Managed services | Cloud based code execution | Variable | Network latency; external dependency |



