Jul 25, 2026·7 min read·5 visits
Unauthenticated remote attackers can permanently freeze py-libp2p connections using a single 12-byte Yamux frame header claiming a 4 GB payload without sending the data, triggering an indefinite read block.
A critical connection-level Denial of Service (DoS) vulnerability exists in the Yamux stream multiplexer implementation of py-libp2p (versions <= 0.6.0). The flaw allows unauthenticated or authenticated peers to permanently stall a Yamux multiplexed connection by transmitting a single malformed 12-byte header claiming an oversized payload while withholding the payload bytes.
The Yamux (Yet Another Multiplexer) stream multiplexing protocol is designed to split a single bidirectional connection into multiple logical streams. In py-libp2p, this is negotiated as the primary multiplexer during the initialization of standard transport connections, such as those secured by the Noise protocol. The multiplexer acts as an intermediate framing layer, processing all raw reads and writes over the secured TCP socket before distributing payload data to individual high-level application streams.\n\nBecause the multiplexer manages the underlying transport socket, any processing disruption at this layer immediately degrades the security and reliability of the entire communication pipe. A remote unauthenticated peer that completes the initial transport handshake can directly interface with the Yamux connection-processing loop. This provides an attack surface where malformed frames can affect the runtime state of the reader coroutine.\n\nThe vulnerability identified as GHSA-HMJ8-5XMH-5573 represents a failure in the input validation and flow control mechanisms of the Yamux layer in py-libp2p versions up to and including 0.6.0. By exploiting this vulnerability, an attacker can trigger an uncontrolled resource consumption condition, classified under CWE-400. This causes a complete, permanent loss of availability for the affected network connection.
The root cause of this vulnerability lies in the sequential execution design of the handle_incoming coroutine combined with a lack of validation on the length metadata field parsed from incoming Yamux frames. In py-libp2p, the connection reader coroutine handles frame processing inside an infinite loop that sequentially parses 12-byte Yamux headers. When decoding a frame of type TYPE_DATA, the reader extracts a 32-bit unsigned integer representing the payload length and attempts to read that exact number of bytes from the decrypted stream.\n\nThe vulnerability is triggered because the application processes the length parameter before performing any checks on the active stream's context, bounds, or availability. Specifically, the implementation fails to verify whether the claimed payload size is consistent with the established Yamux receive window, which is governed by the DEFAULT_WINDOW_SIZE parameter of 256 kilobytes. Section 3.3 of the official Yamux protocol specification dictates that a receiver must validate this window constraint and abort connection handling if the limit is exceeded.\n\nFurthermore, the coroutine executes the blocking function read_exactly(self.secured_conn, length) directly on the main event loop. If an attacker declares a frame length of up to 4 gigabytes but terminates transmission immediately after sending the initial 12-byte header, the read_exactly coroutine will block indefinitely while awaiting the missing payload bytes. Because there are no default read timeout boundaries, and because the connection reader processes frames in a strictly sequential manner, the entire event-driven reader task is starved, freezing all current and future multiplexed streams on that socket.
The structural weakness in the vulnerable implementation can be traced directly to libp2p/stream_muxer/yamux/yamux.py within the handle_incoming loop. In the unpatched versions, the code processes the incoming frame header and immediately invokes read_exactly without validation or safety guards:\n\npython\n# Vulnerable code path in py-libp2p <= 0.6.0\nelif typ == TYPE_DATA:\n try:\n data = (\n await read_exactly(self.secured_conn, length)\n if length > 0\n else b""\n )\n\n\nThe patch implemented in commit 146ea87d1a20cc7dacf684ecf7c204543be04b37 introduces a dual-layered control model. First, it enforces a hard flow-control boundary against the incoming length field, comparing it to MAX_WINDOW_SIZE. Any frame exceeding this size results in an immediate connection termination and cleanup. Second, it wraps the read operation inside an asynchronous helper _read_data_body(length) that relies on a Trio-based timeout cancellation scope:\n\npython\n# Patched validation block in yamux.py\nif typ == TYPE_DATA and length > MAX_WINDOW_SIZE:\n logger.warning(\n f"Yamux: peer {self.peer_id} announced oversized DATA "\n f"frame length={length} (> MAX_WINDOW_SIZE="\n f"{MAX_WINDOW_SIZE}) on stream {stream_id} "\n f"closing connection"\n )\n self.event_shutting_down.set()\n await self._cleanup_on_error()\n break\n\n\nThe underlying read is now protected by a configurable timeout boundary (defaulting to 60 seconds), catching any trio.TooSlowError and cleanly closing the underlying socket connection:\n\npython\nasync def _read_data_body(self, length: int) -> bytes:\n \"\"\"Read a DATA frame's length-byte body, bounded by timeout\"\"\"\n with trio.fail_after(_yamux_data_read_timeout()):\n return await read_exactly(self.secured_conn, length)\n
Exploitation of this vulnerability is straightforward and requires minimal attacker effort. The attack does not require any credentials or administrative privileges, as the Yamux negotiation occurs immediately following the automated transport security handshake. To execute the attack, an adversary must establish a standard TCP connection with a node running the vulnerable version of py-libp2p and successfully complete the initial Noise handshake.\n\nOnce the connection is upgraded to a secure channel, the attacker negotiates the Yamux multiplexer and transmits a malicious 12-byte header. The header contains a frame type of 0x00 (representing TYPE_DATA) and a length field set to the maximum 32-bit unsigned integer value of 0xFFFFFFFF (approximately 4 gigabytes). The stream identifier used in the header can be completely arbitrary, as the victim's connection reader processes and blocks on the payload read before verifying whether the target stream actually exists.\n\nmermaid\ngraph LR\n A["Attacker Node"] -->|Noise Handshake| B["Secured Connection"]\n B -->|12-Byte Header Length = 4GB| C["Yamux Reader Loop"]\n C -->|read_exactly 4GB| D["Indefinite Block"]\n\n\nAfter transmitting these 12 bytes over the encrypted channel, the attacker simply halts all network traffic on the socket. The victim's daemon reads the header, registers the 4-gigabyte length requirement, and calls read_exactly, suspending the coroutine indefinitely. This causes a permanent stall of the entire multiplexer connection, effectively terminating all active streams without consuming significant attacker bandwidth.
The security impact of GHSA-HMJ8-5XMH-5573 is classified as high, focusing exclusively on a total loss of connection availability. A single malicious connection frame can permanently block the reader loop, which silences all other active sub-streams sharing the multiplexed TCP socket. For nodes executing mission-critical decentralized services, such as peer-to-peer data syncing, transaction dissemination, or routing, this results in immediate node isolation.\n\nThe asymmetric nature of this attack represents a substantial vector for service disruption. An attacker can disable hundreds of active inbound and outbound peer connections simultaneously by utilizing only a few kilobytes of bandwidth. Because the default configuration of py-libp2p's new_host() helper instantiates connections with the Noise-secured Yamux muxer active by default, almost all default deployments are vulnerable to this attack out of the box.\n\nThe CVSS v3.1 base score for this vulnerability is assessed at 7.5, reflecting a Network attack vector with Low complexity, No privileges required, No user interaction, and a High impact on Availability. Additionally, because the vulnerability exists entirely within the stream reader loop, it does not lead to remote code execution, memory corruption, or information disclosure. The threat remains restricted to Denial of Service (DoS).
Because no formal package version was tagged in the Python Package Index (PyPI) following the remediation of this flaw, developers and operators must integrate the security patch directly from the source repository. The fix is located in commit 146ea87d1a20cc7dacf684ecf7c204543be04b37. Organizations running deployments built on top of py-libp2p must vendor this commit or manually apply the changes to libp2p/stream_muxer/yamux/yamux.py.\n\nIn environments where immediate source-code remediation is not feasible, security engineers can enforce transport-layer mitigations. A short-term workaround is to deploy network-layer monitoring tools or proxy wrappers that actively tear down inactive TCP sessions. Operators can also adjust the runtime environment variable PY_YAMUX_DATA_READ_TIMEOUT to a conservative threshold, such as 5.0 seconds, to ensure that stalled reads are quickly garbage-collected:\n\nbash\nexport PY_YAMUX_DATA_READ_TIMEOUT=\"5.0\"\n\n\nDetection of exploitation attempts can be automated by analyzing system logs for specific warnings generated by the patched codebase. Patched systems will log occurrences where a peer announces an oversized frame or when a read-body operation times out. Intrusion Detection Systems (IDS) can be configured with signatures to detect the negotiation of oversized Yamux frames on unencrypted transport layers, though this is less effective once the Noise protocol encryption layer has been fully established.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
py-libp2p libp2p | <= 0.6.0 | commit 146ea87 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 7.5 (High) |
| Exploit Status | PoC (Proof of Concept) |
| KEV Status | Not Listed |
| Impact | Denial of Service (DoS) |
The software does not properly limit or throttle the allocation of internal resources when processing incoming network-facing headers.
A prototype pollution vulnerability (CWE-1321) in the Quasar framework's utility function 'extend' allows unauthenticated remote attackers to modify the global Object.prototype. This vulnerability can lead to application-level logic bypasses, denial of service, or potentially arbitrary code execution depending on downstream implementation.
An unauthenticated remote resource exhaustion vulnerability in Amazon aws-smithy-http-server enables denial-of-service (DoS) attacks. Affected versions do not enforce connection limits or header timeouts, allowing standard Slowloris techniques to block server operations.
An authorization bypass vulnerability in the gRPC Watch API of etcd allows low-privileged users to read keys outside of their authorized range. By utilizing an open-ended range request sentinel, the input is prematurely normalized before RBAC validation, misclassifying a range watch as a single-key point query.
An argument injection vulnerability in the AWS Bedrock AgentCore Python SDK allows authenticated users to execute arbitrary commands inside the Code Interpreter sandbox container via crafted Python package specifiers containing shell metacharacters.
A NULL pointer dereference vulnerability was discovered in the getkin/kin-openapi Go library. When parsing incoming request parameters that are validated against a content map with an empty media type, the openapi3filter request validation engine attempts to resolve an uninitialized schema pointer. This results in an unhandled Go runtime panic and process termination, yielding an unauthenticated, remote Denial of Service vector.
A Server-Side Request Forgery (SSRF) vulnerability in FrontMCP allows unauthenticated remote attackers to query internal network services by exploiting the un-guarded background OpenAPI specification polling mechanism.