Re: Bug: RFC 7230 Section 3.3.3 violation in httpd header parsing enables TE.CL Request Smuggling

Claudio Jeker <[email protected]>
Newsgroups gmane.os.openbsd.bugs
Message-ID <[email protected]>
On Mon, Apr 13, 2026 at 12:42:30PM +0000, Mohamed Lemine Ahmed Jidou wrote:
> Hello,
> 
> During a source code review of httpd, I identified an RFC compliance issue in how the HTTP header parser handles unrecognized or obfuscated Transfer-Encoding values.
> 
> Instead of actively rejecting requests with malformed transfer encodings (as mandated by RFC 7230), httpd silently ignores the header and falls back to using the Content-Length header to determine the message body length. When httpd is deployed behind a frontend reverse proxy (e.g., HAProxy, Nginx), this semantic discrepancy allows an attacker to perform HTTP Request Smuggling (TE.CL) attacks.
> 
> ### 1. Vulnerability Analysis & Source Code Reference
> 
> In usr.sbin/httpd/server_http.c, within the server_read_http function, the parser handles the Transfer-Encoding header as follows:
> 
> ```c
> /* usr.sbin/httpd/server_http.c */
> if (strcasecmp("Transfer-Encoding", key) == 0 &&
> strcasecmp("chunked", value) == 0)
> desc->http_chunked = 1;
> 
> ```
> 
> The code strictly expects the value to be exactly "chunked". If an attacker supplies an obfuscated value that a frontend proxy might normalize or accept (e.g., Transfer-Encoding: chunked, identity or Transfer-Encoding: chunked\\r), the strcasecmp condition evaluates to false.
> 
> Because there is no else block to handle unrecognized values for this specific header, desc->http_chunked remains 0. The parser proceeds without error and later uses clt->clt_toread (which was populated if a Content-Length header was also provided in the request) to determine the body size.
> 
> ### 2. RFC 7230 Violation
> 
> This behavior is a direct violation of RFC 7230, Section 3.3.3, Paragraph 3, which dictates how servers must handle requests containing both Transfer-Encoding and Content-Length:
> 
> > "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. [...] If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the final encoding, the message body length cannot be determined reliably; the server MUST respond with the 400 (Bad Request) status code and then close the connection."
> 
> By silently falling back to Content-Length rather than returning a 400 Bad Request, httpd breaks the request boundary synchronization with upstream proxies, creating a classic TE.CL smuggling vector.
> 
> ### 3. Steps to Reproduce (PoC)
> 
> To reproduce this behavior on a standalone OpenBSD httpd instance, send a POST request with an invalid Transfer-Encoding and a valid Content-Length.
> 
> Run the following command against httpd:
> 
> ```bash
> printf "POST / HTTP/1.1\r\nHost: target.local\r\nContent-Length: 6\r\nTransfer-Encoding: chunked, invalid\r\n\r\n0\r\n\r\nX" | nc <HTTPD_IP> 80
> 
> **Expected Result (per RFC 7230):** The server MUST reject the request immediately with a `HTTP/1.0 400 Bad Request` and close the connection because the transfer encoding is not strictly "chunked". **Actual Vulnerable Result:** The server accepts the request and responds with a `200 OK`, `403 Forbidden`, or `404 Not Found` (depending on the configured routes). It successfully processes the request using the `Content-Length: 6` to consume the `0\\r\\n\\r\\nX` payload, completely ignoring the malformed `Transfer-Encoding` header. ### 4. Security Impact In a typical reverse-proxy architecture, the frontend proxy processes the request using the `Transfer-Encoding` (interpreting the `0\\r\\n\\r\\n` as the end of the chunked request), while `httpd` processes it using the `Content-Length`. This leaves the trailing bytes (the smuggled request) in the backend TCP buffer. The backend `httpd` will process this smuggled payload as the beginning of the next user's HTTP request, leading to cache poisoning, WAF bypass, or cross-user response hijacking. ### 5. Proposed Fix The parser should explicitly reject the request if the `Transfer-Encoding` header is present but its value is not supported. Suggested patch logic in `server_http.c`: c
> if (strcasecmp("Transfer-Encoding", key) == 0) {
> if (strcasecmp("chunked", value) == 0) {
> desc->http_chunked = 1;
> 
> } else {
> server_abort_http(clt, 400, "malformed transfer-encoding");
> return;
> }
> }
> ```
 
Playing devils advocate here. If your request makes it through the WAF /
reverse proxy then aren't those systems vulnerable to this and not httpd?

Now the handling of Transfer-Encoding and Content-Length needs to be
stricter in httpd, your simplistic fix is IMO not enough.

RFC9112 has:
A sender MUST NOT send a Content-Length header field in any message that
contains a Transfer-Encoding header field.

Your diff only covers possible headers like:
	Transfer-Encoding: gzip, chunked

Which is a start.

-- 
:wq Claudio
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.