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-2025-69225
5.30.04%

CVE-2025-69225: HTTP Request Smuggling via Unicode Range Header in aiohttp

Alon Barad
Alon Barad
Software Engineer

Feb 28, 2026·6 min read·12 visits

PoC Available

Executive Summary (TL;DR)

aiohttp failed to restrict the parsing of the HTTP 'Range' header to ASCII digits. Python's default regex engine interprets non-ASCII Unicode digits (e.g., Devanagari numerals) as valid numbers. This discrepancy allows attackers to bypass proxy filters or desynchronize the interpretation of the request, leading to HTTP Request Smuggling or Cache Poisoning. Fixed in version 3.13.3.

A differential interpretation vulnerability exists in aiohttp versions 3.13.2 and earlier, caused by the mishandling of Unicode digits in the HTTP 'Range' header. This flaw allows attackers to smuggle requests or poison web caches by injecting non-ASCII numeric characters that are interpreted as valid digits by the Python backend but treated as invalid or text by upstream proxies.

Vulnerability Overview

CVE-2025-69225 represents a semantic parsing vulnerability within the aiohttp library, a widely used asynchronous HTTP client/server framework for Python. The vulnerability resides in the server-side handling of the HTTP Range header, specifically within the aiohttp.web_request module.

Modern web architectures typically involve a chain of systems processing a single HTTP request—load balancers, WAFs, reverse proxies (like Nginx or HAProxy), and finally the application backend. Security relies on these systems having a consistent interpretation of the HTTP protocol. This vulnerability breaks that consistency. While the HTTP standards (RFC 9110/9112) imply that header values like byte ranges should consist of ASCII digits (0-9), aiohttp leveraged Python's permissive string handling to accept a broader range of Unicode numeric characters.

This behavior introduces a classic 'differential interpretation' attack surface. An attacker can craft an HTTP request containing Unicode digits (e.g., '५' for '5') in the Range header. A strict frontend proxy may view this as an invalid header, a distinct textual string, or garbage data, potentially forwarding it without normalization. The aiohttp backend, however, parses these characters as valid integers. This divergence creates the conditions necessary for HTTP Request Smuggling (HRS) and Cache Poisoning.

Root Cause Analysis

The root cause of this vulnerability lies in the default behavior of Python's re (regular expression) module combined with the int() constructor, neither of which defaults to strict ASCII enforcement in Python 3.

The Regex Discrepancy

In Python 3, the regular expression character class \d (digit) matches any character in the Unicode category Nd (Number, Decimal Digit). This includes standard ASCII digits [0-9], but also hundreds of other characters from various scripts, such as:

  • Devanagari: ५ (U+096B, Value: 5)
  • Arabic-Indic: ١ (U+0661, Value: 1)
  • Fullwidth: 1 (U+FF11, Value: 1)

The vulnerable code in aiohttp utilized the regex pattern r"^bytes=(\d*)-(\d*)$" without the re.ASCII flag. Consequently, the pattern matches strings like bytes=0-५. When the captured group ५ is passed to Python's int() function, it is successfully converted to the integer 5.

The Parsing differential

Upstream infrastructure (written in C, Go, or Rust) typically relies on standard ASCII byte processing. When a proxy sees Range: bytes=0-५, it does not interpret it as a valid byte range request because ५ is not in the ASCII range 0x30-0x39. The proxy might ignore the header or forward it as a generic custom header. aiohttp, acting on the same input, processes it as a valid range request bytes=0-5, altering the response content (e.g., returning a 206 Partial Content status). This specific misalignment allows attackers to manipulate the response state without the proxy being aware.

Code Analysis

The vulnerability existed in aiohttp/web_request.py within the http_range property. Below is the analysis of the vulnerable code and the remediation applied in version 3.13.3.

Vulnerable Code (<= 3.13.2)

# aiohttp/web_request.py
 
