CVE-2026-92952

mediumpriority · track · 27EPSS 0.005 · 40 %ileDERUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.46% (40th) EPSS
CVSS 6.8 · medium
Is it mine?
4 products
Check your exposure
Who says so?
3 sources
fstec_bdu · cert_bund · osv

EPSS · scored 2026-10-27

vm2 versions 3.11.4 through 3.11.6 incompletely filter Node.js registered internal symbols across the sandbox boundary. The extraction filters in lib/setup-sandbox.js and the cross-realm symbol checks and write traps in lib/bridge.js use a fixed list of known dangerous registered symbols that omits nodejs.stream.disturbed and nodejs.stream.errored, which are exposed on host WebStream prototypes on newer Node.js releases (validated on Node.js v25.8.0). When the embedder exposes a host WebStream object and the host stream/web module to the sandbox, sandbox code can obtain the real host symbols via Object.getOwnPropertySymbols(streamWeb.ReadableStream.prototype) and use them as write keys on host stream objects, corrupting host-visible stream state — for example making stream.Readable.isDisturbed() return false for an already-consumed stream. This can bypass host logic that relies on Node's public stream-state helpers to enforce one-shot body consumption, reject errored streams, or decide whether a stream is safe to hand to another component. It is not a host code-execution primitive in the reported proof of vulnerability. This is an incomplete fix for the earlier nodejs.* symbol filtering issue. Fixed in vm2 3.11.7.

01

cvss

Technical severity, 0–10. One row per scoring source.

8.9
010

base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · medium–high · 6.3–8.9

