TE-OBS-FOLD

Test IDSMUG-TE-OBS-FOLD
CategorySmuggling
RFCRFC 9112 §5.2
RequirementMUST reject or unfold obs-fold
Expected400, or 2xx with connection close

What it sends

Transfer-Encoding header value wrapped using obs-fold (obsolete line folding), with Content-Length also present.

POST / HTTP/1.1\r\n
Host: localhost:8080\r\n
Transfer-Encoding:\r\n
 chunked\r\n
Content-Length: 5\r\n
\r\n
hello

The Transfer-Encoding value chunked is placed on a continuation line (preceded by CRLF and a space), using the obsolete line folding syntax.

What the RFC says

“A server that receives an obs-fold in a request message that is not within a ‘message/http’ container MUST either reject the message by sending a 400 (Bad Request), preferably with a representation explaining that obsolete line folding is unacceptable, or replace each received obs-fold with one or more SP octets prior to interpreting the field value or forwarding the message downstream.” – RFC 9112 Section 5.2

When obs-fold is used on the Transfer-Encoding header with Content-Length also present, the risk is acute: a folding-aware parser unfolds the value and sees Transfer-Encoding: chunked, while a strict parser that does not recognize the fold sees an empty Transfer-Encoding value and falls back to Content-Length. This creates a direct CL/TE desync.

Why it matters

This is a high-confidence smuggling vector. The obs-fold mechanism was deprecated precisely because of parser disagreements. When applied to Transfer-Encoding, one parser can unfold to chunked while another ignores it and falls back to Content-Length.

Deep Analysis

ABNF

field-line   = field-name ":" OWS field-value OWS  ; RFC 9112 §5
obs-fold     = OWS CRLF RWS                        ; RFC 9112 §5.2
OWS          = *( SP / HTAB )                       ; RFC 9110 §5.6.3
RWS          = 1*( SP / HTAB )                      ; RFC 9110 §5.6.3

The obs-fold rule (obsolete line folding) allows a field value to be continued on the next line if that line begins with at least one space or tab (RWS). In this test, the Transfer-Encoding value is split: the colon is followed by \r\n (CRLF) and then chunked (space + value), matching the obs-fold production.

RFC Evidence

“A server that receives an obs-fold in a request message that is not within a message/http container MUST either reject the message by sending a 400 (Bad Request), preferably with a representation explaining that obsolete line folding is unacceptable, or replace each received obs-fold with one or more SP octets prior to interpreting the field value or forwarding the message downstream.” – RFC 9112 §5.2

“A sender MUST NOT generate a message that includes line folding (i.e., that has any field line value that contains a match to the obs-fold rule) unless the message is intended for packaging within the message/http media type.” – RFC 9112 §5.2

“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 §6.3

Chain of Reasoning

  1. The test sends a Transfer-Encoding header where the value chunked is placed on a continuation line using obs-fold syntax: Transfer-Encoding:\r\n chunked. A Content-Length: 5 header is also present.
  2. RFC 9112 section 5.2 explicitly states that a sender MUST NOT generate messages with obs-fold. The sender is in violation.
  3. For the server, RFC 9112 section 5.2 provides a MUST-level requirement with two options: either reject with 400 or replace the obs-fold with spaces and interpret the field value normally.
  4. If the server chooses to unfold, it replaces the \r\n with a space and sees Transfer-Encoding: chunked. Combined with Content-Length: 5, this triggers the dual-header rules of RFC 9112 section 6.3.
  5. The critical danger is that a parser unaware of obs-fold sees the Transfer-Encoding: header as having an empty value (since the value after the colon on that line is empty), followed by what looks like a new header line starting with chunked. The space-prefixed line may be discarded as malformed or interpreted as a separate entity. This parser sees no valid Transfer-Encoding and falls back to Content-Length.
  6. Meanwhile, a folding-aware parser unfolds the value and uses chunked framing. This disagreement between folding-aware and folding-unaware parsers is the core of the smuggling vector.

Scored / Unscored Justification

This test is scored. RFC 9112 §5.2 gives two compliant server behaviors: reject with 400, or replace obs-fold with SP and continue. If unfolded, the message still carries both TE and CL, so RFC 9112 §6.1 requires closing the connection after responding.

  • Pass: 400.
  • Warn: 2xx with connection close (accepted unfold path).
  • Fail: 2xx without connection close.

Smuggling Attack Scenarios

  • Fold-Aware vs. Fold-Unaware Desync: A front-end proxy that does not implement obs-fold parsing sees Transfer-Encoding: with an empty value and ignores it, using Content-Length for framing. A back-end that implements obs-fold unfolding sees Transfer-Encoding: chunked and uses chunked framing. The attacker embeds a second request inside the chunked body that the front-end never sees.
  • Header Injection via Fold Confusion: A parser that does not recognize obs-fold may interpret the continuation line chunked as a malformed header line. Some parsers silently discard lines starting with whitespace, while others attempt to parse them as headers. This inconsistency can cause different hops in a proxy chain to see different sets of headers.
  • Selective Obs-Fold Processing: Even among folding-aware parsers, some may only unfold certain headers. A proxy that unfolds general headers but not Transfer-Encoding specifically would see an empty TE value, while the back-end unfolds all headers and processes chunked encoding. The selective unfolding creates the exact framing disagreement attackers need.

Sources