Before diving into the lab, there are some important concepts we need to understand first: what Content-Length and Transfer-Encoding are, and how they work.

Content-Length is a header that specifies the size of the request body in bytes.

For example:

POST /sso/api/check-email/ HTTP/1.1
Host: xyz.com
Content-Length: 5

hello

Here, the Content-Length is 5 because the body (hello) consists of 5 bytes (each character = 1 byte in this case).

Transfer-Encoding is a header that defines how the request body is sent to the server. The most common type is:

Transfer-Encoding: chunked

What does chunked mean?

Instead of sending the whole body at once, the data is split into smaller parts called chunks, and each chunk is preceded by its size.

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

5
hello
0

Explanation:

  • 5 → size of the chunk (5 bytes)
  • hello → the actual data
  • 0 → indicates the end of the body

Key difference:

  • Content-Length → specifies the total size of the body
  • Transfer-Encoding: chunked → sends the body in multiple chunks, each with its own size

After understanding Content-Length and Transfer-Encoding, we need to understand how the front-end and back-end servers handle requests.

Normally, both servers should use the same method to parse the request, whether it is Content-Length or Transfer-Encoding.

But what happens if they interpret the request differently? For example, if the front-end relies on Content-Length while the back-end uses Transfer-Encoding, this creates a parsing mismatch.

This mismatch can lead to an HTTP Request Smuggling vulnerability.

This is the scenario of today's lab, and now we can proceed to solve it🔥

Here is The Lab :

None

The goal is to send a crafted request containing a smuggled second request, causing the back-end to interpret them as two separate requests. The smuggled request is then processed before the next legitimate request, which leads to the subsequent normal request returning a 404 Not Found response.

After opening the lab, we go to the home page or refresh it so that the requests appear in Burp Suite.

Then we send the request for / to Repeater.

None

The first thing we need to do is ensure that the connection is using HTTP/1.1.

Note: We need to make sure the connection is using HTTP/1.1, because HTTP request smuggling attacks rely on how HTTP/1.1 parses and handles requests. This issue does not typically exist in the same way with HTTP/2 due to its different request framing.

None

Click as shown in the image to set the connection to HTTP/1.1. After that, send a second request and name it the normal request, while the first one is named the attacker request.

None

and click change request method to make the method to post

Then remove all unnecessary headers to simplify the request and make the analysis easier.

The Final look :

None

Now we add the header Transfer-Encoding: chunked so that the back-end ignores Content-Length and processes the request using Transfer-Encoding.

None

Now, how do we confirm that the back-end is processing Transfer-Encoding?

We can test this using the following payload: ```

3
abc
c

After that, we try sending the request.

None

We notice that the response is long, and at the end it returns: Server Error: Communication timed out

So what happened?

The back-end actually understood Transfer-Encoding: chunked.

It read the chunk size 3, then read the next line and found exactly 3 bytes of data as expected.

After that, it encountered an unexpected value (c/extra input instead of a valid chunk size or the terminating 0).

Normally, 0 is used to indicate the end of the request body, meaning the server should stop reading the request. Since the format was malformed or unexpected, the back-end could not properly parse the request, which eventually led to a timeout error.

When the server encounters 0 in chunked encoding, it treats it as the end of the request body. Any data after 0 is not part of the current request anymore and may be interpreted as the start of a new request on the same TCP connection.

So why don't we try sending 0 followed by the non-existent request that would return a 404?

None

A request like this will be interpreted differently by the front-end and back-end servers. The front-end relies on Content-Length, so it considers the entire body (including everything after 0) as a single request.

However, the back-end uses Transfer-Encoding: chunked, so it reads the request body until it reaches 0, which marks the end of the request. Anything after 0 is no longer part of the same request.

Instead, the remaining data stays on the same TCP connection and is treated as a new request.

So while the front-end sees one complete request, the back-end splits it into two: one request ending at 0, and another request starting with the remaining data (e.g. /notfound), which may result in a 404 response.

###NOTE : X-ANYTHING: X is just a dummy header used to help the request be parsed correctly. It has no real functional purpose and is only there to structure the request properly.

None

Why did we get a 200 OK response when sending this request?

The back-end correctly parsed the first part of the request:

POST / HTTP/1.1
Host: ...
Content-Type: application/x-www-form-urlencoded
Content-Length: 42
Transfer-Encoding: chunked

0

From its perspective, this is a valid and complete request. The 0 correctly indicates the end of the chunked body, so it returns 200 OK.

Where is the issue?

The problem is that the front-end and back-end are not interpreting the request in the same way.

The front-end has already sent additional data (the smuggled part), but the back-end considers the request finished at 0. As a result, the leftover data is still present on the same TCP connection and will be interpreted as the beginning of a new request.

Now we send the normal request and observe what happens.

None

Boom, we get a 404 Not Found response.

None

And we find that the lab is successfully solved.

What happened?

When the normal request was sent, it was not processed as a clean request anymore.

Instead, due to request smuggling, the back-end had leftover data from the previous request still waiting on the same TCP connection.

So instead of processing:

GET / HTTP/1.1
Host: ...

The server actually interpreted the combined data as:

GET /notfound HTTP/1.1
X-ANYTHING: X
GET / HTTP/1.1
Host: …

As a result, the back-end treated the first request it parsed as /notfound, which does not exist, so it returned a 404 Not Found.

This confirms that the two requests were desynchronized and effectively "mixed together" due to request smuggling.

That's it. I hope this write-up was helpful. Stay tuned for more write-ups soon.

Linkedin : https://www.linkedin.com/in/mohamed-amgad-760169218/