vm2 Sandbox Escape via Promise Constructor Unhandled Rejection
A sandbox escape vulnerability exists in vm2 versions 3.10.5 and earlier that allows sandboxed code to crash the host Node.js process via a Promise constructor that triggers an unhandled rejection, leading to a denial-of-service condition.
A sandbox escape vulnerability has been identified in vm2 versions 3.10.5 and earlier. This vulnerability enables malicious sandboxed code to crash the host Node.js process through a crafted Promise constructor that triggers an unhandled rejection. The root cause lies in the fact that Promise executor errors are not adequately caught and sanitized before they can propagate as unhandled rejections to the host process. This results in an immediate process crash, effectively causing a denial-of-service condition. Notably, the allowAsync: false setting, intended to mitigate asynchronous code execution, does not prevent this vulnerability and, paradoxically, can exacerbate the issue by blocking .catch() handlers, thereby guaranteeing that rejections remain unhandled. The CVE-2026-22709 patch (v3.10.2) only sanitized .then() and .catch() callback chains but left the executor-to-unhandledRejection path completely open.
Attack Chain
- Attacker crafts malicious JavaScript code designed to be executed within a vm2 sandbox.
- The malicious code constructs a
Promiseobject using thePromiseconstructor. - Within the
Promiseexecutor function, anErrorobject is created. - The
Errorobject'snameproperty is set to aSymbol(). - The code then attempts to access the
stackproperty of theErrorobject, triggering V8's internalFormatStackTracefunction. FormatStackTraceattemptsSymbol.toString(), resulting in a host-realmTypeError.- Because no
.catch()handler is attached, the error becomes an unhandled rejection. - The unhandled rejection propagates to the host Node.js process, causing it to crash.
Impact
Successful exploitation of this vulnerability leads to a denial-of-service (DoS) condition. A single malicious request can crash the entire host Node.js process, disrupting service for all concurrent users. The persistent nature of this DoS, coupled with the ease of triggering it, renders the application unavailable even with standard recovery mechanisms like Docker restart policies or Kubernetes liveness probes. The attacker only needs to send a small request (approximately 150 bytes) to bring down the entire server, demonstrating a high amplification factor. All applications using vm2 are vulnerable, regardless of the allowAsync setting.
Recommendation
- Upgrade to a patched version of vm2 that addresses this vulnerability. As of this writing, no patched version exists, but monitor the project for updates.
- Implement rate limiting on API endpoints that execute user-provided JavaScript code to reduce the impact of DoS attacks.
- Deploy the Sigma rule "Detect vm2 Sandbox Escape Attempt via Symbol Error Name" to identify attempts to exploit this vulnerability within your environment.
- Monitor application logs for unhandled promise rejections, which could indicate exploitation attempts.
- Consider implementing a custom error handler within the vm2 sandbox to catch and sanitize errors before they can propagate to the host process.
Detection coverage 2
Detect vm2 Sandbox Escape Attempt via Symbol Error Name
highDetects attempts to exploit the vm2 sandbox escape vulnerability by identifying code that sets Error.name to a Symbol within a vm2 process.
Detect vm2 Sandbox Escape Attempt via Unhandled Rejection
criticalDetects attempts to exploit the vm2 sandbox escape vulnerability by monitoring for unhandled promise rejections containing 'TypeError: Cannot convert a Symbol value to a string'.
Detection queries are available on the platform. Get full rules →