Demo
Learn how Cobalt’s Pentest as a Service (PtaaS) model makes you faster, better, and more efficient.
Demo
Learn how Cobalt’s Pentest as a Service (PtaaS) model makes you faster, better, and more efficient.

A Dive into Client-Side Desync Attacks

A client-side desync, a.k.a CSD, is an attack in which the victim's web browser is tricked into desynchronizing its connection to the vulnerable website. Core Pentester Harsh Bothra takes a look at how attackers can find these vulnerabilities in the wild.

This blog is based on the research by James Kettle from PortSwigger that can be found here and here

A client-side desync, a.k.a CSD, is an attack in which the victim's web browser is tricked into desynchronizing its connection to the vulnerable website. This is different from regular request smuggling attacks, which cause the connection between a front-end server and a back-end server to desynchronize.

Standard desync/request smuggling attacks depend on specially crafted requests that common browsers will never send. As a result, only websites with a front-end/back-end architecture are vulnerable to these attacks. However, utilizing completely browser-compatible HTTP/1.1 requests can result in a desync. This creates new opportunities for server-side request smuggling attacks and makes client-side desync attacks a whole new class of threat.

Sometimes it is advised for web servers to respond to POST requests without examining the body. A client-side desync vulnerability arises if they subsequently permit the browser to use the same connection for additional requests. In some cases, it is possible to convince servers to ignore the Content-Length header, meaning they will think each request ends at the end of the headers. This is equivalent to setting the Content-Length to 0. 

The Client-side desync attack is possible if the back-end server displays the above-mentioned behavior while the front-end continues to rely on the Content-Length header to detect when a request has been completed.

Hunting for Client-Side Desync in the Wild

Finding or creating a request that causes the server to ignore the Content-Length header is the first step in testing for client-side desync attacks. Sending a request with a Content-Length that is longer than the actual content is the easiest approach to test for this attack.

For the practical demonstration, we will be using Portswigger labs. Follow the below-mentioned steps to get started:

  1. Capture the request using Burp Suite or other proxy tools to the GET/endpoint. 

  2. Notice the / endpoint redirects to the /en endpoint by default.

  3. Send a request to the repeater tab, and from settings, disable the Update Content-Length option.

  4. Change the request method to POST and update the content length to a value greater than 1.

  5. Leave the request body empty and send the request like the below request:

  1. Observe how the server replies immediately without waiting until the body arrives. This indicates that the application's backend is ignoring the Content-Length header.

Confirming the Client-Side Desync Vulnerability

We have verified that the server is ignoring the content-length header; now it’s time to confirm if the application is vulnerable to a CSD attack. Follow the steps to get started.

  1. Send the same GET request to the second tab of the repeater.

  2. Re-enable the Update Content-Length option from settings.

  3. Click on the + icon and select the option: Create tab group.

  1. Create a group by adding both tabs.

  1. In the first tab of a POST request, add the arbitrary prefix to the request body:

GET /404 HTTP/1.1

Foo: x

  1. Update the connection header as Connection: Keep-alive

  1. Change the Send option to Send Group (single connection).

