You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|**Expected**|`400` or close preferred; `2xx` acceptable |
14
15
15
16
## What it sends
16
17
17
-
A full CL.TE smuggling payload — a POST request with both Content-Length and Transfer-Encoding headers, where the body contains a chunked`0` terminator followed by a smuggled second request.
18
+
A request with both `Content-Length` and `Transfer-Encoding: chunked` — the classic CL.TE conflict pattern.
A CL-only parser reads 4 bytes (`0\r\n\r`) as the body and sees the follow-up `GET`. A TE parser sees the `0` chunk as end-of-body and processes the `GET` as a separate request.
38
-
30
+
A CL-only parser reads 4 bytes (`0\r\n\r`) as the body. A TE parser sees the `0` chunk as end-of-body. The ambiguity is what makes this a smuggling vector in proxy chains.
39
31
40
32
## What the RFC says
41
33
42
-
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and ought to be handled as an error." — RFC 9112 §6.3
43
-
44
34
> "A server MAY reject a request that contains both Content-Length and Transfer-Encoding or process such a request in accordance with the Transfer-Encoding alone. Regardless, the server MUST close the connection after responding to such a request to avoid the potential attacks." — RFC 9112 §6.1
45
35
46
-
## Why it matters
47
-
48
-
This is not a theoretical test — it's the actual attack payload. If the server processes the first request using CL and the second appears in the pipeline, the smuggling succeeded.
49
-
50
-
## Deep Analysis
51
-
52
-
### Relevant ABNF
53
-
54
-
From RFC 9112 Section 6, the message body length algorithm depends on these headers:
55
-
56
-
```
57
-
Transfer-Encoding = #transfer-coding
58
-
Content-Length = 1*DIGIT
59
-
message-body = *OCTET
60
-
```
61
-
62
-
When both headers are present, the specification defines a strict precedence rule that eliminates ambiguity.
63
-
64
-
### RFC Evidence
65
-
66
-
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling or response splitting and ought to be handled as an error." -- RFC 9112 Section 6.3
67
-
68
-
> "A server MAY reject a request that contains both Content-Length and Transfer-Encoding or process such a request in accordance with the Transfer-Encoding alone. Regardless, the server MUST close the connection after responding to such a request to avoid the potential attacks." -- RFC 9112 Section 6.1
69
-
70
-
> "A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field." -- RFC 9112 Section 6.2
71
-
72
-
### Chain of Reasoning
36
+
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and ought to be handled as an error." — RFC 9112 §6.3
73
37
74
-
1.**Dual framing headers create ambiguity by design.** The CL.TE payload sends `Content-Length: 4` and `Transfer-Encoding: chunked` simultaneously. A CL-only parser reads exactly 4 bytes (`0\r\n\r`) as the body, leaving the trailing `\n` and the pipelined `GET` request on the connection. A TE-compliant parser reads the `0` chunk terminator and considers the body complete at a different boundary.
38
+
## Why it matters
75
39
76
-
2.**RFC 9112 Section 6.3 explicitly names this as a smuggling vector.** The specification does not merely discourage dual headers -- it calls out "request smuggling" by name and says the message "ought to be handled as an error." This is one of the rare cases where the RFC specifically names the attack it is trying to prevent.
40
+
When both framing headers are present, different parsers in a proxy chain may disagree on where the body ends. A server that rejects the ambiguous request with `400` eliminates the risk entirely. A server that accepts it (processing via TE alone) is RFC-compliant but relies on connection closure to prevent exploitation.
77
41
78
-
3.**The MUST-close requirement in Section 6.1 is the critical defense.** Even if the server chooses to process the request (using TE alone, as permitted), it MUST close the connection afterward. This prevents any leftover bytes from being interpreted as a subsequent request. A server that keeps the connection open after processing a dual-header request is vulnerable regardless of which framing method it chose.
42
+
## Verdicts
79
43
80
-
4.**Attack scenario.** An attacker sends the CL.TE payload to a proxy-origin pair. The proxy uses Content-Length (reads 4 bytes, forwards, then reads the `GET` as a separate pipelined request). The origin uses Transfer-Encoding (reads the `0` chunk, considers the POST done, then reads the `GET` -- but from the attacker's smuggled bytes, not from the proxy's pipeline). The origin now processes a request the proxy never authorized, potentially with attacker-controlled headers and path.
44
+
-**Pass** — Server rejects with `400` or closes the connection (safest behavior)
45
+
-**Warn** — Server responds with `2xx` (RFC-compliant if it processes via TE and closes the connection, but the lenient path)
46
+
-**Fail** — Any other response
81
47
82
-
###Scored / Unscored Justification
48
+
## Scored / Unscored Justification
83
49
84
-
This test is **scored** -- a `2xx` response results in a **Fail**. The RFC uses MUST-level language requiring connection closure after processing a dual CL+TE request, and the specification explicitly identifies this pattern as a smuggling vector. The test sends a pipelined follow-up `GET` on the same connection: if the server responds to it with`2xx`, it means the connection was kept alive after the ambiguous request, directly violating the MUST-close requirement and demonstrating exploitable behavior. There is no RFC-defensible reason for a server to keep the connection open in this scenario.
50
+
This test is **scored**. Although the RFC uses MAY language, there is a clear preferred outcome: rejecting the ambiguous request is safer than accepting it. A`2xx` response counts as a warning rather than a pass, reflecting the security trade-off.
|**Expected**|`400` or close preferred; `2xx` acceptable |
14
15
15
16
## What it sends
16
17
17
-
A full TE.CL smuggling payload — the reverse of CLTE. The front-end uses Transfer-Encoding and the body is crafted so the back-end (using Content-Length) sees a smuggled request.
18
+
The reverse of CL.TE — a request with `Transfer-Encoding: chunked` listed first, plus a conflicting `Content-Length`.
18
19
19
20
```http
20
21
POST / HTTP/1.1\r\n
@@ -26,62 +27,27 @@ Content-Length: 30\r\n
26
27
\r\n
27
28
```
28
29
29
-
Followed immediately on the same connection by:
30
-
31
-
```http
32
-
GET / HTTP/1.1\r\n
33
-
Host: localhost:8080\r\n
34
-
\r\n
35
-
```
36
-
37
-
A TE parser sees the `0` chunk as end-of-body. A CL-only parser tries to read 30 bytes and consumes the follow-up `GET` as body data.
38
-
30
+
A TE parser sees the `0` chunk as end-of-body (5 bytes consumed). A CL parser tries to read 30 bytes, consuming far more than the chunked body. The disagreement is what enables the TE.CL smuggling variant.
39
31
40
32
## What the RFC says
41
33
42
-
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and ought to be handled as an error." — RFC 9112 §6.3
43
-
44
34
> "A server MAY reject a request that contains both Content-Length and Transfer-Encoding or process such a request in accordance with the Transfer-Encoding alone. Regardless, the server MUST close the connection after responding to such a request to avoid the potential attacks." — RFC 9112 §6.1
45
35
46
-
## Why it matters
47
-
48
-
The TE.CL variant is equally dangerous to CL.TE. Together, they cover both possible orderings of front-end/back-end preference.
49
-
50
-
## Deep Analysis
51
-
52
-
### Relevant ABNF
53
-
54
-
From RFC 9112 Section 6:
55
-
56
-
```
57
-
Transfer-Encoding = #transfer-coding
58
-
Content-Length = 1*DIGIT
59
-
message-body = *OCTET
60
-
```
61
-
62
-
The TE.CL variant reverses the header order compared to CL.TE, but the same precedence rule applies: Transfer-Encoding overrides Content-Length.
63
-
64
-
### RFC Evidence
65
-
66
-
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling or response splitting and ought to be handled as an error." -- RFC 9112 Section 6.3
67
-
68
-
> "A server MAY reject a request that contains both Content-Length and Transfer-Encoding or process such a request in accordance with the Transfer-Encoding alone. Regardless, the server MUST close the connection after responding to such a request to avoid the potential attacks." -- RFC 9112 Section 6.1
69
-
70
-
> "A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field." -- RFC 9112 Section 6.2
71
-
72
-
### Chain of Reasoning
36
+
> "If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and ought to be handled as an error." — RFC 9112 §6.3
73
37
74
-
1.**TE.CL reverses the parser disagreement.** In this variant, the front-end uses Transfer-Encoding (reads the `0` chunk, considers the POST body complete) while the back-end uses Content-Length (tries to read 30 bytes, consuming the pipelined `GET` as part of the POST body). The fundamental issue is identical to CL.TE: two parsers in the same chain disagree on where the first request's body ends.
38
+
## Why it matters
75
39
76
-
2.**The 30-byte Content-Length is carefully chosen.** The chunked body (`0\r\n\r\n`) is only 5 bytes. Content-Length says 30. A CL parser will attempt to read 25 more bytes from the connection, consuming part or all of the follow-up `GET` request. This means the back-end either hangs waiting for data, consumes the next request, or produces an error -- all of which indicate a desync.
40
+
Together with CL.TE, this covers both orderings of the dual-header conflict. A proxy chain where one hop prefers TE and the other prefers CL is vulnerable to this variant. Rejecting the request outright is the safest defense.
77
41
78
-
3.**The RFC treats both variants identically.** Section 6.3 does not distinguish CL+TE from TE+CL header ordering. The rule is the same: TE overrides CL, the message ought to be treated as an error, and Section 6.1 mandates connection closure regardless of how the server processes the request. A compliant server handles both CLTE and TECL with the same defensive behavior.
42
+
## Verdicts
79
43
80
-
4.**Attack scenario.** An attacker sends the TE.CL payload through a proxy. The proxy processes chunked encoding, sees the empty `0` terminator, and forwards the completed POST. The back-end, using Content-Length, reads 30 bytes -- consuming the chunked body plus the beginning of the next legitimate request from the proxy's pipeline. The back-end now has a corrupted view of the request stream, and the attacker can inject arbitrary request fragments that the proxy never inspected.
44
+
-**Pass** — Server rejects with `400` or closes the connection (safest behavior)
45
+
-**Warn** — Server responds with `2xx` (RFC-compliant if it processes via TE and closes the connection, but the lenient path)
46
+
-**Fail** — Any other response
81
47
82
-
###Scored / Unscored Justification
48
+
## Scored / Unscored Justification
83
49
84
-
This test is **scored** -- a `2xx` response results in a **Fail**. The reasoning mirrors CLTE-PIPELINE exactly: RFC 9112 Section 6.1 uses MUST-level language requiring connection closure after a dual CL+TE request. If the pipelined `GET` receives a `2xx` response, the server kept the connection open and is demonstrably vulnerable to the TE.CL smuggling variant. Both CL.TE and TE.CL are scored because the RFC requirement is the same for both, and both represent direct, exploitable attack payloads.
50
+
This test is **scored**. Although the RFC uses MAY language, there is a clear preferred outcome: rejecting the ambiguous request is safer than accepting it. The reasoning mirrors CLTE-PIPELINE exactly.
0 commit comments