Aug 6, 2026·5 min read·2 visits
The 'h2' Python library fails to reject HTTP/2 streams containing multiple 'Host' headers. If an intermediary proxy downgrades these streams to HTTP/1.1, the duplicate headers are forwarded, triggering HTTP Request Smuggling.
A protocol-parsing vulnerability in the pure-Python HTTP/2 library 'h2' (versions <= 4.4.0) allows unauthenticated remote attackers to perform HTTP Request Smuggling (CWE-444). The vulnerability exists because the library does not validate the uniqueness of 'Host' headers in incoming HTTP/2 request streams. When an upstream gateway parses such requests and downgrades them to HTTP/1.1 for internal backend servers, the resulting stream contains duplicate Host headers, which leads to parsing inconsistency and potential bypass of security filters.
The pure-Python HTTP/2 protocol implementation library h2 is widely deployed as a core dependency in Python web applications, ASGI servers, and microservice proxy frameworks. The library is responsible for parsing low-level HTTP/2 frames, including HEADERS frames, and ensuring compliance with HTTP/2 and generic HTTP standards. Under standard configurations, the library acts as the front-line parser for incoming external network traffic before passing structured requests to upper-layer applications.
The vulnerability is classified under CWE-444: Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling'). The flaw lies in the handling of HTTP/2 headers that map directly to HTTP/1.1 connection concepts. Because HTTP/2 mandates lower-case header names and relies primarily on pseudo-headers like :authority for routing, standard HTTP/1.1 header validation rules are sometimes overlooked or improperly implemented during transition phases.
When a downstream proxy downgrades incoming HTTP/2 connections to HTTP/1.1 to route them to legacy backends, the absence of duplicate Host header validation in h2 causes both headers to be serialized. The downstream systems then receive conflicting metadata regarding the target host, exposing the deployment to request smuggling vectors.
The root cause of this vulnerability is located within the _validate_host_authority_header generator function in src/h2/utilities.py. In an HTTP/2 context, request authorities are primarily defined by the :authority pseudo-header. However, for backward compatibility and transition purposes, RFC 9110 specifies that clients may also supply a standard host header. According to HTTP semantics, any individual request must contain at most a single host identifier.
Prior to version 4.4.1, h2 did not track whether a host header had already been processed when parsing incoming headers in a loop. The utility generator processed and yielded every b"host" byte-string identifier encountered. The parser updated a local variable host_header_val to the last value parsed, discarding earlier values without raising any protocol exception.
Because the parser allowed multiple instances of the host key to pass validation, an attacker could include multiple conflicting host headers within a single HEADERS frame. The state machine continued tracking the connection successfully, eventually yielding all the headers directly to the application layer.
To understand the implementation flaw, observe the original validation loop inside src/h2/utilities.py before the patch:
# Vulnerable implementation in h2 <= 4.4.0
def _validate_host_authority_header(headers: Iterable[Header]) -> Generator[Header, None, None]:
# ... initialization of variables ...
for header in headers:
if header[0] == b":authority":
authority_header_val = header[1]
elif header[0] == b"host":
# Missing: Verification that host_header_val is None
host_header_val = header[1]
yield headerThe patch introduced in version 4.4.1 (commit 292a40829feefda98c8509dcdbbb4a57af9bd6a6) inserts a explicit check to verify that host_header_val is unassigned before recording a new b"host" value:
# Patched implementation in h2 4.4.1
def _validate_host_authority_header(headers: Iterable[Header]) -> Generator[Header, None, None]:
# ... initialization of variables ...
for header in headers:
if header[0] == b":authority":
authority_header_val = header[1]
elif header[0] == b"host":
if host_header_val is not None:
msg = "Request header block has multiple Host headers."
raise ProtocolError(msg)
host_header_val = header[1]
yield headerThis modification ensures that if a duplicate b"host" header is encountered, h2 immediately raises a ProtocolError exception, shutting down the stream. This prevents the request from reaching the application layer or downstream servers.
The fix is complete because HTTP/2 mandates that header names must be entirely lowercase. The byte-string comparison header[0] == b"host" is sufficient to catch all validly formatted host headers. Any mixed-case variants (e.g., Host or HOST) are rejected at an earlier stage by the HTTP/2 frame decoder, preventing bypasses utilizing header name capitalization.
An attacker seeking to exploit this vulnerability must target an environment that uses a vulnerable h2 gateway downstream of an HTTP/1.1 backend infrastructure. The attack is structured as follows:
Request Crafting: The attacker sends an HTTP/2 request containing standard pseudo-headers and appends duplicate host headers. For instance:
:authority: valid-target.comhost: valid-target.comhost: restricted-internal-target.comProxy Processing: The front-end proxy uses the vulnerable h2 library to parse the request. Since h2 does not reject the duplicate headers, the request is passed through to the proxy's routing logic.
Connection Downgrading: The proxy converts the HTTP/2 stream to HTTP/1.1 to forward it to the backend server, serializing the headers as:
GET /admin HTTP/1.1
Host: valid-target.com
Host: restricted-internal-target.comInconsistent Interpretation: If the front-end proxy validated the request path /admin against valid-target.com (which is permitted), but the backend server prioritizes the second Host header (restricted-internal-target.com), the attacker successfully accesses restricted backend functions. This discrepancy allows bypass of host-based routing restrictions, web cache poisoning, or credential access.
The impact of CVE-2026-71554 depends heavily on the architecture of the hosting environment. If the Python application is directly exposed to the internet and does not downgrade connection protocols, the impact is negligible.
However, in cloud-native configurations where an ASGI server (such as Uvicorn or Hypercorn) or a custom gateway relies on h2 and sits behind an external load balancer, the threat is elevated. The vulnerability scores 5.3 (Medium) under CVSS v3.1, reflecting low confidentiality and integrity impact if exploited to bypass access control measures.
Additionally, because HTTP Request Smuggling can affect upstream caches, an attacker could potentially poison shared web caches, mapping legitimate pages to administrative resources, affecting other active users of the platform.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
h2 python-hyper | < 4.4.1 | 4.4.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-444 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 5.3 (Medium) |
| Impact | Low Integrity & Availability (Potential HTTP Request Smuggling) |
| Exploit Status | none (Theoretical / No public PoCs available) |
| KEV Status | Not Listed |
The software receives an HTTP request and parses it in an inconsistent way compared to upstream or downstream servers, allowing actors to 'smuggle' a request to the recipient without the intermediary's knowledge.
An authenticated information disclosure vulnerability in Craft CMS allows high-privilege administrators to extract sensitive environment variables, including the CRAFT_SECURITY_KEY and database credentials, using a blind error-based template injection attack within element select condition rules.
An information disclosure vulnerability in Craft CMS allows users with administrative or non-sandboxed template-authoring privileges to read arbitrary system and configuration files. The issue stems from an incomplete class instantiation blocklist in the Twig template extension, which omitted PHP's built-in SplFileObject class.
An authorization bypass vulnerability in Craft CMS allows authenticated control panel users with low privileges to reorder global sets. This alters structure and writes to the project configuration database schema without administrative rights.
Prior to versions 10.9.8 and 11.16.1, Mermaid is vulnerable to prototype pollution via its deep-merge utility function assignWithDepth. This helper is invoked by public configuration-setting interfaces, specifically mermaid.initialize, mermaidAPI.setConfig, and mermaidAPI.updateSiteConfig. Because assignWithDepth recursively merges developer-provided properties into Mermaid's internal configuration state without proper sanitization, an attacker who can control or influence the configuration payload can corrupt the global Object.prototype. This vulnerability can lead to security bypasses, cross-site scripting (XSS), or execution flow modifications in applications using vulnerable Mermaid integrations.
A high-severity path traversal vulnerability exists in Traefik's Kubernetes Ingress NGINX provider. The flaw resides in the RewriteTarget middleware, which is auto-generated when an Ingress resource specifies the `nginx.ingress.kubernetes.io/rewrite-target` annotation. This allows remote, unauthenticated attackers to bypass route-level authentication and access restricted downstream endpoints by exploiting a parser differential.
CVE-2026-65600 is a path traversal vulnerability in the ReplacePathRegex middleware component of Traefik. An unauthenticated remote attacker can exploit the vulnerability to inject directory traversal sequences. When Traefik forwards the resulting un-normalized path, downstream backend web servers normalize the request to execute administrative or protected paths, bypassing gateway-enforced security policies.