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

Mohamed Lemine Ahmed Jidou <[email protected]>
Newsgroups gmane.os.openbsd.bugs
Message-ID <RAhvhhYxLBpTx8zyR95G-1k6OiBMaMqLOwmLYvwYyhVozShmAiDDYzlxUNNDsZQe7jx1fNVOjJSMwTMMF4t2_-AO1_4XErligIVLel79tck=@proton.me>
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;
}
}
```

Please let me know if you need further clarification or assistance with testing the patch.

Best regards,

Mohamed Lemine Ahmed Jidou

Security Researcher | Zero-Day Research

AegisSec
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.