v3.16.8mediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:Nprimary · disclosure@vulncheck.comexploitability 2.2 · impact 4
v3.16.8mediumAV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:Nsecondary · FSTEC (BDU) RU
v3.16.8mediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:Nsecondary · OSV
v4.08.9highCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/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:Xsecondary · disclosure@vulncheck.com
v4.06.3mediumCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/E:U/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:Xsecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (4)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • WID-SEC-W-2026-2997vm2: Mehrere SchwachstellenDeutsch2026-08-24csaf · cert_bundDE

    Ein Angreifer kann mehrere Schwachstellen in vm2 ausnutzen, um Sicherheitsvorkehrungen zu umgehen, um Informationen offenzulegen, um Daten zu manipulieren, um einen Denial of Service Angriff durchzuführen und um beliebigen Programmcode auszuführen.

  • GHSA-jf8q-945g-9q4cvm2: Incomplete nodejs.* symbol filtering lets sandbox override host WebStream state checks2026-10-01ghsa · osvUS

    ## Summary vm2 current head (`v3.11.5`, commit `7a1f5100b96f48d34e0fe104ab37c0acc5944f92`) still exposes registered Node.js internal symbols from host WebStream prototypes to sandbox code. The prior `nodejs.*` symbol hardening blocks `Symbol.for('nodejs.<name>')` at the source, but the extraction filters and bridge write traps still enumerate a fixed set of known registered symbols. On Node.js `v25.8.0`, `stream/web` exposes two additional registered symbols: - `nodejs.stream.disturbed` - `nodejs.stream.errored` Sandbox code can extract those real host symbols with `Object.getOwnPropertySymbols(streamWeb.ReadableStream.prototype)` and then use them as write keys on host objects. On a real host `ReadableStream`, an attacker can make `stream.Readable.isDisturbed(stream)` return `false` after the stream has already been read. ## Technical Details `lib/setup-sandbox.js` correctly blocks future `nodejs.*` keys at the `Symbol.for()` source: ```js if (apply(localStringStartsWith, keyStr, ['nodejs.'])) { ... return fresh; } ``` However, the extraction filters are still driven by a fixed `realDangerousSymbols` list. That list does not include `nodejs.stream.disturbed` or `nodejs.stream.errored`, so `Object.getOwnPropertySymbols()` and related paths can still return those real host symbols. `lib/bridge.js` has the same fixed-list problem in `isDangerousCrossRealmSymbol()` and in the host-result scrub list. Because the two new symbols are not recognized, the `set` and `defineProperty` traps allow sandbox-originated writes using those keys. Current-head source references: - `lib/setup-sandbox.js:180-196` denies `Symbol.for('nodejs.*')` by namespace. - `lib/setup-sandbox.js:214-230` uses a fixed `realDangerousSymbols` list for extraction filtering; the two reported symbols are absent. - `lib/bridge.js:187-199` uses a fixed `isDangerousCrossRealmSymbol()` list; the two reported symbols are absent. - `lib/bridge.js:1499-1509` treats the write trap as the last line of defense, but it only rejects keys recognized by that fixed list. ## Impact Sandbox code can corrupt host-visible WebStream state checks for host WebStream objects that cross into the sandbox. In the validated PoV, a stream that the host has already consumed is made to appear undisturbed to `stream.Readable.isDisturbed()`. This can bypass host logic that relies on Node's public stream-state helpers to enforce one-shot body consumption, reject errored streams, or decide whether a host WebStream is safe to hand to another component. This is not a host-code-execution primitive in the current PoV. The report is an incomplete-fix / guard-coverage gap in the same symbol-boundary family as the prior `nodejs.*` symbol advisory. ## Affected Package/Versions Confirmed affected on Node.js `v25.8.0`: - `v3.11.4` - `v3.11.5` - current head `7a1f5100b96f48d34e0fe104ab37c0acc5944f92` `v3.11.3` is also affected, but it predates the broader `nodejs.*` symbol fix. For this incomplete-fix report, the suggested affected range is `>= 3.11.4, <= 3.11.5` on Node.js versions where these stream symbols exist. No patched version is known. ## Configuration Required The PoV uses a `VM` where the embedder exposes a host WebStream object and the host `stream/web` module object to sandbox code: ```js const vm = new VM({ sandbox: { rs, streamWeb } }); ``` This matches the same trust boundary as the earlier cross-realm symbol-write class: sandbox code must not be able to obtain registered Node.js internal symbols and write them back onto host objects. The PoV is local-only. It does not require network access, a public target, `NodeVM` builtin access, `process`, filesystem access, or child-process access. ## Local Proof of Concept Run from the `oss-zero-day-harness` directory: ```sh node submission-bundle/vm2-pov-test-incomplete-nodejs-stream-symbol-filter/pov-nodejs-stream-symbol-incomplete-fix.js ``` The PoV: 1. The host creates a `ReadableStream`. 2. The host reads one chunk so `stream.Readable.isDisturbed(rs)` is `true`. 3. Sandbox code confirms `Symbol.for('nodejs.stream.disturbed')` is blocked and returns a sandbox-local symbol. 4. Sandbox code extracts the real registered `nodejs.stream.disturbed` / `nodejs.stream.errored` symbols from the host `ReadableStream.prototype`. 5. Sandbox code writes an own `nodejs.stream.disturbed` property onto the host stream with value `false`. 6. The host calls `stream.Readable.isDisturbed(rs)` again and receives `false`. Observed result on current head: ```json { "beforeHostDisturbed": true, "controls": { "symbolForDisturbedIsRegistered": false, "symbolForErroredIsRegistered": false }, "extracted": [ { "description": "nodejs.stream.disturbed", "keyFor": "nodejs.stream.disturbed" }, { "description": "nodejs.stream.errored", "keyFor": "nodejs.stream.errored" } ], "overrideDisturbedOk": true, "afterHostDisturbed": false } ``` ## Official Disclosure Policy Fit vm2's `SECURITY.md` asks reporters not to create a public issue and to submit It asks for reproduction steps, affected versions, environment/configuration details, and potential impact: - Policy: https://github.com/patriksimek/vm2/blob/main/SECURITY.md - Private report route: https://github.com/patriksimek/vm2/security/advisories/new This bundle is formatted for that private GitHub report flow and should not be posted publicly before maintainer triage and a fixed release. ## Suggested Fix Direction Make the dangerous-symbol checks namespace-based instead of list-based: - In `setup-sandbox.js`, make `isDangerousSymbol(sym)` return true for any registered symbol whose `Symbol.keyFor(sym)` starts with `nodejs.`. - In `bridge.js`, make `isDangerousCrossRealmSymbol(key)` do the same for any symbol key crossing the bridge. - In host-result scrubbing, delete all own symbol keys whose registered key starts with `nodejs.` instead of iterating a hard-coded list. - Keep the current explicit list only as regression documentation, not as the complete security boundary. Regression tests should include: - `Object.getOwnPropertySymbols(ReadableStream.prototype)` must not expose `nodejs.stream.disturbed` or `nodejs.stream.errored`. - A sandbox-local `Symbol.for('nodejs.stream.disturbed')` write must not affect host `stream.Readable.isDisturbed()`. - Even if the real symbol is passed into the sandbox by a host test harness, `set`, `defineProperty`, and `deleteProperty` traps must reject writes and deletes against host objects. ## Why This Is Not Intended Behavior The hardening comments and tests establish the intended invariant: - any `nodejs.*` internal symbol should be sandbox-local when requested through `Symbol.for()`; - dangerous registered symbols should not be enumerable/extractable from host objects; - even if a sandbox obtains one, bridge write traps should reject writes using that key. This report shows that the source-side rule is active, but the extraction and write-trap rules are incomplete for newer registered `nodejs.stream.*` symbols. Node's stream state helpers consult these symbols directly. For example, `stream.Readable.isDisturbed()` reads the internal disturbed symbol before falling back to public state. After sandbox writes an own property under the extracted symbol, the host helper returns attacker-controlled state. Node's public documentation describes `stream.isErrored(stream)` as reporting whether a stream has encountered an error, and `stream.Readable.isDisturbed(stream)` as reporting whether the stream has been read from or cancelled: - https://nodejs.org/api/stream.html#streamiserroredstream - https://nodejs.org/api/stream.html#streamreadableisdisturbedstream

06

also known as (1)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-09-17modified 2026-09-22
08

references (2) nvd

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan