CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-109932

CVE-2026-109932: Unbounded Memory Consumption via WebSocket PING Frame Flood in Node.js undici

Alon Barad
Alon Barad
Software Engineer

Oct 28, 2026·10 min read·2 visits

Executive Summary (TL;DR)

Node.js undici versions 5.15.0 through 6.29.0, 7.0.0 through 7.30.0, and 8.0.0 through 8.12.1 fail to check socket write backpressure when responding to WebSocket PING control frames. A malicious WebSocket server can flood a connected client with 2-byte PING frames while stalling TCP reads, causing unbounded memory growth in the client's outbound socket buffer and terminating the Node.js process via an out-of-memory error.

An uncontrolled resource consumption vulnerability in the WebSocket client implementation of Node.js undici allows a remote WebSocket server to trigger an out-of-memory (OOM) crash in the client process. The vulnerability stems from the failure of the incoming frame parser (ByteParser) to respect socket write backpressure when automatically emitting PONG responses to incoming PING control frames. By continuously streaming small 2-byte PING frames while refusing to read from the TCP connection, a malicious server forces the client to queue outbound PONG frames in the V8 heap without bound.

Vulnerability Overview

undici is the high-performance HTTP/1.1 and HTTP/2 client written from scratch for Node.js. In addition to standard HTTP dispatching, undici provides a specification-compliant WebSocket client implementation that backs the global WebSocket API (globalThis.WebSocket) exposed natively in modern Node.js runtimes. Consequently, vulnerabilities within undici's WebSocket subsystem affect both applications that import the undici package directly and applications relying on the built-in Node.js WebSocket client.

CVE-2026-109932 (also tracked as GitHub Security Advisory GHSA-9fm3-97px-rjf6) is an uncontrolled resource consumption vulnerability (CWE-400) and asymmetric resource amplification flaw (CWE-405) located in undici's WebSocket frame receiver (lib/web/websocket/receiver.js). When a client establishes a WebSocket connection to an untrusted or compromised server, the client parses incoming frames through the ByteParser class, which extends Node.js's stream.Writable abstraction.

Under RFC 6455 (The WebSocket Protocol), an endpoint receiving a PING control frame (opcode 0x9) must respond promptly with a PONG control frame (opcode 0xA). In vulnerable versions of undici, ByteParser automatically constructs and writes these PONG responses directly to the underlying transport socket without checking if the socket's write buffer is experiencing backpressure.

If the remote WebSocket server stops reading data from its TCP receive buffer while simultaneously flooding the client with minimal 2-byte PING frames, the client's outbound socket write queue grows without limit. Because ByteParser does not pause incoming frame ingestion while awaiting the socket 'drain' event, the V8 heap and Node.js internal stream buffers accumulate millions of pending write requests until the Node.js process terminates with an out-of-memory (OOM) error.

Root Cause Analysis

The root cause of CVE-2026-109932 resides in how ByteParser coordinates state transitions between its readable input loop and the writable state of the underlying net.Socket or tls.TLSSocket instance. In Node.js stream architectures, writing data to a duplex socket via socket.write(chunk) sends data to the operating system's kernel TCP send buffer. If the remote peer stops acknowledging packets or advertises a TCP ZeroWindow, the kernel send buffer fills up. Once the queued data exceeds the stream's highWaterMark, socket.write() returns false and sets the boolean property socket.writableNeedDrain to true, signaling that the caller must cease writing until the 'drain' event is emitted.

