Sandbox Escape
Description
vm2 is a sandbox that can run untrusted code with Node's built-in modules. A sandbox escape vulnerability exists in vm2 for versions up to and including 3.9.17. It abuses an unexpected creation of a host object based on the specification of Proxy. As a result a threat actor can bypass the sandbox protections to gain remote code execution rights on the host running the sandbox. This vulnerability was patched in the release of version 3.9.18 of vm2. Users are advised to upgrade. There are no known workarounds for this vulnerability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A sandbox escape in vm2 versions ≤3.9.17 allows remote code execution by exploiting unexpected host object creation via Proxy.
CVE-2023-32314 is a sandbox escape vulnerability in vm2, a Node.js sandbox for running untrusted code. The flaw, present in all versions up to and including 3.9.17, abuses the specification of the ECMAScript Proxy object. Specifically, when the [[Call]] internal method of a Proxy is invoked, the argument list is converted to an array using CreateArrayFromList, which creates a native host array. This unintended host object can then be used to escape the sandbox and access the host's Function constructor [1][2][4].
Exploitation
To exploit the vulnerability, an attacker provides untrusted code to the VM.run() method. The provided proof-of-concept (PoC) constructs an Error whose name property's toString method is a Proxy. Inside the Proxy's apply trap, the args parameter—now a host array—allows access to args.constructor.constructor, which yields the host's Function constructor. From there, the attacker can execute arbitrary system commands by invoking process.mainModule.require('child_process').execSync [4]. No authentication or special privileges are required beyond the ability to submit code to the vm2 sandbox.
Impact
Successful exploitation grants the attacker remote code execution (RCE) on the host running the vm2 sandbox, with the privileges of the Node.js process. This completely bypasses the intended sandbox isolation, allowing data exfiltration, system compromise, or further lateral movement within the host environment [1][2].
Remediation
The vulnerability is patched in vm2 version 3.9.18. The fix introduces makeSafeHandlerArgs to wrap handler arguments in a safe array, preventing the leak of host objects through Proxy traps. Users are strongly advised to upgrade immediately, as no known workarounds exist. The project also cautions that in-process sandboxing is inherently challenging and recommends defense-in-depth or more robust isolation mechanisms such as separate processes or containers [1][3].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vm2npm | < 3.9.18 | 3.9.18 |
Affected products
2- Range: < 3.9.18
Patches
1d88105f99752Ensure host array does not leak through proxy
1 file changed · +58 −0
lib/setup-sandbox.js+58 −0 modified@@ -21,6 +21,7 @@ const { const { getPrototypeOf: localReflectGetPrototypeOf, apply: localReflectApply, + construct: localReflectConstruct, deleteProperty: localReflectDeleteProperty, has: localReflectHas, defineProperty: localReflectDefineProperty, @@ -432,6 +433,63 @@ if (AsyncGeneratorFunction) { overrideWithProxy(AsyncGeneratorFunction.prototype, 'constructor', AsyncGeneratorFunction, makeCheckFunction(true, true)); } +function makeSafeHandlerArgs(args) { + const sArgs = ensureThis(args); + if (sArgs === args) return args; + const a = []; + for (let i=0; i < sArgs.length; i++) { + localReflectDefineProperty(a, i, { + __proto__: null, + value: sArgs[i], + enumerable: true, + configurable: true, + writable: true + }); + } + return a; +} + +const makeSafeArgs = Object.freeze({ + __proto__: null, + apply(target, thiz, args) { + return localReflectApply(target, thiz, makeSafeHandlerArgs(args)); + }, + construct(target, args, newTarget) { + return localReflectConstruct(target, makeSafeHandlerArgs(args), newTarget); + } +}); + +const proxyHandlerHandler = Object.freeze({ + __proto__: null, + get(target, name, receiver) { + const value = target.handler[name]; + if (typeof value !== 'function') return value; + return new LocalProxy(value, makeSafeArgs); + } +}); + +function wrapProxyHandler(args) { + if (args.length < 2) return args; + const handler = args[1]; + args[1] = new LocalProxy({__proto__: null, handler}, proxyHandlerHandler); + return args; +} + +const proxyHandler = Object.freeze({ + __proto__: null, + apply(target, thiz, args) { + return localReflectApply(target, thiz, wrapProxyHandler(args)); + }, + construct(target, args, newTarget) { + return localReflectConstruct(target, wrapProxyHandler(args), newTarget); + } +}); + +const proxiedProxy = new LocalProxy(LocalProxy, proxyHandler); + +overrideWithProxy(LocalProxy, 'revocable', LocalProxy.revocable, proxyHandler); + +global.Proxy = proxiedProxy; global.Function = proxiedFunction; global.eval = new LocalProxy(localEval, EvalHandler);
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-whpj-8f3w-67p5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-32314ghsaADVISORY
- gist.github.com/arkark/e9f5cf5782dec8321095be3e52acf5acghsax_refsource_MISCWEB
- github.com/patriksimek/vm2/commit/d88105f99752305c5b8a77b63ddee3ec86912dafghsax_refsource_MISCWEB
- github.com/patriksimek/vm2/releases/tag/3.9.18ghsax_refsource_MISCWEB
- github.com/patriksimek/vm2/security/advisories/GHSA-whpj-8f3w-67p5ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.