CVE-2026-92939 Microsoft Sentinel · KQL

Detect vm2 Native Code Execution via crypto.setEngine Sandbox Escape (CVE-2026-92939) in Microsoft Sentinel

Detects exploitation and presence of CVE-2026-92939, a critical (CVSS 9.9, CWE-114 Process Control) sandbox escape in the vm2 npm package versions >= 3.11.3 and <= 3.11.6. The vm2 crypto builtin proxy fails to restrict the native `crypto.setEngine()` call, allowing untrusted code executing inside the vm2 sandbox to load an attacker-controlled native engine shared object (.so/.dll). Loading the native module executes attacker code outside the JavaScript sandbox with full host privileges of the Node.js process, resulting in remote code execution. A public PoC is available via GHSA-46pr-c5wc-xffx. This detection surfaces runtime indicators of the escape (Node.js processes loading unexpected native modules via OpenSSL engine loading, child process spawns from Node runtimes hosting vm2, and anomalous dlopen of untrusted shared objects) as well as software-inventory evidence of vulnerable vm2 versions. Remediate by upgrading to vm2 >= 3.11.7 or migrating off vm2 (the project is deprecated).

MITRE ATT&CK

Tactic
Execution Privilege Escalation

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
// CVE-2026-92939 - vm2 crypto.setEngine native code execution
// Surface Node.js processes loading native engine modules or spawning children (escape payload)
let nodeProcs = DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("node", "node.exe", "nodejs");
// Child process spawned by a node process == strong post-escape signal for a pure-JS workload
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("node", "node.exe", "nodejs")
| where FileName in~ ("sh", "bash", "cmd.exe", "powershell.exe", "pwsh", "gcc", "cc", "ld", "openssl", "whoami", "id", "curl", "wget")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, FolderPath
| union (
    DeviceImageLoadEvents
    | where Timestamp > ago(7d)
    | where InitiatingProcessFileName in~ ("node", "node.exe", "nodejs")
    | where FileName endswith ".so" or FileName endswith ".dll" or FileName endswith ".node"
    | where not(FolderPath has_any ("node_modules", "\\nodejs\\", "/usr/lib/node", "/usr/local/lib/node"))
    | project Timestamp, DeviceName, AccountName=InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, FolderPath
)
| order by Timestamp desc
critical severity medium confidence

Flags Node.js processes that spawn shell/compiler/recon child processes or load native modules (.so/.dll/.node) from paths outside standard Node runtime directories — the observable result of a vm2 setEngine escape loading attacker native code.

Data Sources

Microsoft Defender for Endpoint

Required Tables

DeviceProcessEventsDeviceImageLoadEvents

False Positives & Tuning

  • Node.js applications that legitimately use native addons (node-gyp built .node modules) loaded from non-standard build directories
  • Build/CI agents where node legitimately invokes gcc/cc/ld during npm install of native dependencies
  • Monitoring or APM agents that inject native profiling libraries into the Node process

Other platforms for CVE-2026-92939


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.

  1. Test 1Install vulnerable vm2 version (inventory detection)

    Expected signal: npm install process events, file creation under /tmp/vm2-cve/node_modules/vm2, node process reading package.json

  2. Test 2Node process spawns shell child (escape payload simulation)

    Expected signal: auditd execve showing node as parent of sh/id; Sysmon-equivalent process creation

  3. Test 3Node loads untrusted native module (dlopen simulation)

    Expected signal: node process image/library load of /tmp/evil_engine.so outside node_modules; appears in /proc/<pid>/maps


Response Playbook

Triage

  1. Confirm the host runs a Node.js application that embeds the vm2 package and determine its version via `npm ls vm2` / inspection of package-lock.json; versions >= 3.11.3 and <= 3.11.6 are vulnerable to CVE-2026-92939.
  2. Correlate the alerting Node.js process with the application it serves and establish whether that application accepts and evaluates untrusted/user-supplied JavaScript inside vm2 (the required precondition for exploitation).
  3. Examine the child process or loaded native module path flagged by the alert: determine whether the .so/.dll/.node file is a legitimate npm native addon or an attacker-staged artifact, including its on-disk location, hash, and signer.
  4. Review the parent Node process command line and recent application logs for the crypto.setEngine call pattern or errors referencing OpenSSL engine loading.

Containment

  1. Isolate the affected host from the network to prevent lateral movement and C2 from the escaped native payload.
  2. Kill the offending Node.js process and any child processes it spawned, and disable the service/pod until vm2 is upgraded to >= 3.11.7 or removed.
  3. Quarantine the suspicious native module/shared object and block its hash across the fleet.