In lib/web/websocket/receiver.js, ByteParser processes incoming chunks inside its run(callback) method, which executes a while (this.#loop) iteration. When a frame header and payload are fully read, ByteParser checks whether the opcode represents a control frame (PING, PONG, or CLOSE) using isControlFrame(this.#info.opcode). If the frame is a control frame, it invokes this.parseControlFrame(body) and resets this.#state = parserStates.INFO.

Inside parseControlFrame(body), receiving a PING frame triggers the creation of an outbound WebsocketFrameSend instance containing the PONG response, which is immediately written to this.#handler.socket. However, neither parseControlFrame() nor the caller loop in run() checks the return value of socket.write() or inspects this.#handler.socket.writableNeedDrain. Instead, this.#loop remains true, and ByteParser immediately moves to the next iteration to parse the subsequent PING frame from the inbound buffer.

This creates a severe resource asymmetry (CWE-405). On the wire, an unmasked server-to-client PING frame with an empty payload occupies exactly 2 octets: 0x89 (FIN bit set, opcode 0x9) followed by 0x00 (Mask bit unset, payload length 0). For every 2 bytes transmitted by the server, the client allocates a new masked client-to-server PONG frame buffer (at least 6 bytes on the wire due to the mandatory 4-byte masking key), along with V8 heap objects, Buffer slice descriptors, and linked-list queue nodes inside socket._writableState.buffered. Because input parsing is never suspended, the client converts a low-bandwidth inbound stream into unbounded memory allocation.

Code Analysis

The vulnerability is located in lib/web/websocket/receiver.js within the ByteParser class. Prior to the patch, the control frame handling branch inside ByteParser.prototype.run executed parseControlFrame and immediately continued the while (this.#loop) parsing cycle regardless of the transport socket's outbound buffer state.

// Vulnerable code in lib/web/websocket/receiver.js
if (isControlFrame(this.#info.opcode)) {
  this.#loop = this.parseControlFrame(body)
  this.#state = parserStates.INFO
  // BUG: No check on this.#handler.socket.writableNeedDrain.
  // The while(this.#loop) loop continues immediately, parsing more PINGs
  // and queuing infinite PONGs even when the socket cannot flush data.
} else {
  if (!this.#info.compressed) {
    if (!this.writeFragments(body)) {
      // Data frames handled flow control, but control frames did not.

In commits b217373bb024cd93f85eb5b3ffd25684855f60f8, 39f93e142df03e559fe7c3d1fc7fba9beee2c185, and 53944624462b6797560288c55dbd8c66857bb495, the maintainers patched ByteParser by inspecting this.#handler.socket.writableNeedDrain immediately after this.parseControlFrame(body) returns.

File: lib/web/websocket/receiver.js
@@ -252,6 +252,19 @@ class ByteParser extends Writable {
         if (isControlFrame(this.#info.opcode)) {
           this.#loop = this.parseControlFrame(body)
           this.#state = parserStates.INFO
+
+          // If the automatic PONG/CLOSE reply filled the socket's write buffer,
+          // stop parsing and resume on drain so a PING flood cannot grow the
+          // outbound queue without bound.
+          if (this.#loop && this.#handler.socket.writableNeedDrain) {
+            this.#loop = false
+            this.#handler.socket.once('drain', () => {
+              if (this.destroyed) return
+              this.#loop = true
+              this.run(callback)
+            })
+            return
+          }
         } else {
           if (!this.#info.compressed) {
             if (!this.writeFragments(body)) {

The fix introduces three critical synchronization steps when this.#loop is true and this.#handler.socket.writableNeedDrain evaluates to true:

  1. Loop Suspension: It sets this.#loop = false and returns early from run(callback) without invoking callback(). Because run is invoked by the Writable._write(chunk, encoding, callback) method of ByteParser, withholding the completion callback propagates backpressure upstream to the readable side of the socket, halting further reads from the OS network stack.
  2. Drain Listener Registration: It registers a one-time event listener via this.#handler.socket.once('drain', ...) to wait until the kernel TCP send buffer flushes and the outbound queue drains below its highWaterMark.
  3. Safe Resumption: When 'drain' fires, the callback verifies that the parser has not been destroyed (if (this.destroyed) return), sets this.#loop = true, and re-invokes this.run(callback) to process any remaining bytes in the current chunk before finally signaling readiness for more inbound data.

> [!NOTE] > Fix Completeness Assessment: The patch checks writableNeedDrain for all control frames (PING, PONG, and CLOSE) processed by parseControlFrame(body). Because CLOSE frames only trigger a single reciprocal CLOSE response before terminating the state machine (this.#loop becomes false), and PONG frames do not generate automatic replies, halting on writableNeedDrain when this.#loop is true effectively covers the PING flood vector without introducing listener leaks on connection teardown.

Exploitation

Exploiting CVE-2026-109932 requires a target Node.js application using an affected version of undici (or Node.js's built-in globalThis.WebSocket) to initiate a WebSocket connection to an attacker-controlled or compromised server endpoint. This scenario commonly occurs in server-side applications that connect to external WebSocket feeds (such as cryptocurrency market tickers, webhook relays, federated messaging protocols, or remote telemetry endpoints) or applications that accept user-supplied WebSocket URLs.

Once the client initiates the HTTP Upgrade: websocket handshake, the malicious server completes the RFC 6455 handshake by returning an HTTP/1.1 101 Switching Protocols response with the computed Sec-WebSocket-Accept header. At this point, the target Node.js process instantiates ByteParser and pipes the underlying TCP/TLS socket into it.

To trigger the memory exhaustion condition, the server executes a two-part asymmetric attack:

  1. Induce Outbound Backpressure: The server stops calling recv() or read() on the accepted socket descriptor (or sets SO_RCVBUF to a minimal value and pauses reading). Once the client transmits enough PONG data to fill the server's OS TCP receive window and the client's local OS TCP send buffer, the client socket transitions to writableNeedDrain = true.
  2. Stream 2-Byte PING Frames: Simultaneously, the server continuously writes buffers filled with repeated 0x89 0x00 byte pairs over the socket.
// Conceptual demonstration based on test/websocket/pong-backpressure.js
// A single unmasked server-to-client PING frame with 0-length payload:
const serverPingFrame = Buffer.from([0x89, 0x00])
 
// Concatenating thousands of 2-byte PING frames into a single TCP payload:
const payload = Buffer.concat(Array.from({ length: 10000 }, () => serverPingFrame))

As documented in the regression test test/websocket/pong-backpressure.js included in the patch commit, when a blocked socket (writableNeedDrain === true) receives a batch of 50 consecutive PING frames (100 bytes total), a patched client halts after writing exactly 1 PONG frame (socket.pongWrites === 1) and pauses parsing until socket.drain() is emitted. In contrast, an unpatched client parses all 50 frames immediately and queues 50 outbound writes. Over a sustained network stream, sending megabytes of packed 0x89 0x00 sequences causes the unpatched client to allocate gigabytes of heap memory in seconds, crashing the Node.js runtime with FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory.

Impact Assessment

The primary impact of CVE-2026-109932 is a complete Denial of Service (DoS) of the Node.js application process via memory exhaustion. Because Node.js executes JavaScript on a single-threaded event loop within a shared V8 isolate by default, exhausting the V8 heap or system RSS causes the entire process to abort immediately. Any concurrent HTTP requests, background jobs, or unrelated WebSocket connections handled by that same Node.js process are terminated abruptly.

The vulnerability carries a CVSS v3.1 base score of 5.9 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H:

  • Attack Vector (AV:N): Exploitation occurs over the network via standard TCP/TLS WebSocket traffic.
  • Attack Complexity (AC:H): The client must actively connect to a malicious or compromised WebSocket server, and the server must maintain connection state while deliberately stalling TCP reads to induce socket backpressure.
  • Privileges Required (PR:N) & User Interaction (UI:N): No authentication or interactive user actions are required if the application connects to the endpoint automatically or via unauthenticated API triggers.
  • Availability Impact (A:H): Total loss of availability for the target process due to out-of-memory termination, while Confidentiality (C:N) and Integrity (I:N) remain unaffected.

From a threat intelligence perspective, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-109932 a score of 0.00321 (0.32%), placing it in the 24.94th percentile. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no public weaponized exploit modules currently exist in ExploitDB or Metasploit.

Remediation

To remediate CVE-2026-109932, organizations must upgrade the undici package to a patched release that enforces socket drain backpressure during WebSocket control frame processing. The OpenJS Foundation and undici maintainers have released fixes across all supported major branches:

  • undici 5.x and 6.x: Upgrade to version 6.29.1 or later (note that 5.x users must upgrade to the 6.x release line starting at 6.29.1).
  • undici 7.x: Upgrade to version 7.30.1 or later.
  • undici 8.x: Upgrade to version 8.12.2 or later.

For applications that do not declare undici in package.json directly but instead utilize the native globalThis.WebSocket client built into the Node.js runtime, administrators must update the Node.js runtime binary to a release that bundles a patched version of undici (>= 6.29.1, >= 7.30.1, or >= 8.12.2).

> [!WARNING] > There are no configuration-based workarounds within undici to disable automatic PONG responses or enforce control frame backpressure on unpatched versions. Until patches are applied, applications should restrict outbound WebSocket connections Strictly to trusted, verified endpoints and avoid connecting to arbitrary user-supplied ws:// or wss:// URIs.

Security and operations teams can verify whether vulnerable versions of undici are present in their dependency trees using standard package manager inspection commands:

# Inspect installed undici versions across the dependency tree
npm ls undici
 
# Run automated vulnerability auditing
npm audit

In production environments, defenders should also configure process-level memory telemetry (monitoring process.memoryUsage().heapUsed and socket.writableLength on active WebSocket clients) and enforce outbound egress filtering so backend services cannot establish WebSocket sessions to untrusted external hosts.

Official Patches

Node.js / undiciGitHub Security Advisory GHSA-9fm3-97px-rjf6
Node.js / undiciundici Release v6.29.1
Node.js / undiciundici Release v7.30.1
Node.js / undiciundici Release v8.12.2

Fix Analysis (3)

Technical Appendix

CVSS Score
5.9/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.32%
Top 75% most exploited

Affected Systems

Node.js undici (versions 5.15.0 through 6.29.0)Node.js undici (versions 7.0.0 through 7.30.0)Node.js undici (versions 8.0.0 through 8.12.1)Node.js runtime environments utilizing bundled undici for globalThis.WebSocket

Affected Versions Detail

Product
Affected Versions
Fixed Version
undici
Node.js / OpenJS Foundation
>= 5.15.0, <= 6.29.06.29.1
undici
Node.js / OpenJS Foundation
>= 7.0.0, <= 7.30.07.30.1
undici
Node.js / OpenJS Foundation
>= 8.0.0, <= 8.12.18.12.2
AttributeDetail
CWE IDCWE-400 (Uncontrolled Resource Consumption) / CWE-405 (Asymmetric Resource Consumption)
CVSS v3.1 Score5.9 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H)
Affected Componentundici WebSocket Client (ByteParser in lib/web/websocket/receiver.js)
Attack VectorNetwork (Malicious WebSocket server flooding 2-byte PING frames while stalling TCP reads)
Primary ImpactDenial of Service (Out-of-Memory crash of the Node.js runtime process)
Patched Versionsundici 6.29.1, 7.30.1, 8.12.2
EPSS Score0.32% (24.94th percentile)
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-400
Uncontrolled Resource Consumption

The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.

Vulnerability Timeline

Backpressure fix and regression tests committed to undici repository by Ulises Gascón and Matteo Collina
2026-10-14
Patched undici versions 6.29.1, 7.30.1, and 8.12.2 released
2026-10-15
GitHub Security Advisory GHSA-9fm3-97px-rjf6 and CVE-2026-109932 published via OpenJS Foundation CNA
2026-10-15
NVD publishes official CVSS v3.1 vector and CWE mappings
2026-10-19

References & Sources

  • [1]GitHub Security Advisory GHSA-9fm3-97px-rjf6
  • [2]CVE.org Record: CVE-2026-109932
  • [3]National Vulnerability Database (NVD): CVE-2026-109932
  • [4]OpenJS Foundation CNA Security Advisories
  • [5]GitHub Patch Commit (b217373bb024cd93f85eb5b3ffd25684855f60f8)
  • [6]GitHub Patch Commit (39f93e142df03e559fe7c3d1fc7fba9beee2c185)
  • [7]GitHub Patch Commit (53944624462b6797560288c55dbd8c66857bb495)
  • [8]undici Release v6.29.1
  • [9]undici Release v7.30.1
  • [10]undici Release v8.12.2

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 2 hours ago•GHSA-F897-62HF-6GXQ
7.5

GHSA-f897-62hf-6gxq: Unbounded Request Body and JSON-RPC Batch DoS in Model Context Protocol TypeScript SDK

The Model Context Protocol (MCP) TypeScript SDK contains an uncontrolled resource consumption vulnerability in its HTTP server transport implementations and middleware adapters. Affected versions fail to enforce upper bounds on incoming HTTP POST request body sizes and JSON-RPC 2.0 batch array lengths. In addition, middleware wrappers for Express and Hono parse request bodies into memory prior to validating Host and Origin headers. Unauthenticated remote attackers can exploit these flaws to trigger Out-Of-Memory (OOM) process crashes or monopolize the single-threaded Node.js event loop.

Amit Schendel
Amit Schendel
1 views•11 min read
•about 3 hours ago•CVE-2026-87029
5.9

CVE-2026-87029: Remote Denial of Service via Uncaught Exception in Node.js Undici Decompress Interceptor

An uncaught exception vulnerability (CWE-248) exists in the Node.js HTTP/1.1 client library undici, affecting versions 7.15.0 through 7.30.0 and 8.0.0 through 8.12.1. The flaw resides in the DecompressHandler interceptor (lib/interceptor/decompress.js), which processes compressed HTTP response streams using asynchronous Node.js zlib callbacks. When forwarding decompressed data chunks or stream completion events to downstream handlers, the interceptor fails to wrap the invocations of super.onResponseData() and super.onResponseEnd() in try...catch blocks. If a downstream interceptor or handler throws an exception during response processing—such as the built-in dump interceptor enforcing a maximum response size—the exception escapes the asynchronous zlib callback stack onto the top-level Node.js event loop, terminating the entire client process.

Alon Barad
Alon Barad
2 views•10 min read
•about 5 hours ago•CVE-2026-87018
7.5

CVE-2026-87018: Denial of Service via TLS 1.2 Renegotiation in Node.js Undici

CVE-2026-87018 (GHSA-qrxm-fqhp-jwvm) is a high-severity Denial of Service (DoS) vulnerability in undici, the default HTTP/1.1 and HTTP/2 client for Node.js that backs the global fetch() implementation. The flaw stems from reachable assertions in undici's socket and session error handlers, which assume that TLS hostname verification errors (ERR_TLS_CERT_ALTNAME_INVALID) cannot occur after the initial connection establishment. By initiating a post-handshake TLS 1.2 renegotiation and presenting a certificate with a mismatched Subject Alternative Name (SAN), a remote HTTPS server can trigger this assertion and terminate the entire Node.js runtime process via an uncaught AssertionError.

Amit Schendel
Amit Schendel
4 views•11 min read
•about 6 hours ago•CVE-2026-116682
8.6

CVE-2026-116682: Arbitrary File Read via Improper Symlink Resolution in FileBrowser Quantum

FileBrowser Quantum prior to versions 1.5.2-stable and 2.0.1-beta contains an improper link resolution vulnerability (CWE-59) in its file indexing and retrieval subsystems. When processing file read, download, preview, media streaming, OnlyOffice integration, or public share requests, the application resolves symbolic links via Go's filepath.EvalSymlinks without verifying that the canonical target path remains within the configured source root and the user's assigned access scope. Consequently, authenticated users with read access or unauthenticated holders of public share links can read arbitrary server-accessible files outside their designated directory boundaries.

Amit Schendel
Amit Schendel
5 views•10 min read
•about 7 hours ago•CVE-2026-116676
8.8

CVE-2026-116676: OS Command Injection via Terraform Workspace Parameter in Atlantis

CVE-2026-116676 (GHSA-rjh2-4xgw-p2hj) is an OS command injection vulnerability (CWE-78) in Atlantis, an open-source Go application that automates Terraform pull request workflows via webhooks. From version 0.4.0 prior to version 0.47.1, Atlantis constructed Terraform and Conftest commands by joining argument slices into a single space-separated string and passing them to a shell interpreter (`sh -c`). Because user-controlled workspace names from `atlantis.yaml`, pull request comments, HCL configurations, and API requests were insufficiently validated against shell metacharacters, an authenticated contributor eligible to trigger a Terraform plan can execute arbitrary commands under the Atlantis service account.

Amit Schendel
Amit Schendel
5 views•11 min read
•about 8 hours ago•CVE-2026-116679
5.4

CVE-2026-116679: Incorrect Authorization in m1k1o/neko Legacy WebRTC Input Handler

CVE-2026-116679 (GHSA-5cp7-fp4j-wwcv) is an incorrect authorization vulnerability (CWE-863) in m1k1o/neko, a self-hosted virtual browser running inside Docker via WebRTC. In versions from 3.0.0 prior to 3.1.5, a boolean logic inversion in the LegacyIsHost() method within server/internal/session/session.go causes the server to unconditionally grant host input privileges over the legacy WebRTC data channel when implicit hosting is enabled. Authenticated non-privileged room members can exploit this flaw to bypass room control locks and profile restrictions, allowing unauthorized injection of mouse and keyboard events into the shared virtual browser session.

Amit Schendel
Amit Schendel
3 views•8 min read