Aug 3, 2026·7 min read·174 visits
A heap out-of-bounds read in the C-parser of aiohttp allows unauthenticated remote attackers to crash application processes or leak adjacent heap memory by sending specially crafted malformed HTTP requests or responses.
A high-severity heap-based out-of-bounds (OOB) read vulnerability exists in the Cython-based HTTP response and request parser extension of aiohttp. When processing malformed HTTP traffic, the parser fails to properly handle raw C pointers returned by the underlying llhttp library during error-message construction. This triggers an uncontrolled strlen() call on non-null-terminated network buffers, which can result in a Denial of Service (DoS) via worker process crash or the exposure of adjacent heap memory inside exception messages.
The aiohttp framework is a widely deployed asynchronous HTTP client and server library built on Python's asyncio subsystem. To maximize parsing throughput, the project implements a high-performance native C-parser extension in Cython, which wraps the low-level llhttp library. This architecture minimizes the overhead of HTTP message framing, header ingestion, and chunk validation. However, handling execution errors at the boundary between native Python code and raw C memory constructs introduces significant memory safety hazards.
This vulnerability, designated CVE-2026-69244, is a heap-based out-of-bounds (OOB) read that occurs during exception generation. When the llhttp parser encounters syntactically invalid input, the Cython wrapper tries to format a descriptive error message displaying the context of the error. During this process, the parser obtains a raw C pointer pointing to the invalid payload byte and incorrectly treats it as a standard null-terminated C-string.
The attack surface is exposed to any network endpoint that processes untrusted HTTP requests (in server-side configurations) or untrusted HTTP responses (in client-side configurations). The vulnerability is classified under CWE-125 (Out-of-bounds Read), with secondary impacts associated with CWE-416 (Use After Free) and CWE-400 (Uncontrolled Resource Consumption). The vulnerability is completely resolved in version 3.14.3 of the library.
To understand the root cause, it is necessary to examine how llhttp and Cython interact during error handling. When the llhttp state machine detects a protocol violation (such as invalid characters in a chunk size or a missing carriage return), it halts parsing. The Cython wrapper class HttpParser then calls cparser.llhttp_get_error_pos(self._cparser). This C function returns a raw char * pointer indicating the exact memory address in the parsing buffer where the error occurred.
In the vulnerable implementation, the Cython wrapper assigns this raw pointer to the variable after. The code subsequently executes the Python slicing and splitting statement after_b = after.split(b"\r\n", 1)[0]. Because after is a raw C char * and .split() is a Python string method, Cython must implicitly convert the C pointer into a Python bytes object before executing the method. Because Cython is not given an explicit length for the buffer at the C-pointer address, it assumes the pointer references a standard, null-terminated C-string.
To perform this implicit conversion, Cython invokes the native C-library function strlen() on the pointer. However, network buffers received from sockets are not null-terminated; they contain raw bytes representing TCP stream segments. If a parsing error occurs exactly at the end of the incoming buffer chunk, strlen() will fail to find a null byte (\x00) within the boundaries of the allocated buffer. The function then continues reading sequentially through adjacent heap memory blocks until it eventually hits a null byte. This behavior leads directly to a heap-based out-of-bounds read.
The following code block highlights the exact flaw in the Cython-based HTTP parser (aiohttp/_http_parser.pyx) and contrasts the vulnerable logic with the official patch.
# === VULNERABLE CODE (aiohttp < 3.14.3) ===
# The parser retrieves the raw C error position pointer.
# Cython implicitly casts the raw char* pointer to a Python bytes object
# using strlen() because no buffer length is specified.
after = cparser.llhttp_get_error_pos(self._cparser)
before = data[:after - base]
# The split method forces an un-bounded strlen call on a non-null-terminated heap buffer.
after_b = after.split(b"\r\n", 1)[0]
# === PATCHED CODE (aiohttp 3.14.3) ===
# The patch computes the safe integer offset relative to the base pointer.
error_pos = cparser.llhttp_get_error_pos(self._cparser)
error_off = error_pos - base
# Slicing is performed strictly on the native Python 'data' bytes object.
before = data[:error_off]
after = data[error_off:].split(b"\r\n", 1)[0]The patched version replaces pointer-based manipulation with integer offset calculations. Rather than converting the raw pointer after directly to a Python object, the code calculates error_off = error_pos - base. Since base is the starting address of the known buffer and error_pos is the error address within that buffer, error_off is a safe, relative integer offset.
Slicing is then performed directly on data, which is already a native Python bytes object representing the buffer. Slicing in Python is strictly bounds-checked by the interpreter. If error_off points to the end of the buffer, Python safely handles the slice without reading beyond the underlying memory allocation. This approach completely prevents unsafe native-level pointer parsing and eliminates the vulnerability.
Exploitation of CVE-2026-69244 does not require authentication and can be executed via either server-side or client-side vectors. In a server-side attack vector, the target is an aiohttp-based web server. The attacker crafts a malicious HTTP request designed to trigger a protocol syntax error precisely at the termination boundary of the request payload. For example, the attacker can transmit a request utilizing chunked transfer encoding where the chunk specification ends abruptly as 0_ or 0\rX without a trailing carriage return line feed (CRLF).
When the server receives this payload, the socket buffer terminates immediately after the malformed character. The llhttp engine flags the error, and the exception handler is executed. Because the buffer lacks a trailing null terminator, the strlen() call traverses the heap, attempting to locate a null byte. The physical flow of this exploitation sequence is illustrated below:
The vulnerability behaves in two ways depending on the heap layout. If the strlen() scan crosses page boundaries into unmapped virtual memory, the operating system kernel immediately kills the python worker process with a segmentation fault (SIGSEGV). This results in an immediate Denial of Service. Alternatively, if the scan resolves within mapped memory, the adjacent heap bytes are converted into the python exception object and can be leaked to logs or directly to the attacker in HTTP error responses.
The primary threat posed by this vulnerability is a remote, unauthenticated Denial of Service (DoS). For applications running aiohttp as a web server, sending a steady stream of malformed requests can repeatedly crash active worker processes. This forces container engines or service managers to repeatedly restart the process, exhausting system resources and causing a sustained outage for legitimate users.
The CVSS v4.0 base score is rated at 7.1 with the vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N. This calculation highlights that the vulnerability is remotely exploitable with low complexity, requires no special system configurations, and demands no administrative privileges. Although the CVSS evaluation conservatively rates confidentiality impact as None, real-world exploitation in multi-tenant environments introduces a localized risk of informational leakage.
Because the out-of-bounds heap data is formatted into a Python exception object, any application configured to return verbose error tracebacks to clients may leak adjacent heap memory. This leaked memory can contain sensitive objects, such as session cookies, database connection strings, or encryption keys from other active connection threads. This behavior elevates the actual risk beyond a simple process crash.
The recommended remediation is to upgrade all installations of aiohttp to version 3.14.3 or later. This replaces the vulnerable Cython pointer manipulation logic with the safe, offset-based slicing mechanism. Package managers can execute the following command to update the dependency:
pip install -U aiohttp>=3.14.3
If upgrading is not immediately feasible due to legacy system constraints, administrators can fully mitigate the vulnerability by disabling the compiled Cython extensions. This forces aiohttp to fall back on its pure-Python HTTP parser, which is inherently safe from native-level memory bounds issues. To implement this workaround, set the following environment variable in the application's runtime environment:
export AIOHTTP_NO_EXTENSIONS=1
Note that disabling C extensions will decrease HTTP parsing performance and increase CPU utilization under high-traffic conditions. Consequently, this workaround should only be used as a temporary measure until the library can be updated. Organizations should also monitor deployment environments for container crashes returning exit code 139 (SIGSEGV) to identify potential exploitation attempts.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
aiohttp aio-libs | < 3.14.3 | 3.14.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-125 |
| Attack Vector | Network |
| CVSS v4.0 Score | 7.1 |
| EPSS Score | Not Available |
| Vulnerability Class | Heap Out-of-Bounds Read |
| Exploit Status | Proof-of-Concept (PoC) |
| CISA KEV Status | Not Listed |
The program reads data past the end, or before the beginning, of the intended buffer.
An authorization bypass and credential oracle vulnerability in pyload-ng allows authenticated users with minimal or no privileges to brute-force the administrator password. This is achieved through sensitive API methods exposed globally combined with a non-constant-time password hash comparison algorithm.
An authentication bypass vulnerability in pyLoad allows unauthenticated remote attackers to gain administrative API access. The vulnerability is caused by a logical flaw in the API key cache validation lookup, where authentication states are cached using only the public key identifier, skipping cryptographic token verification on cache hits.
An authorization bypass vulnerability in Strawberry GraphQL between versions 0.217.0 and 0.326.1 occurs when a synchronous permission handler returns an awaitable object (such as an unawaited coroutine). Due to Python's truthiness rules, the unawaited coroutine is evaluated as True, leading to an immediate bypass of security policies.
An uncontrolled resource consumption vulnerability in Strawberry GraphQL allows unauthenticated remote attackers to trigger a Denial of Service on persistent WebSocket connections using the legacy graphql-ws protocol. When the server enforces max_subscriptions_per_connection, naturally terminating subscriptions are not cleared from memory registries, leading to exhaustion of connection slots.
A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.
NearForm fast-jwt prior to version 6.3.0 is vulnerable to an input validation flaw where configuring verifier properties (such as clockTolerance, clockTimestamp, and cacheTTL) with non-finite values like Infinity or NaN allows attackers to bypass temporal claim validations, including expiration (exp) and activation (nbf) boundaries. This validation bypass can result in unauthorized session persistence and cache poisoning.