Critical

vm2 crypto builtin loads attacker native code through setEngine

  • Disclosed: 01.10.2026
  • Updated: 11.10.2026 10:22

Summary

vm2 3.11.6 exposes the host crypto module to a NodeVM when that single builtin is allowed. The module is presented through a read-only bridge, but its functions still execute with host-process authority. crypto.setEngine() accepts a filesystem path and asks OpenSSL to dynamically load the referenced native library.

An attacker whose untrusted plugin package contains a native library can therefore load that library into the host process by calling crypto.setEngine() from sandboxed JavaScript. The native library's constructor executes before OpenSSL finishes validating whether the file is a usable engine. Consequently, even the expected ERR_CRYPTO_ENGINE_UNKNOWN exception occurs only after arbitrary native code has already run.

The exploit requires only the crypto builtin. It does not require fs, process, module, child_process, worker_threads, vm, inspector, unrestricted builtins, or vm2 nesting.

Details

The vulnerable boundary is the generic builtin loader. Builtins that are not specially wrapped or classified as dangerous are imported in the host realm and exposed through a recursive read-only proxy:

js
builtins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));

Read-only prevents sandbox code from assigning properties on the module object. It does not reduce the authority of callable exports. Calls are forwarded to the original host function with bridge values converted back to host values.

The crypto module includes this callable export:

js
crypto.setEngine(enginePath)

When enginePath names a dynamic library, OpenSSL loads that file into the current process. Operating-system dynamic loaders execute library constructors as part of loading. Engine-symbol validation happens afterward. A file does not have to become a functional cryptographic engine for its constructor to execute.

The resulting exploit flow is:

attacker supplies an untrusted plugin package containing a native library
  -> host executes the plugin in NodeVM with only crypto allowed
  -> plugin calls crypto.setEngine(pathToBundledLibrary)
  -> vm2 forwards the call to the host crypto module
  -> OpenSSL asks the operating-system loader to load the library
  -> the library constructor executes native code in the host process
  -> OpenSSL may then reject the file, but host compromise has already occurred

This is different from merely granting JavaScript filesystem access. A plugin system commonly has to place an untrusted package on disk before running its JavaScript. The native file can therefore already exist inside that package even when the sandbox denies fs and all process-execution builtins. Allowing crypto for hashing or signature verification unexpectedly turns that inert package file into a native-code execution primitive.

PoC

The attached PoC is local and harmless. Its native library constructor creates a marker file; it does not spawn a process, open a network connection, or modify any other file.

  1. Install Node.js with OpenSSL engine support, a C compiler, and npm.

  2. In the attached poc directory, install the exact affected package:

    bash
    npm install --ignore-scripts
  3. Build the marker library.

    On macOS:

    bash
    cc -dynamiclib -O2 -o probe-engine.dylib engine_probe.c
    node poc.js ./probe-engine.dylib

    On Linux:

    bash
    cc -shared -fPIC -O2 -o probe-engine.so engine_probe.c
    node poc.js ./probe-engine.so
  4. A vulnerable result contains all of the following:

    • the sandbox configuration lists only crypto as an allowed builtin;
    • crypto.setEngine() reports ERR_CRYPTO_ENGINE_UNKNOWN or returns;
    • vm2-setengine-native-marker.txt exists afterward;
    • the marker contains VM2_SETENGINE_NATIVE_CODE_EXECUTED.

The exception is not a negative result. The marker proves that the native constructor ran before engine validation failed.

Impact

This is a sandbox escape to arbitrary native code execution. The code runs with the operating-system identity and privileges of the Node.js host process, outside all vm2 language and module restrictions.

An attacker can replace the marker-only constructor with native code that:

  • reads application secrets, environment variables, credentials, and files available to the host user;
  • modifies application data or executable files and establishes persistence;
  • accesses internal services using the host's network identity;
  • steals other tenants' data from the same process;
  • terminates or corrupts the host process; and
  • executes arbitrary operating-system actions permitted to the host account.

The realistic affected workflow is a plugin, automation, notebook, or multi-tenant code runner that stores attacker-supplied package contents on disk and runs the package's JavaScript in NodeVM while allowing crypto. The attacker does not need the sandbox to expose a file-write or command-execution module.

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
npm package vm2 >= 3.11.3, < 3.11.7 3.11.7

References

CVSS score 9.9/10
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Weakness (CWE)
CWE-114
Platform
npm
Package
vm2
Disclosed
01.10.2026
Updated
11.10.2026 10:22

Sources and licenses

OSV / GitHub Advisory Database - GHSA-46pr-c5wc-xffx - CC BY 4.0

Vulnerability history: vm2

All published vulnerabilities of this package (npm package), newest first. Shows whether this is a one-off slip or a package that needs watching.

All (78) in the archive
Total entries
78
First → latest
10.2021 10.2026
Without a fix
1
Exploited in attacks
0

Severity breakdown

  • Critical 51
  • High 14
  • Medium 12
  • Low 1

Disclosures per year

  1. 2021: 1
  2. 2022: 4
  3. 2023: 7
  4. 2024: 0
  5. 2025: 0
  6. 2026: 66
Severity:
  1. vm2 crypto builtin loads attacker native code through setEngine this entry
    Critical · 9.9 CVE-2026-92939 >= 3.11.3, < 3.11.7 3.11.7
  2. Critical · 10.0 CVE-2026-93605 < 3.12.1 3.12.1
  3. Critical · 10.0 CVE-2026-100721 < 3.12.2 3.12.2
  4. Critical · 10.0 CVE-2026-92955 < 3.11.8 3.11.8
  5. Critical · 10.0 CVE-2026-92956 >= 3.10.1, < 3.11.7 3.11.7
  6. Critical · 9.9 CVE-2026-92948 >= 3.9.6, < 3.11.7 3.11.7
  7. Critical · 10.0 CVE-2026-92940 >= 3.11.3, < 3.11.7 3.11.7
  8. Critical · 9.0 CVE-2026-92935 >= 3.11.4, < 3.11.7 3.11.7
  9. Critical · 10.0 CVE-2026-92937 >= 3.11.6, < 3.11.7 3.11.7
Show more (20) Showing 21 of 65

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.