HTTP Request smuggling

HTTP request smuggling is a security exploit on the HTTP protocol that uses inconsistency between the interpretation of Content-Length and/or Transfer-Encoding headers between HTTP server implementations in an HTTP proxy server chain. It was first documented in 2005 by Linhart et al.

-- Wikipedia

How it works

  • Place both Content-Length (CL) and Transfer-Encoding (TE) headers into a single HTTP request. OR

  • Infrastructure uses HTTP/2, but not end-to-end, e.g. FE downgrades requests to HTTP/1 for BE.

Content-Length

The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.

From the HTTP RFC (RFC2616, 14.13)

Be aware that linebreaks (\r) are 2 octets/bytes long.

Example

POST / HTTP/1.1
Host: website.com
Content-Length: 11

test\r\n
123\r

Calculation

  • "test": 4 bytes

  • linebreak (\r\n): 2 bytes

  • "123": 3 bytes

  • linebreak (\r\n): 2 bytes => Content-Length: 11

Transfer-Encoding

The Transfer-Encoding general-header field indicates what (if any) type of transformation has been applied to the message body in order to safely transfer it between the sender and the recipient. This differs from the content-coding in that the transfer-coding is a property of the message, not of the entity.

From the HTTP RFC (RFC2616, 14.41)

  • For this vulnerability type we're only interested in the chunked option.

  • Each chunk starts with a hexadecimal number, determining the length of the chunk, followed by a linebreak (\r), followed by the acutal data (chunk), followed by another linebreak (\r).

  • The terminating chunk has a length of 0, an empty chunk and another linebreak, after the "empty chunk".

Example

POST / HTTP/1.1
Host: website.com
Transfer-Encoding: chunked

4\r\n
test\r\n
3\r\n
123\r\n
0\r\n
\r

Different types of request sumuggling

CL.TE

Frontend uses Content-Length, backend Transfer-Encoding.

POST / HTTP/1.1
Host: website.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
  • Frontend determines the request body is 13 bytes long (up to the end of "SMUGGLED").

  • Backend determines the first chunk is 0 and terminates the request. => "SMUGGLED" is then treated as the start of next request.

TE.CL

Frontend uses Transfer-Encoding, backend Content-Length.

POST / HTTP/1.1
Host: website.com
Content-Length: 3
Transfer-Encoding: chunked

8
SMUGGLED
0
  • Frontend determines the request body has one 8 byte long chunk (up to the end of "SMUGGLED").

  • Backend determines the request body is 3 bytes long ("8" plus 2 bytes for the following linebreak, right up until the start of "SMUGGLED"). => "SMUGGLED" is then treated as the start of the next request.

TE.TE

Both support Transfer-Encoding, but the header can be obfuscated to be only processed by one of them. This leads to either CL.TE or TE.CL depending on wether frontend or backend then uses Content-Length instead of Transfer-Encoding.

Examples

Transfer-Encoding: xchunked
Transfer-Encoding:<tab>chunked
Transfer-Encoding : chunked
[...]

For more examples see PortSwigger - Web Security Academy - TE.TE behavior: obfuscating the TE header.

HTTP/2 downgrade attacks

If HTTP/2 is not used end-to-end, but only by frontend servers, which then downgrade requests to HTTP/1 for backend servers, quite a few potential attacks are possible.

H2.CL

Eventhough HTTP/2 doesn't use a Content-Length header, some frontend servers may nevertheless copy it over when downgrading a request to HTTP/1.

H2.TE

Basically the same as H2.CL, but regarding Transfer-Encoding header.

HTTP/2 exclusive vectors

  • Injecting linebreaks into HTTP/2 requests, like: foo: bar\r\nTransfer-Encoding: chunked

  • Sending the Host header together with :authority pseudo-header may enable a range of "Host header attacks"

  • Supplying ambiguous path by applying the :path pseudo-header twice with different values

  • Injecting a HTTP/1 request line in :method pseudo-header, like :method GET /whatever HTTP/1.1

  • Injecting a URL prefix into :scheme pseudo-header, like: :scheme https://evil-site.com?

  • Injecting newlines into pseudo-headers, like: :path /path HTTP/1.1\r\nTransfer-Encoding: chunked\r\nX: x

For details, see PortSwigger - Web Security Academy - HTTP/2-exclusive vectors.

Further reading

Last updated