CVE-2026-92939
vm2 3.11.3 through 3.11.6 Native Code Execution via crypto.setEngine
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
9.4
vm2 3.11.3 through 3.11.6 Native Code Execution via crypto.setEngine
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
CVE-2026-92939- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
EUVDEUVD-2026-81589
9.4
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
EUVD-2026-81589- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-46pr-c5wc-xffx
9.9
vm2 crypto builtin loads attacker native code through setEngine
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:
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:
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.
-
Install Node.js with OpenSSL engine support, a C compiler, and npm.
-
In the attached
pocdirectory, install the exact affected package:npm install --ignore-scripts -
Build the marker library.
On macOS:
cc -dynamiclib -O2 -o probe-engine.dylib engine_probe.c node poc.js ./probe-engine.dylibOn Linux:
cc -shared -fPIC -O2 -o probe-engine.so engine_probe.c node poc.js ./probe-engine.so -
A vulnerable result contains all of the following:
- the sandbox configuration lists only
cryptoas an allowed builtin; crypto.setEngine()reportsERR_CRYPTO_ENGINE_UNKNOWNor returns;vm2-setengine-native-marker.txtexists afterward;- the marker contains
VM2_SETENGINE_NATIVE_CODE_EXECUTED.
- the sandbox configuration lists only
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…
- Record
GHSA-46pr-c5wc-xffx- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-6jgm-4w45-vh8j
9.4
Duplicate Advisory: vm2 crypto builtin loads attacker native code through setEngine
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-46pr-c5wc-xffx. This link is maintained to preserve external references.
Original Description
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
GHSA-6jgm-4w45-vh8j- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
NVD
9.4
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
CVE-2026-92939- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
OSV
9.4
vm2 3.11.3 through 3.11.6 Native Code Execution via crypto.setEngine
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
CVE-2026-92939- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - Weakness
- CWE-114
- Updated
- 2026-11-02
Show 4 moreShow less
OSVGHSA-46pr-c5wc-xffx
9.9
vm2 crypto builtin loads attacker native code through setEngine
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:
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:
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.
-
Install Node.js with OpenSSL engine support, a C compiler, and npm.
-
In the attached
pocdirectory, install the exact affected package:npm install --ignore-scripts -
Build the marker library.
On macOS:
cc -dynamiclib -O2 -o probe-engine.dylib engine_probe.c node poc.js ./probe-engine.dylibOn Linux:
cc -shared -fPIC -O2 -o probe-engine.so engine_probe.c node poc.js ./probe-engine.so -
A vulnerable result contains all of the following:
- the sandbox configuration lists only
cryptoas an allowed builtin; crypto.setEngine()reportsERR_CRYPTO_ENGINE_UNKNOWNor returns;vm2-setengine-native-marker.txtexists afterward;- the marker contains
VM2_SETENGINE_NATIVE_CODE_EXECUTED.
- the sandbox configuration lists only
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…
- Record
GHSA-46pr-c5wc-xffx- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
OSVGHSA-6jgm-4w45-vh8j
9.4
Duplicate Advisory: vm2 crypto builtin loads attacker native code through setEngine
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-46pr-c5wc-xffx. This link is maintained to preserve external references.
Original Description
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, child_process, worker_threads, vm, or inspector access, resulting in a sandbox escape and arbitrary native code execution. Fixed in 3.11.7.
- Record
GHSA-6jgm-4w45-vh8j- CVSS 4.0
- 9.4 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-114
- Updated
- 2026-11-02
OSVCLSA-2026-1790888247 No score
TuxCare security update for vm2 (15 CVEs)
TuxCare rebuilt vm2 to address 15 CVEs. Fixed in 3.11.5-tuxcare.3.
- Record
CLSA-2026-1790888247- Updated
- 2026-11-02
OSVROOT-APP-NPM-CVE-2026-92939 No score
CVE-2026-92939 in vm2 - Patched by Root
Root has patched CVE-2026-92939 in the vm2 package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-CVE-2026-92939- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
vm2npm |
3.11.3 to 3.11.6 | 3.11.7 |
vm2TuxCare:npm From an advisory covering 13 CVEs |
3.11.5-tuxcare.0 up to 3.11.5-tuxcare.3, plus listed versions 3.11.53.11.5-tuxcare.13.11.5-tuxcare.2 |
3.11.5-tuxcare.3 |
vm2npm |
3.11.3 up to 3.11.7 | 3.11.7 |
vm2Root:npm |
before 3.11.6-aikido.1 | 3.11.6-aikido.1 |
@rootio/vm2Root:npm |
before 3.11.6-root.io.1 | 3.11.6-root.io.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| affected | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-92939 is a critical vulnerability (CVSS 9.4) in Patriksimek Vm2, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.62%, the 49th percentile. Fixed in vm2 3.11.7.
References
github.com/patriksimek/vm2/security/advisories/GHSA-46pr-c5wc-xffx
vulncheck.com/advisories/vm2-3.11.3-through-3.11.6-native-code-execution-via-crypto-setengine
nvd.nist.gov/vuln/detail/CVE-2026-92939
github.com/patriksimek/vm2/commit/aa146a77f859325e079f3bfbfe6d8309af483daa
github.com/patriksimek/vm2/releases/tag/v3.11.7
github.com/advisories/GHSA-46pr-c5wc-xffx
github.com/advisories/GHSA-6jgm-4w45-vh8j
github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92939.json
github.com/patriksimek/vm2
Left out here: the rest of 2 source descriptions. The JSON record has them.