Evidence Collection

  1. Capture the Node.js process memory and the full process tree (parent node + all children) before termination for forensic analysis.
  2. Collect the suspect native engine file (.so/.dll/.node), its filesystem metadata (timestamps, ownership), and compute cryptographic hashes.
  3. Preserve application and reverse-proxy logs showing the request/payload that reached the vm2 sandbox, plus package-lock.json documenting the vulnerable vm2 version.

Escalation Criteria

  • !Escalate to incident response if a non-npm, unsigned native module was loaded by the Node process or if any child process performed reconnaissance, credential access, or outbound network connections.
  • !Escalate if the affected application is internet-facing and accepts untrusted code, indicating probable active exploitation rather than a false positive.
  • !Escalate to threat hunting if the same native-module-load or node-spawns-shell pattern appears on multiple hosts.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >The attacker-supplied native engine shared object (.so/.dll/.node) on disk and its load entry in /proc/<pid>/maps or Sysmon ImageLoad events
  • >package-lock.json / node_modules/vm2/package.json showing version 3.11.3–3.11.6
  • >Application logs or stack traces referencing crypto.setEngine or OpenSSL ENGINE_by_id loading
  • >Node.js process tree showing child processes uncharacteristic of a pure-JS workload

Tuning Guidance

Baseline which Node.js applications legitimately spawn child processes or load native addons (node-gyp .node modules) and allowlist those specific parent application paths and module directories. Restrict the match to production application hosts and exclude CI/build agents where gcc/cc/ld invocation by node during npm install is expected. Prioritize alerts where the loaded native module resides outside node_modules or is unsigned/unknown-hash, as those are the strongest escape indicators.


Hunting Queries

Hunts for Node.js processes spawning shells, compilers, or recon tooling across the fleet, surfacing vm2 escape activity beyond the initial alert.

Hunting — KQL
kql
DeviceProcessEvents | where InitiatingProcessFileName in~ ("node","node.exe","nodejs") | where FileName in~ ("sh","bash","openssl","gcc","cc","whoami","id","curl","wget") | summarize count() by DeviceName, FileName, ProcessCommandLine, bin(Timestamp,1h)
Hunting — SPL
spl
index=* sourcetype="WinEventLog:Sysmon" OR sourcetype="linux:audit" | eval p=lower(coalesce(ParentImage,parent_process_name)) | where like(p,"%node%") | stats count values(Image) as children by host,user

Atomic Red Team Tests

Test 1 Install vulnerable vm2 version (inventory detection)
linux

Installs vm2 3.11.6 to generate software-inventory evidence of the vulnerable version range for CVE-2026-92939.

Command

bash
mkdir -p /tmp/vm2-cve && cd /tmp/vm2-cve && npm init -y >/dev/null 2>&1 && npm install [email protected] >/dev/null 2>&1 && node -e "console.log(require('/tmp/vm2-cve/node_modules/vm2/package.json').version)"

Cleanup

bash
rm -rf /tmp/vm2-cve

Expected Telemetry

npm install process events, file creation under /tmp/vm2-cve/node_modules/vm2, node process reading package.json

Expected Detection

Software inventory / SCA flags [email protected] as within affected range >=3.11.3,<=3.11.6.

Test 2 Node process spawns shell child (escape payload simulation)
linux

Simulates the post-escape behavior where native code loaded via setEngine spawns a host shell from the Node.js process.

Command

bash
node -e "require('child_process').execSync('id > /tmp/vm2_escape_proof.txt')"

Cleanup

bash
rm -f /tmp/vm2_escape_proof.txt

Expected Telemetry

auditd execve showing node as parent of sh/id; Sysmon-equivalent process creation

Expected Detection

Alert fires on Node.js parent spawning shell/recon child process.

Test 3 Node loads untrusted native module (dlopen simulation)
linux

Simulates loading an attacker-staged native engine module from a non-standard path, mimicking crypto.setEngine native code loading.

Command

bash
cp /usr/lib/x86_64-linux-gnu/libz.so.1 /tmp/evil_engine.so 2>/dev/null; node -e "try{process.dlopen({exports:{}},'/tmp/evil_engine.so')}catch(e){console.log('load attempted:',e.message)}"

Cleanup

bash
rm -f /tmp/evil_engine.so

Expected Telemetry

node process image/library load of /tmp/evil_engine.so outside node_modules; appears in /proc/<pid>/maps

Expected Detection

Alert fires on Node.js loading a native .so from an untrusted path outside standard runtime directories.

Related Detections