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) andTransfer-Encoding
(TE) headers into a single HTTP request. ORInfrastructure 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
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
Different types of request sumuggling
CL.TE
Frontend uses Content-Length
, backend Transfer-Encoding
.
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
.
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
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 valuesInjecting 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