Aug 4, 2026·6 min read·4 visits
A vulnerability in the aiohttp WebSocket client allows malicious servers to bypass client configuration and force decompression of server-supplied payloads. This occurs because the parser improperly defaults to accepting compressed frames even when the permessage-deflate extension was not negotiated, allowing attackers to trigger a Denial-of-Service (DoS) condition via decompression bombs.
CVE-2026-59881 is a protocol compliance and input validation vulnerability in the client-side WebSocket implementation of the aiohttp asynchronous HTTP client/server framework for Python. Prior to version 3.14.2, the framework's parser unexpectedly accepts and attempts to decompress frames containing the RSV1 bit, even when the permessage-deflate extension has not been negotiated during the initial WebSocket handshake. This violation of RFC 6455 allows a malicious or compromised server to bypass client configuration, forcing decompression routines that can lead to high CPU and memory consumption, resulting in a denial-of-service condition.
CVE-2026-59881 is an input validation and protocol compliance vulnerability in the client-side WebSocket parsing architecture of the aiohttp framework. The defect exists within the frame-processing engine, which fails to correctly enforce RFC 6455 specifications governing the use of reserved bits. Specifically, the parser does not validate whether the RSV1 bit in incoming WebSocket headers has been mutually negotiated during the connection upgrade handshake.
According to RFC 6455, the RSV1 bit is reserved for extension negotiations, most notably the permessage-deflate compression protocol defined in RFC 7692. If this extension is not explicitly established, receiving a frame with RSV1 set to 1 represents an invalid state. The specification mandates that the receiver must fail the connection immediately with close code 1002 (Protocol Error) upon encountering this condition.
In affected versions of aiohttp, the client fails to reject these unauthorized frames. Instead, the frame parser accepts the payload and passes it directly to the internal zlib decompression routine. This defect exposes the client to unexpected, server-controlled decompression tasks, which can be leveraged by a malicious remote host to exhaust client system resources.
The root cause of CVE-2026-59881 lies in the default constructor configuration of the WebSocketReader class and its subsequent instantiation pattern. Located in aiohttp/_websocket/reader_py.py, the WebSocketReader is responsible for decoding bytes from the underlying TCP socket and assembling them into distinct WebSocket messages.
The class constructor signature was designed with a default value of True for its internal compress flag. This configuration assumed that decompression capability should be active unless explicitly deactivated by the caller. This default value created a fallback state that bypassed proper state verification during connection establishment.
During connection setup in aiohttp/client.py:_ws_connect, the client initiates the reader but omits the compress argument. Because of this omission, the initialized WebSocketReader operates with compress=True regardless of the negotiated handshake. When the client receives a frame with the RSV1 bit set, it reads the local _compress flag, finds it active, and executes decompression rather than throwing a Protocol Error as mandated by RFC 6455.
An analysis of the vulnerable code paths demonstrates the mechanics of the fallback error. In aiohttp/_websocket/reader_py.py, the vulnerable signature was declared as follows:
# Vulnerable implementation in aiohttp/_websocket/reader_py.py
def __init__(
self,
queue: WebSocketDataQueue,
max_msg_size: int,
compress: bool = True, # Defect: Defaults to True
decode_text: bool = True,
) -> None:
self._compress = compress
# ... parser state setup ...When the client-side connection helper _ws_connect initiated this parser, it did not explicitly supply the status of the compress variable derived from the handshake parameters:
# Vulnerable instantiation in aiohttp/client.py
# The local 'compress' variable is defined but not passed to the constructor
parser = WebSocketReader(reader, max_msg_size, decode_text=decode_text)The patch resolved this discrepancy by removing the default value from the WebSocketReader signature, rendering the configuration parameter mandatory:
# Patched implementation in aiohttp/_websocket/reader_py.py
def __init__(
self,
queue: WebSocketDataQueue,
max_msg_size: int,
compress: bool, # Fix: Default value removed
decode_text: bool,
) -> None:
self._compress = compressThe client upgrade routine was corrected to explicitly map the negotiated state to the reader initialization. This ensures that when compression is not negotiated, compress evaluates to False, forcing the reader to flag incoming RSV1 frames as protocol violations:
# Patched instantiation in aiohttp/client.py
parser = WebSocketReader(
reader,
max_msg_size,
compress=bool(compress), # Fix: Explicitly pass handshake setting
decode_text=decode_text,
)Exploitation of CVE-2026-59881 requires a malicious or compromised WebSocket server to which the vulnerable client initiates a connection. It is also possible for a Man-in-the-Middle (MitM) attacker on an unencrypted ws:// connection to intercept and inject the malicious frames into the stream.
The attack begins during the initial WebSocket HTTP upgrade handshake. The client initiates a connection, and the server accepts the upgrade but omits the Sec-WebSocket-Extensions: permessage-deflate header from the response. The client assumes the connection is established without compression support, yet due to the bug, its frame parser remains configured to accept and decompress frames.
Once the handshake is completed, the server transmits a crafted WebSocket frame where the RSV1 bit in the first byte is set to 1. The payload of this frame is structured as a valid zlib/deflate payload. Crucially, the payload is crafted as a decompression bomb (zip bomb), containing highly repetitive, highly compressible byte patterns designed to expand dramatically upon processing.
When the vulnerable client receives this frame, it passes the compressed bytes directly to zlib.decompress. The decompression process consumes massive CPU resources to process the stream and causes a sudden escalation in memory allocation. This resource spike degrades application performance, stalls asynchronous event loops, and typically triggers an out-of-memory (OOM) termination of the client process.
The impact of CVE-2026-59881 is classified as a remote denial of service (DoS) and resource exhaustion vulnerability affecting systems that deploy the aiohttp client. This vulnerability does not lead to remote code execution, privilege escalation, or unauthorized access to sensitive application data.
Because the defect is situated within the client-side implementation, the vulnerability is only triggered when the client actively establishes a connection to a hostile endpoint. This limits the threat surface primarily to applications that interact with external, dynamic, or user-provided WebSocket targets, such as crawler bots, feeds, or messaging clients.
The CVSS v4.0 score of 6.9 reflects medium severity, acknowledging that while the impact on system availability is low (restricted to the client application process), the attack complexity is low and requires no user interaction. There is currently no evidence of active exploitation in the wild, and the known exploit is limited to a proof-of-concept functional test within the project's repository.
The standard remediation is upgrading the aiohttp library to version 3.14.2 or higher. This update resolves the unsafe initialization defaults and implements strict RFC 6455 validation for the RSV1 bit. You can perform the upgrade through Python's package manager:
pip install -U "aiohttp>=3.14.2"If upgrading is not immediately possible, application developers should implement defensive controls to limit exposure. Restrict outbound WebSocket connections to validated, trusted domains. Avoid connecting to unencrypted ws:// schemes where data modification by network intermediaries is possible.
Additionally, deploy container-level resource limits (such as cgroup memory limits) to restrict the physical resources available to the application process. This ensures that if a client is targeted by a decompression bomb, the resulting resource consumption is contained and cannot exhaust the entire host system's memory or CPU allocation.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
aiohttp aio-libs | < 3.14.2 | 3.14.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-20 |
| Attack Vector | Network |
| CVSS v4.0 Score | 6.9 (Medium) |
| EPSS Score | 0.00302 |
| Impact | Denial of Service (DoS) / Resource Exhaustion |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
The product does not validate or incorrectly validates input that can affect the control flow or data flow of a program.
A critical SQL injection vulnerability was discovered in Sequelize when configured to use the Oracle database dialect. Due to a flawed optimization design in the SQL escaping subsystem (src/sql-string.js), strings that begin with native Oracle date functions bypass standard escaping. This allows unauthenticated remote attackers to execute arbitrary SQL commands on the target database.
An asynchronous HTTP client/server framework for asyncio and Python, aiohttp prior to version 3.14.2 is vulnerable to HTTP Request Smuggling. The server-side HTTP parser immediately transitions the protocol state to 'upgraded' upon receiving a WebSocket upgrade request before consuming the accompanying request body. If the backend handler rejects the upgrade request while keeping the TCP connection alive, the unconsumed request body remains in the socket buffer and is parsed as a subsequent pipelined HTTP request. This allows an attacker to smuggle requests, bypass frontend reverse proxy controls, and perform unauthorized actions.
A vulnerability in the Guzzle HTTP client allows session identifiers, auth tokens, or cookies to be leaked to unauthorized hosts due to incorrect cookie domain validation of noncanonical IPv4 host formats. Guzzle failed to recognize octal, hexadecimal, and percent-encoded IP addresses as IP literals, treating them as standard domains and incorrectly extending their scope to subdomains.
CVE-2026-69246 is a host validation bypass vulnerability in the Guzzle PHP HTTP client. The flaw resides in Guzzle's core HTTP transport handlers (cURL and PHP stream wrappers). Under specific conditions, a parser differential occurs between the host validation layer and the underlying network transport library (e.g., libcurl), allowing remote attackers to bypass SSRF filters, proxy routing rules, and redirect protections via crafted noncanonical URI representations.
A side-channel vulnerability in pyca/cryptography (versions 44.0.0 through 49.9.9) allows unauthenticated remote attackers to expose a Bleichenbacher oracle. This flaw exists within the PKCS#7 decryption module (specifically pkcs7_decrypt_der, pkcs7_decrypt_pem, and pkcs7_decrypt_smime) during Content Encryption Key (CEK) decryption when using RSA PKCS#1 v1.5 padding. Differences in error classification and symmetric execution timing allow an attacker to reconstruct plaintext keys.
An uncontrolled resource consumption vulnerability (CWE-400) exists in the python-cryptography library's Rust-based X.509 verification engine. The flaw allows unauthenticated remote attackers to trigger severe CPU exhaustion and Denial of Service (DoS) by supplying specially crafted certificate chains containing duplicate self-signed certificates, forcing the recursive path builder into an exponential state-search loop.