Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-47139

CVE-2026-47139: vm2 Node.js Sandbox SSRF Vulnerability

CVE-2026-47139 is a server-side request forgery flaw in vm2 that allows sandboxed code to bypass network restrictions using internal HTTP builtins. This article covers the technical details, affected versions, and mitigations.

Published:

CVE-2026-47139 Overview

CVE-2026-47139 is a sandbox escape vulnerability in the vm2 library, an open source virtual machine and sandbox for Node.js. Prior to version 3.11.4, the NodeVM builtin allowlist mechanism failed to block Node.js internal underscored modules such as _http_client and _http_server. Code running inside the sandbox could use these private modules to make outbound HTTP requests and open listening sockets even when http, https, net, tls, dns, dgram, and http2 were explicitly excluded. The flaw maps to [CWE-693: Protection Mechanism Failure].

Critical Impact

Sandboxed code can bypass network restrictions to perform server-side request forgery (SSRF), data exfiltration, or open inbound listeners from a process expected to be network-isolated.

Affected Products

  • vm2 (npm package by patriksimek) versions prior to 3.11.4
  • Node.js applications using NodeVM with the builtin: ['*', '-http', '-https', ...] exclusion pattern
  • Downstream libraries and services embedding vm2 for untrusted code execution

Discovery Timeline

  • 2026-06-12 - CVE-2026-47139 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2026-47139

Vulnerability Analysis

The vm2 library lets host applications execute untrusted JavaScript with a configurable allowlist of Node.js builtin modules. The NodeVM constructor accepts a builtin option that supports the '*' wildcard combined with negated entries to exclude specific modules. Developers relying on this pattern assume that excluding http, https, net, tls, dns, dgram, dns/promises, and http2 denies all network access from sandboxed code.

Node.js exposes private implementation modules through require('module').builtinModules. These underscored modules — _http_client, _http_server, _http_agent, _http_common, _http_incoming, _http_outgoing, _tls_common, _tls_wrap, and _stream_* — back the documented public APIs and expose the same network primitives. The wildcard expansion in vm2 included these private modules but the exclusion list did not.

Root Cause

The filter applied to BUILTIN_MODULES in lib/builtin.js removed only entries beginning with internal/ and a hardcoded list of dangerous builtins. It did not exclude underscored builtins from wildcard expansion. As a result, builtin: ['*', '-http'] still resolved _http_client and _http_server, which expose ClientRequest and Server constructors directly.

Attack Vector

An attacker controlling code inside a NodeVM sandbox calls require('_http_client') or require('_http_server') to obtain network primitives. The attacker can then issue arbitrary outbound HTTP requests to internal services, cloud metadata endpoints, or third-party hosts, and bind listening sockets that accept inbound connections. No authentication or user interaction is required because the sandbox itself is the trust boundary being violated.

javascript
// Patch from lib/builtin.js (vm2 v3.11.4)
// SECURITY (GHSA-r9pm-gxmw-wv6p): Underscored builtins (_http_client,
// _http_server, _http_agent, _http_common, _http_incoming, _http_outgoing,
// _tls_common, _tls_wrap, _stream_*) are Node's private implementation
// modules backing http/https/tls/streams. They are listed by
// `require('module').builtinModules` but are not documented public API and
// expose network primitives directly (`_http_client.ClientRequest`,
// `_http_server.Server`). Filtering them at the `BUILTIN_MODULES` source
// removes them from `'*'` wildcard expansion, so the documented
// `builtin: ['*', '-http', '-https', '-net', '-tls', ...]` pattern is
// once again coherent.
const BUILTIN_MODULES = (nmod.builtinModules || Object.getOwnPropertyNames(process.binding('natives')))
	.filter(s=>!s.startsWith('internal/') && !s.startsWith('_') && !isDangerousBuiltin(s));

Source: vm2 commit 436053e

Detection Methods for CVE-2026-47139

Indicators of Compromise

  • Sandboxed Node.js processes making outbound TCP connections to internal RFC1918 ranges, cloud metadata IPs (169.254.169.254), or unexpected external hosts
  • Process command lines or audit logs showing require('_http_client'), require('_http_server'), or other underscored builtin loads inside vm2-hosted code
  • Unexpected listening sockets bound by a process that should only run untrusted user code
  • vm2 package versions earlier than 3.11.4 in package-lock.json or node_modules inventories

Detection Strategies

  • Inventory Node.js applications for vm2 dependencies using software composition analysis and flag versions below 3.11.4
  • Inspect application logs and runtime traces for require() calls referencing underscored module names from sandboxed contexts
  • Correlate egress network telemetry from Node.js workers against expected destination allowlists to surface SSRF attempts

Monitoring Recommendations

  • Forward Node.js process telemetry, network flows, and package inventory into a centralized analytics platform for retrospective hunts against the underscored-builtin pattern
  • Enable runtime monitoring on hosts executing untrusted code to capture child process and socket creation events
  • Alert on first-seen outbound destinations from vm2-hosted services and on any bind to a listening port from those processes

How to Mitigate CVE-2026-47139

Immediate Actions Required

  • Upgrade vm2 to version 3.11.4 or later across all Node.js services that import it
  • Audit NodeVM configurations that use the ['*', '-http', ...] exclusion pattern and treat any pre-patch deployment as exposed
  • Restrict outbound network egress from processes that execute untrusted code using host or network-level policy
  • Rotate any credentials or tokens that were reachable from a vm2-hosted process during the exposure window

Patch Information

The fix is published in vm2 release v3.11.4. The patch in lib/builtin.js extends the filter applied to BUILTIN_MODULES to exclude any module name beginning with an underscore from wildcard expansion. Explicit opt-in via builtin: ['_http_client'] and mock/override registrations remain functional. Technical details are documented in GHSA-r9pm-gxmw-wv6p.

Workarounds

  • Explicitly deny underscored modules in the NodeVMbuiltin option by listing them as negated entries until the upgrade is applied
  • Run vm2-hosted workloads inside a network namespace or container with default-deny egress policies and no access to internal services or cloud metadata
  • Replace vm2 with an out-of-process isolation boundary such as a separate container or microVM where the threat model requires strong sandboxing
bash
# Upgrade vm2 to the patched release
npm install vm2@3.11.4 --save
npm audit

# Verify resolved version in the lockfile
grep -A1 '"vm2":' package-lock.json | head -n 20

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.