Headers Involved

Emphasis on headers:

  • Content-Length that states the number of characters of the content, example:
POST /update HTTP/1.1
Host: example.com
Content-Length: 13
Content-Type: application/x-www-form-urlencoded

isadmin=true
  • Transfer-Encoding that states the number of characters of the content in hexadecimal, example:
POST /search HTTP/1.1
Host: example.com
Transfer-Encoding: chunked

e
q=smuggledData
0

Transfer-Encoding usually has the value of chunked, others include compress, deflate and gzip.

HTTP Smuggling when the front-end and back-end server prioritise one header over another, so when both are used in a HTTP request, it may cause inconsistent responses between front and back end servers.


CL.TE Example

CL.TE means Content-Length/Transfer-Encoding. So front end prioritises CL and back end prioritises TE.

POST /search HTTP/1.1
Host: example.com
Content-Length: 130
Transfer-Encoding: chunked

0

POST /update HTTP/1.1
Host: example.com
Content-Length: 13
Content-Type: application/x-www-form-urlencoded

isadmin=true

HTTP Smuggling is basically sneaking in a message in another.

Here, the front-end server sees the Content-Length of 130 bytes and believes the request ends after isadmin=true. However, the back-end server sees the Transfer-Encoding: chunked and interprets the 0 as the end of a chunk, making the second request the start of a new chunk. This can lead to the back-end server treating the POST /update HTTP/1.1 as a separate, new request, potentially giving the attacker unauthorized access.

Be mindful of incorrect Content-Length. If it's value is set to 25 but the actual length of content is 30, the server will only process the first 25 characters.


TE.CL Example

TE.CL means Transfer-Encoding/Content-Length. So front end prioritises TE and back end prioritises CL.

POST / HTTP/1.1
Host: example.com
Content-Length: 4
Transfer-Encoding: chunked

78
POST /update HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

isadmin=true
0

In the above payload, the front-end server sees the Transfer-Encoding: chunked header and processes the request as chunked. The 78 (hexadecimal for 120) indicates that the next 120 bytes are part of the current request's body. The front-end server considers everything up to the 0 (indicating the end of the chunked message) as part of the body of the first request.

The back-end server, however, uses the Content-Length header, which is set to 4. It processes only the first 4 bytes of the request, not including the entire smuggled request POST /update. The remaining part of the request, starting from POST /update, is then interpreted by the back-end server as a separate, new request.


TE.TE Example

TE.TE means Transfer-Encoding/Transfer-Encoding AKA Transfer Encoding Obfuscation. So front end prioritises TE and back end prioritises TE.

The TE.TE vulnerability doesn't always require multiple Transfer-Encoding headers. Instead, it often involves a single, malformed Transfer-Encoding header that is interpreted differently by the front-end and back-end servers.

Aim: make one server ignore the TE header and use CL instead

POST / HTTP/1.1
Host: example.com
Content-length: 4
Transfer-Encoding: chunked
Transfer-Encoding: chunked1

4e
POST /update HTTP/1.1
Host: example.com
Content-length: 15

isadmin=true
0

In the above payload, the front-end server encounters two Transfer-Encoding headers. The first one is a standard chunked encoding, but the second one, chunked1, is non-standard. Depending on its configuration, the front-end server might process the request based on the first Transfer-Encoding: chunked header and ignore the malformed chunked1, interpreting the entire request up to the 0 as a single chunked message.

The back-end server, however, might handle the malformed Transfer-Encoding: chunked1 differently. It could either reject the malformed part and process the request similarly to the front-end server or interpret the request differently due to the presence of the non-standard header. If it processes only the first 4 bytes as indicated by the Content-length: 4, the remaining part of the request starting from POST /update is then treated as a separate, new request.