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
Share

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
qualitative

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

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

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
None
Integrity
None
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
May 07, 2026 - 04:32 vuln.today
Analysis Generated
May 07, 2026 - 04:32 vuln.today
CVE Published
May 07, 2026 - 04:10 nvd
HIGH 8.6

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.

AnalysisAI

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.

Technical ContextAI

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.

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.

CVE-2025-1974 CRITICAL POC
9.8 Mar 25

A critical vulnerability in Kubernetes ingress-nginx controller allows unauthenticated attackers with pod network access

CVE-2026-45321 CRITICAL POC
9.6 May 12

Credential-harvesting malware compromised 84 versions of 42 TanStack npm packages on 2026-05-11 via chained GitHub Actio

CVE-2025-1098 HIGH POC
8.8 Mar 25

Kubernetes ingress-nginx contains a configuration injection vulnerability via the mirror-target and mirror-host Ingress

CVE-2025-24514 HIGH POC
8.8 Mar 25

A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-url` Ingres

CVE-2025-1097 HIGH POC
8.8 Mar 25

A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-tls-match-c

CVE-2020-8554 MEDIUM POC
6.3 Jan 21

Kubernetes API server in all versions allow an attacker who is able to create a ClusterIP service and set the spec.exter

CVE-2025-55190 CRITICAL POC
9.9 Sep 04

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Rated critical severity (CVSS 9.9), this vulne

CVE-2018-18843 CRITICAL POC
10.0 Dec 04

The Kubernetes integration in GitLab Enterprise Edition 11.x before 11.2.8, 11.3.x before 11.3.9, and 11.4.x before 11.4

CVE-2026-22039 CRITICAL POC
9.9 Jan 27

Kyverno Kubernetes policy engine prior to 1.x has a privilege escalation vulnerability (CVSS 9.9) allowing policy bypass

CVE-2024-42480 CRITICAL POC
9.9 Aug 12

Kamaji is the Hosted Control Plane Manager for Kubernetes. Rated critical severity (CVSS 9.9), this vulnerability is rem

CVE-2023-28110 CRITICAL POC
9.9 Mar 16

Jumpserver is a popular open source bastion host, and Koko is a Jumpserver component that is the Go version of coco, ref

CVE-2026-25996 CRITICAL POC
9.8 Feb 12

String filter bypass in Inspektor Gadget Kubernetes eBPF tooling before fix. Insufficient string escaping enables filter

Vendor StatusVendor

Share

CVE-2026-44001 vulnerability details – vuln.today

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