[Note: Repeater's Send group in sequence feature lets you send a series of grouped HTTP requests with a single click.]

  1. Send the request and navigate to the second tab and observe that the response received is “Not found,” which is from the second request in the body. This confirms the desync attack.

Testing in the Browser

We have successfully confirmed the CSD attack via Burp Suite (proxy tool);   test the same in the browser.

  1. Open the separate browser and navigate to the exploit server of the Portswigger lab.

  2. Open the developer tools of the browser and navigate to the Network tab.

  3. Clear the existing log entries and enable the Preserve log option.

  4. Navigate to the Console tab and replicate the attack using fetch() API:

 

fetch('https://YOUR-LAB-ID.web-security-academy.net', {

    method: 'POST',

    body: 'GET /hopefully404 HTTP/1.1\r\nFoo: x',

    mode: 'cors',

    credentials: 'include',

}).catch(() => {

        fetch('https://YOUR-LAB-ID.web-security-academy.net', {

        mode: 'no-cors',

        credentials: 'include'

    })

})

 

Client-side desyncs are commonly caused by requests to endpoints that result in server-level redirects. This poses a little challenge when developing an exploit because browsers will follow this redirect, disrupting the attack sequence.

CORS error is intentionally triggered to prevent the browser from following the redirect by setting the mode: cors. The catch() method is used to continue the attack sequence.

  1. Navigate to the Network tab and observe the requests:

    1. A triggered CORS error from the main request.

    2. 404 response to the home page request.

Exploiting the Client-Side Desync

The vulnerable application has a comment section reflecting the user's input. By exploiting the desync attack, we can fetch the other user's request containing the session token.

  1. Navigate to the comment section.

  2. Comment on the post while capturing the request in Burpsuite.

  1. Note the CSRF token, Cookie, and PostID from the request.

  1. To capture your arbitrary request in a comment, paste the following in the first tab of the repeater.

POST / HTTP/1.1

Host: YOUR-LAB-ID.web-security-academy.net

Connection: keep-alive

Content-Length: CORRECT

 

POST /en/post/comment HTTP/1.1

Host: YOUR-LAB-ID.web-security-academy.net

Cookie: session=YOUR-SESSION-COOKIE; _lab_analytics=YOUR-LAB-COOKIE

Content-Length: NUMBER-OF-BYTES-TO-CAPTURE

Content-Type: x-www-form-urlencoded

Connection: keep-alive

 

csrf=YOUR-CSRF-TOKEN&postId=YOUR-POST-ID&name=wiener&email=wiener@web-security-academy.net&website=https://ginandjuice.shop&comment=

  1. In the second tab of the repeater, paste the following request.

Keep in mind that the number of bytes you attempt to capture must be greater than the length of the body of your POST /en/post/comment request prefix but less than the length of the follow-up request.

  1. Navigate to the comment section and refresh the browser; observe that the output of GET /capture-me has successfully started.

  1. For the final exploit, navigate to the exploit server of the Portswigger lab.

  2. Insert the script within the <script> tags in the body panel.

  3. Update the content-length number more than that used previously. Store the exploit and click on deliver to the victim. 

  4. Observe that you will get more responses after updating the content-length header of the vulnerable request.

  1. Keep updating the content length until you get the full response.

Final payload:

  1. We have successfully retrieved the victim's session token after updating the correct content length.

This is how one can attempt to Identify, Validate and Exploit a Client-Side Desync attack. There could be more attack vectors, but it is essential to understand the mitigations we will discuss in the below section.

Remediations:

Below are a few best practices you should implement to prevent such attacks.

  • Requests with both Transfer-Encoding and Content-Length that do not match the payload size should be blocked.

  • Implement strict controls on the server, so it never assumes requests won't have a body.  This is the root of client-side desync vulnerabilities.

  • Utilize two different values to prevent duplicate headers.

  • To improve back-end communication, use HTTP/2. Using a framed structure, HTTP/2 eliminates ambiguity about where HTTP packets stop. Both the Content-Length and Transfer-Encoding headers are no longer required with HTTP/2; in addition, HTTP/2 explicitly restricts the usage of chunked transfer encoding.

References:

Back to Blog
About Harsh Bothra
Harsh Bothra is a Security Engineer with expertise in Web application, API, Android Application, Thick Client, and Network Pentesting. He has over 5 years of experience in Cybersecurity and penetration testing. He has written multiple books on ethical hacking including Mastering Hacking and Hacking: Be a Hacker with Ethics, presented at various security conferences, and is an active bug bounty hunter. More By Harsh Bothra
Hacking Web Cache - Deep Dive in Web Cache Poisoning Attacks
Web cache poisoning is an attack where an attacker takes advantage of flaws in the caching mechanism. They attempt to store an altered and malicious response in the cache entry, forcing the website to serve malicious information to its users.  Core Pentester Harsh Bothra deep dives into these attacks and remediations.
Blog
Jan 31, 2023
Web Socket Vulnerabilites
WebSockets are an exciting technology that has been gaining traction in the industry. Many companies are using the technology, especially in their real-time services
Blog
Sep 27, 2022