May 5, 2026·6 min read·24 visits
vm2 prior to version 3.10.5 contains a critical sandbox escape (CVSS 9.8). Attackers bypass internal security wrappers by overwriting Function.prototype.call and hijacking Promise creation, achieving unauthenticated remote code execution on the host system.
An incomplete mitigation for a previous sandbox escape in the vm2 Node.js module allows attackers to execute arbitrary code on the host system. By manipulating Promise species and intercepting internal method calls via prototype pollution, attackers bypass sandbox protections and gain full host access.
The vm2 package is a widely used Node.js module designed to run untrusted code within an isolated V8 execution context. Versions prior to 3.10.5 contain a critical vulnerability, tracked as CVE-2026-24120, which enables a complete sandbox escape. The vulnerability represents a failure of internal protection mechanisms (CWE-693) leading to arbitrary code injection (CWE-94) on the host system.
The attack surface involves the execution of untrusted JavaScript input within the sandbox environment. The vulnerability specifically targets the Promise resolution mechanism and the internal security wrappers implemented by the package maintainers. Attackers manipulate standard JavaScript built-in objects before the sandbox completes its secure initialization phase.
Successful exploitation results in arbitrary command execution on the host operating system with the privileges of the executing Node.js process. This completely compromises the application relying on the sandbox for security boundary enforcement. Organizations utilizing vm2 for multi-tenant isolation or untrusted code evaluation face immediate risk of host compromise.
The vulnerability stems from an incomplete patch for CVE-2023-37466, a prior sandbox escape regarding Promise species hijacking. The vm2 security logic attempts to neutralize attacks by resetting the Symbol.species property on the Promise constructor. This reset process relies on JavaScript internal method execution during sandbox setup.
Prior to version 3.10.5, the vm2 wrapper code utilized standard, mutable prototype lookups to execute original Promise methods. Specifically, internal security wrappers for then and catch invoked globalPromiseThen.call(this, ...). This reliance on .call() introduces an exploitable condition if the prototype chain is altered.
An attacker executing code within the sandbox initializes the exploit by overwriting Object.defineProperty with a no-operation function. This neutralization prevents the subsequent vm2 security fixups from correctly applying immutable properties to internal objects. The sandbox environment remains partially initialized and susceptible to further manipulation.
Concurrently, the attacker overrides Function.prototype.call. When the internal vm2 wrapper processes a Promise, it inadvertently routes execution through the attacker-controlled call function. This exposes internal references and allows the V8 engine to resolve the Promise using a malicious constructor defined via Symbol.species.
Analyzing the setup-sandbox.js file reveals the exact mechanism of the protection failure. The vulnerable implementation binds the execution of wrapped Promise methods using Function.prototype.call. This design incorrectly assumes the integrity of the prototype chain within the sandbox environment during internal wrapper execution.
The following code illustrates the vulnerable implementation alongside the patch introduced in commit 4b009c2d4b1131c01810c1205e641d614c322a29.
// File: lib/setup-sandbox.js
globalPromise.prototype.then = function then(onFulfilled, onRejected) {
// ...
- return globalPromiseThen.call(this, onFulfilled, onRejected);
+ return apply(globalPromiseThen, this, [onFulfilled, onRejected]);
};
globalPromise.prototype.catch = function _catch(onRejected) {
// ...
- return globalPromiseCatch.call(this, onRejected);
+ return apply(globalPromiseCatch, this, [onRejected]);
};The fix implements a required architectural shift by replacing .call() with a local apply primordial. This apply reference is derived securely from Reflect.apply during the initial environment capture. Capturing the primordial reference early makes the operation immune to runtime prototype pollution.
By utilizing local primordials, the patch severs the internal dependency on the mutable Function.prototype. Attackers modifying Function.prototype.call no longer intercept the internal execution flow of the vm2 wrappers. This closes the specific execution hijack vector utilized in this escape sequence.
The exploitation chain requires the attacker to submit a precisely constructed JavaScript payload into the vm2.run() interface. The payload first neutralizes the environment by redefining Object.defineProperty = () => {}. This action stops vm2 from locking down internal object structures during subsequent execution phases.
The attacker proceeds to define a malicious FakePromise constructor and assigns it to p.constructor[Symbol.species]. Simultaneously, the attacker overrides Function.prototype.call. When the sandbox invokes a native Promise operation, the overridden call method intercepts the V8 internal execution flow.
The interception forces the V8 engine's PromiseResolveThenableJob to utilize the attacker's custom constructor. Inside this malicious constructor, the engine passes contextual arguments originating from the host environment. These arguments typically manifest as Error objects or internal stack structures referencing host-level contexts.
The attacker executes prototype climbing on the leaked host object. By accessing err.constructor.constructor, the payload obtains a direct reference to the host environment's Function object. From this access point, Function('return process')() retrieves the Node.js global process object, enabling direct command execution via process.mainModule.require('child_process').execSync().
The vulnerability carries a CVSS v3.1 base score of 9.8, categorizing it as Critical severity. The attack vector is entirely network-based, assuming the target application accepts remote input for evaluation within the sandbox. The complexity is low, as reliable and deterministic proof-of-concept payloads exist.
Exploitation requires no authentication and no user interaction. An attacker successfully executing the payload achieves a complete bypass of the intended security boundary. The confidentiality, integrity, and availability impacts are universally high, resulting in total control of the underlying host environment.
The EPSS score stands at 0.00080 (23.22nd percentile), indicating a lower immediate probability of mass exploitation relative to other vulnerabilities. However, the operational impact remains catastrophic for systems relying on vm2 for multi-tenant isolation or untrusted code execution.
While the vulnerability currently shows no known active exploitation in the wild and is absent from the CISA Known Exploited Vulnerabilities (KEV) catalog, the public availability of the exploitation methodology facilitates rapid weaponization by threat actors.
The immediate technical mitigation requires upgrading the vm2 package to version 3.10.5. This release integrates the primordial replacements for the internal Promise wrappers. Engineering teams must verify the version update across all dependency trees within their Node.js environments.
The vm2 project is officially deprecated by its maintainers. The inherent architectural limitations of securing the V8 execution context against recursive prototype escapes render long-term maintenance of this model unfeasible. Organizations must treat the 3.10.5 patch strictly as a temporary, stopgap measure.
A permanent remediation strategy requires migrating away from vm2 entirely. Security architectures must transition to isolated processes utilizing strict operating system boundaries. Valid alternatives include WebAssembly runtimes, Docker containers with dropped capabilities, or Firecracker microVMs for robust isolation.
In environments where immediate migration is impossible, network-level monitoring provides secondary defense. Security teams should deploy Endpoint Detection and Response (EDR) signatures monitoring for anomalous child processes spawning from Node.js applications. Specifically, alerts should trigger on sh, bash, or cmd.exe invocations originating from processes handling untrusted input.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
vm2 patriksimek | < 3.10.5 | 3.10.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94, CWE-693 |
| Attack Vector | Network |
| CVSS Score | 9.8 (Critical) |
| EPSS Score | 0.00080 (23.22%) |
| Exploit Status | Proof of Concept Available |
| KEV Status | Not Listed |
Improper Control of Generation of Code ('Code Injection') via Protection Mechanism Failure
The @jhb.software/payload-cloudinary-plugin exposes an endpoint that performs unvalidated cryptographic signing of Cloudinary API parameters, allowing authenticated users with minimal privileges to forge valid signatures for arbitrary actions. This flaw allows attackers to overwrite remote storage assets, execute unauthorized file uploads, alter asset visibility parameters, trigger SSRF webhooks, and perform directory traversal within Cloudinary repositories.
A Server-Side Request Forgery (SSRF) and Bearer Token Exfiltration vulnerability exists in the @merill/lokka (Lokka) Model Context Protocol (MCP) server prior to version 2.1.2. The server constructed Azure Resource Manager request URLs by concatenating user-controlled path parameters directly into destination request strings. By injecting authority-redefinition characters, an attacker can manipulate URL parsing to execute a host-escape attack, forcing the server to send high-privilege Azure Resource Manager (ARM) Bearer tokens to an external attacker-controlled host. This allows complete administrative access to the associated Azure subscriptions.
A directory traversal and symlink following vulnerability exists in Pydantic Settings when using the NestedSecretsSettingsSource with nested subdirectory lookups enabled. An attacker capable of writing to the secrets directory can bypass size limitations, read arbitrary host files, or cause a denial-of-service condition via cyclic symlinks.
A Server-Side Request Forgery (SSRF) vulnerability exists in SurrealDB's Identity & Access Management (IAM) module prior to version 3.1.5. When configuring JSON Web Key Set (JWKS) URLs for token verification, the remote fetcher follows HTTP redirects by default without validating redirect targets against configured network capabilities. This allows high-privileged users to bypass network access limits and perform blind port scanning of internal network resources.
A local file disclosure vulnerability exists in SurrealDB's full-text search capabilities, allowing authenticated users with database EDITOR or OWNER roles to read arbitrary files from the host system filesystem. This occurs by abusing the mapper() filter inside a DEFINE ANALYZER statement to point to system files.
SurrealDB versions 3.0.0 through 3.1.4 contain an information exposure vulnerability (CWE-203) where the query planner optimizes sorted queries using indexes on fields with field-level SELECT restrictions. Because the query planner performs index-based sorting before enforcing permission-based redaction, unauthorized users can observe the physical order of returned rows to deduce the relative values of protected fields.