Mar 6, 2026·7 min read·3 visits
Pingora < 0.8.0 improperly handles HTTP/1.0 bodies and Transfer-Encoding headers, allowing attackers to smuggle requests past the proxy. Fixed in v0.8.0.
A critical HTTP Request Smuggling vulnerability (CWE-444) exists in Cloudflare Pingora versions prior to 0.8.0. The vulnerability stems from non-compliant parsing of HTTP/1.0 request bodies and ambiguous 'Transfer-Encoding' headers. By crafting malicious HTTP requests that exploit these framing inconsistencies, unauthenticated attackers can desynchronize the proxy from backend servers, leading to cache poisoning, security control bypasses, and potential session hijacking.
Cloudflare Pingora, a Rust-based asynchronous multithreaded proxy, contains a critical HTTP Request Smuggling vulnerability in versions prior to 0.8.0. The flaw resides in the core HTTP parsing logic, specifically within pingora-core/src/protocols/http/v1/server.rs. The parser fails to strictly adhere to RFC 9112 regarding message framing, creating a discrepancy between how Pingora interprets an HTTP request and how a backend upstream server interprets it.
HTTP Request Smuggling occurs when a frontend (the proxy) and a backend (the application server) disagree on where a request ends and the next one begins. In this instance, Pingora's permissive parsing allowed two specific framing violations: permitting 'close-delimited' bodies in HTTP requests (a behavior reserved strictly for responses in HTTP/1.0) and failing to enforce that chunked is the final encoding in the Transfer-Encoding header. These oversights allow an attacker to hide a second, malicious request inside the body of a seemingly benign first request.
The impact is severe because Pingora is often deployed as a high-performance edge proxy handling security controls (WAF, ACLs). If an attacker successfully smuggles a request, that request effectively bypasses all edge security measures. Furthermore, because the backend processes the smuggled request as the start of the next legitimate user's request, this can lead to massive cross-user interference, including the theft of session cookies or the serving of poisoned cache entries to innocent users.
The vulnerability is caused by two distinct failures in implementing the HTTP/1.1 specification (RFC 9112). The first issue is the improper handling of HTTP/1.0 Close-Delimitation. In the HTTP protocol, a response body can be terminated by closing the TCP connection. However, RFC 9112 explicitly forbids this for requests. A request must have a defined length (via Content-Length or Transfer-Encoding: chunked) or be treated as zero-length. Pingora's BodyReader incorrectly defaulted to init_until_close() for HTTP/1.0 requests lacking length headers. This allowed an attacker to send a request with a body but no length header. Pingora would forward the bytes until the connection closed, while a compliant backend would stop reading at the end of the headers, leaving the 'body' bytes to be processed as a separate, smuggled request.
The second issue is Transfer-Encoding (TE) Ambiguity. When a Transfer-Encoding header is present, the final encoding must be chunked for the message to be framed as chunked data. Pingora's original parser, is_header_value_chunked_encoding, performed a naive check that failed to account for multiple values or comma-separated lists correctly. For example, a header like Transfer-Encoding: chunked, identity is invalid per RFC, but Pingora might interpret it as chunked if it simply scans for the string 'chunked'. If the backend interprets the same header as identity (ignoring the TE or prioritizing Content-Length), the framing logic desynchronizes.
The remediation involved enforcing strict state machine transitions in the request parser. The fix was applied across multiple commits in the pingora-core crate.
1. Enforcing Zero-Length Bodies for HTTP/1.0 (Commit 40c3c1e)
The logic was updated to prevent the parser from entering the UntilClose state for requests. Instead, if no length header is found, the body size is set to 0.
// pingora-core/src/protocols/http/v1/server.rs
// VULNERABLE CODE (Simplified)
// if req.header.content_length.is_none() && !req.header.transfer_encoding {
// // Incorrectly allows reading until connection close for requests
// self.body_reader.init_until_close();
// }
// PATCHED CODE
// RFC 9112: A request without Content-Length or Transfer-Encoding must have 0 length.
if req.header.content_length.is_none() && !is_chunked {
// Force zero-length body for HTTP/1.0 requests
self.body_reader.init_fixed_length(0);
}2. Strict Transfer-Encoding Validation (Commit 7f7166d)
The function responsible for checking chunked encoding was rewritten to verify that chunked is strictly the final encoding applied.
// pingora-core/src/protocols/http/v1/common.rs
// VULNERABLE APPROACH
// Checked if "chunked" appeared anywhere in the header value
// PATCHED APPROACH
fn is_chunked_encoding_from_headers(headers: &HeaderMap) -> bool {
// Get the last Transfer-Encoding header
if let Some(value) = headers.get_all(TRANSFER_ENCODING).next_back() {
// Parse comma-separated values and check the LAST token
let final_encoding = get_last_encoding_token(value);
return final_encoding.eq_ignore_ascii_case("chunked");
}
false
}These changes ensure that Pingora's interpretation of request boundaries mathematically aligns with standard-compliant upstream servers.
An attacker can exploit this via a TE.CL (Transfer-Encoding vs. Content-Length) desynchronization attack. The goal is to send a request that Pingora views as having a specific length, while the backend views it as shorter. The remaining bytes are then processed by the backend as the start of the next request on that persistent connection.
Scenario: HTTP/1.0 Smuggling
Content-Length header.init_until_close behavior).Scenario: TE Obfuscation
Transfer-Encoding: chunked, identity.Content-Length).The security implications of HTTP Request Smuggling in an edge proxy like Pingora are critical. Because the proxy handles multiple users over shared TCP connections to the backend, desynchronization affects not just the attacker, but potentially any user routed to the same backend process.
Key Impacts:
/admin) are applied to the first request. The smuggled second request bypasses these checks entirely because Pingora never inspects it—it is merely forwarded as "data" belonging to the first request.The primary remediation is to upgrade Cloudflare Pingora to version 0.8.0 or later. This version introduces the strict state machine required to prevent framing ambiguities. No configuration changes are sufficient to fully mitigate the risk if the underlying parser logic is flawed, so binary replacement is necessary.
Immediate Actions:
pingora-core and related crates to v0.8.0 in your Cargo.toml.Defense-in-Depth:
chunked_transfer_encoding is on and consider dropping requests with ambiguous headers before they reach the application logic.CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:H/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Pingora Cloudflare | < 0.8.0 | 0.8.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-444 |
| Attack Vector | Network |
| CVSS v4.0 | 9.3 (Critical) |
| EPSS Score | 0.00048 (14.80%) |
| Impact | Request Smuggling / Cache Poisoning |
| Fix Version | 0.8.0 |