@property
def http_range(self) -> slice:
    rng = self.headers.get(hdrs.RANGE)
    if rng is not None:
        try:
            # VULNERABILITY: \d matches Unicode digits by default in Python 3
            pattern = r"^bytes=(\d*)-(\d*)$"
            start, end = re.findall(pattern, rng)[0]
            
            # int() also accepts Unicode digits
            if start:
                start = int(start)
            if end:
                end = int(end)
            # ...

Patched Code (3.13.3+)

The fix, applied in commit c7b7a044f88c71cefda95ec75cdcfaa4792b3b96, forces the regex engine to operate in ASCII-only mode. This ensures that \d only matches characters [0-9].

# aiohttp/web_request.py
 
@property
def http_range(self) -> slice:
    rng = self.headers.get(hdrs.RANGE)
    if rng is not None:
        try:
            pattern = r"^bytes=(\d*)-(\d*)$"
            # FIX: re.ASCII flag restricts matching to [0-9]
            start, end = re.findall(pattern, rng, re.ASCII)[0]
            
            if start:
                start = int(start)
            # ...

This simple one-line change aligns the application's parsing logic with the strict expectations of the HTTP RFCs and upstream infrastructure, effectively neutralizing the attack vector.

Exploitation Scenarios

The exploitation of this vulnerability relies on the presence of an intermediary system that inspects or acts upon HTTP headers but interprets them differently than aiohttp.

Scenario: Cache Poisoning

  1. Setup: An application sits behind a caching reverse proxy. The proxy is configured to cache static assets but ignores invalid Range headers to prevent cache fragmentation.
  2. Attack: The attacker sends a request for a large static file (e.g., main.js) with the header Range: bytes=0-५.
  3. Proxy Interpretation: The proxy sees non-ASCII characters, deems the Range header invalid, and assumes the backend will return the full file (200 OK). It prepares to cache the response associated with the URL.
  4. Backend Interpretation: aiohttp parses ५ as 5, interprets the header as bytes=0-5, and returns a 206 Partial Content response containing only the first 6 bytes of the file.
  5. Poisoning: The proxy receives the response. Depending on its configuration and how strictly it validates the status code against the expected behavior, it may cache this truncated 6-byte fragment as the valid full response for main.js.
  6. Impact: Subsequent legitimate users requesting main.js receive the corrupted 6-byte file, causing a denial of service for that asset.

Impact Assessment

The impact of CVE-2025-69225 is classified as Medium (CVSS 5.3). While the mechanism allows for protocol manipulation, the actual impact is highly dependent on the architecture of the deployed environment.

  • Confidentiality: None. The vulnerability does not directly expose sensitive data, though it could theoretically be used to retrieve cached data belonging to other users in complex smuggling scenarios.
  • Integrity: Low. The primary risk is cache poisoning, where valid content is replaced with partial or invalid content, affecting the integrity of the served application.
  • Availability: Low to None. Successful cache poisoning effectively denies access to the affected resources for legitimate users.

Prerequisites: The attack requires no authentication (PR:N) and can be executed remotely (AV:N). However, it requires a specific infrastructure setup involving a disparity between the proxy and the backend. Standalone aiohttp instances without an upstream proxy are generally not vulnerable to the smuggling aspect, though they will still process the unexpected range.

Official Patches

aio-libsOfficial patch commit

Fix Analysis (1)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

aiohttp <= 3.13.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
aiohttp
aio-libs
<= 3.13.23.13.3
AttributeDetail
CWE IDCWE-436
Attack VectorNetwork
CVSS v3.15.3 (Medium)
CVSS v4.02.7 (Low)
ImpactCache Poisoning / Request Smuggling
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1587Develop Capabilities
Resource Development
T1190Exploit Public-Facing Application
Initial Access
CWE-436
Interpretation Conflict

Interpretation Conflict

Known Exploits & Detection

GitHub Security AdvisoryAdvisory containing PoC logic regarding unicode digit handling
NucleiDetection Template Available

Vulnerability Timeline

Patch applied to maintenance branch
2026-01-03
GHSA Advisory Published
2026-01-05
CVE Published
2026-01-06

References & Sources

  • [1]GHSA-mqqc-3gqh-h2x8
  • [2]NVD - CVE-2025-69225

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.