# [CRIT] [GHSA / CRITICAL] CVE-2026-47208: vm2 is Vulnerable to Sandbox Breakout Through Promise Species

**Source:** GitHub Security Advisories
**Published:** 2026-05-29
**Article:** https://github.com/advisories/GHSA-76w7-j9cq-rx2j

## Threat Profile

vm2 is Vulnerable to Sandbox Breakout Through Promise Species

### Summary

VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system.

### Details

The `localPromise` constructor was changed to call `this.then(undefined, eater)` to ensure a rejected promise is always used. However, this is missing a call to `resetPromiseSpecies` to ensure that `this` has no special species. Since…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-47208`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1059.007** — Command and Scripting Interpreter: JavaScript
- **T1068** — Exploitation for Privilege Escalation
- **T1059.003** — Command and Scripting Interpreter: Windows Command Shell

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Vulnerable vm2 package (<=3.11.3) present on endpoints — CVE-2026-47208 exposure

`UC_176_1` · phase: **weapon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Filesystem.file_path) as file_path from datamodel=Endpoint.Filesystem where Filesystem.file_name="package.json" Filesystem.file_path="*\\node_modules\\vm2\\package.json" by Filesystem.dest Filesystem.user Filesystem.file_path | `drop_dm_object_name(Filesystem)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
// Hunts vm2 package.json drops on disk; pair with DeviceTvmSoftwareInventory if Node modules ingested
DeviceFileEvents
| where Timestamp > ago(30d)
| where FileName =~ "package.json"
| where FolderPath has @"\node_modules\vm2\" or FolderPath has "/node_modules/vm2/"
| project Timestamp, DeviceName, FolderPath, FileName, SHA256,
          InitiatingProcessFileName, InitiatingProcessCommandLine,
          InitiatingProcessAccountName
| order by Timestamp desc
```

### node.exe spawning child_process targets — vm2 Promise species sandbox escape post-exploitation

`UC_176_2` · phase: **exploit** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.parent_process) as parent_cmdline from datamodel=Endpoint.Processes where Processes.parent_process_name="node.exe" AND Processes.process_name IN ("cmd.exe","powershell.exe","pwsh.exe","bash.exe","sh.exe","wscript.exe","cscript.exe","touch.exe","curl.exe","wget.exe","certutil.exe") AND NOT (Processes.parent_process IN ("*npm*install*","*npm*ci*","*yarn*","*pnpm*","*node-gyp*","*electron*")) by Processes.dest Processes.user Processes.process_name Processes.parent_process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
// vm2 sandbox escape -> child_process.execSync spawns host process from node.exe
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName =~ "node.exe"
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","bash.exe","sh.exe","wscript.exe","cscript.exe","touch.exe","curl.exe","wget.exe","certutil.exe","bitsadmin.exe")
| where not (InitiatingProcessCommandLine has_any ("npm install","npm ci","npm run","yarn install","pnpm install","node-gyp","electron-builder","husky","postinstall"))
| where AccountName !endswith "$"
| project Timestamp, DeviceName, AccountName,
          ParentPath = InitiatingProcessFolderPath,
          ParentCmd  = InitiatingProcessCommandLine,
          ChildPath  = FolderPath,
          ChildCmd   = ProcessCommandLine,
          SHA256, ProcessIntegrityLevel
| order by Timestamp desc
```

### vm2 Promise species sandbox escape PoC fingerprint in scripts/command lines

`UC_176_3` · phase: **weapon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline from datamodel=Endpoint.Processes where Processes.process_name IN ("node.exe","nodejs.exe") (Processes.process="*constructor.constructor(\"return process\")*" OR Processes.process="*mainModule.require*child_process*" OR Processes.process="*Symbol.species*Promise*") by Processes.dest Processes.user Processes.process_name Processes.parent_process_name | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
```

**Defender KQL:**
```kql
// Hunts CVE-2026-47208 PoC fingerprint in node.exe cmdlines AND .js file drops
let poc_strings = dynamic([
    "constructor.constructor(\"return process\")",
    "constructor.constructor('return process')",
    "mainModule.require('child_process')",
    "mainModule.require(\"child_process\")",
    "Symbol.species"
]);
let cmdline_hits =
    DeviceProcessEvents
    | where Timestamp > ago(30d)
    | where FileName in~ ("node.exe","nodejs.exe")
    | where ProcessCommandLine has_any (poc_strings)
    | project Timestamp, DeviceName, AccountName, Source = "cmdline",
              Evidence = ProcessCommandLine,
              InitiatingProcessFileName, InitiatingProcessCommandLine;
let file_hits =
    DeviceFileEvents
    | where Timestamp > ago(30d)
    | where FileName endswith ".js" or FileName endswith ".mjs" or FileName endswith ".cjs"
    | where InitiatingProcessCommandLine has_any (poc_strings) or FolderPath has "vm2"
    | project Timestamp, DeviceName, AccountName = InitiatingProcessAccountName,
              Source = "file",
              Evidence = strcat(FolderPath, "\\", FileName),
              InitiatingProcessFileName, InitiatingProcessCommandLine;
union cmdline_hits, file_hits
| order by Timestamp desc
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-47208`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 4 use case(s) fired, 4 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
