CVE-2026-92939: vm2 3.11.3 through 3.11.6 Native Code Execution via crypto.setEngine

Published Sep 17, 2026
·
Updated

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 ERRCRYPTOENGINEUNKNOWN exception occurs only after arbitrary native code has already run.

The exploit requires only the crypto builtin. It does not require fs, process, module, childprocess, workerthreads, 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:

text 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 engineprobe.c node poc.js ./probe-engine.dylib

On Linux:

bash cc -shared -fPIC -O2 -o probe-engine.so engineprobe.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 ERRCRYPTOENGINEUNKNOWN or returns; - vm2-setengine-native-marker.txt exists afterward; - the marker contains VM2SETENGINENATIVECODEEXECUTED.

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.

Other sources

vm2 3.11.3 through 3.11.6 exposes the host Node.js crypto module to a NodeVM sandbox when the crypto builtin is allowed. The module is presented via a recursive read-only proxy, but its callable exports still execute with host-process authority. Sandboxed JavaScript can therefore call crypto.setEngine() with a filesystem path to an attacker-supplied native library (for example, one bundled in an untrusted plugin package already written to disk); OpenSSL asks the operating-system dynamic loader to load the file, and the library's constructor executes native code in the host process before engine-symbol validation rejects it. Exploitation requires only the crypto builtin and does not require fs, process, module, childprocess, workerthreads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.

— MITRE

Affected Software

2 affected componentsFixes available
npm/vm2>=3.11.3<=3.11.6
npm/vm2>=3.11.3<=3.11.6
3.11.7

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.7
  2. Upgrade

    Upgrade vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.7

Event History

Sep 17, 2026
CVE Published
via MITRE·01:46 PM
Data Sourced
via MITRE·01:46 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·02:17 PM
DescriptionSeverityWeakness
Oct 1, 2026
Advisory Published
via GitHub·03:28 PM
Data Sourced
via GitHub·03:28 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are exposed?

Deployments using vm2 versions 3.11.3 through 3.11.6 are exposed when a NodeVM sandbox is configured to allow the crypto builtin. The issue applies even if other powerful builtins such as fs, process, module, child_process, worker_threads, vm, and inspector are unavailable.

2

What does an attacker need to exploit this?

An attacker needs the ability to run JavaScript in the affected NodeVM sandbox and to provide a filesystem path to an attacker-supplied native library already present on disk. No user interaction or additional builtin access is required beyond crypto.

3

Is the default sandbox configuration affected?

The provided information only identifies configurations where the crypto builtin is allowed as affected. It does not state whether crypto is allowed by default.

4

What should be done if the update cannot be applied immediately?

Remove or disable the crypto builtin for untrusted NodeVM sandboxes. This prevents the described crypto.setEngine() path from being available.

5

How can I determine whether an environment is already at risk?

Check whether vm2 is version 3.11.3 through 3.11.6 and whether any NodeVM configuration permits the crypto builtin for untrusted code. Upgrade to version 3.11.7 to obtain the fix.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203