Difference between CL.0 Desync and HTTP Pipelining?

I am trying to decide if my server is vulnerable to CL.0 Desync attack. I did some tests but not sure if it is normal behavior of HTTP pipelining or CL.0 Desync.

Tool: Blurp Community Edition

query 1:

POST /login HTTP/1.1
Host: 127.0.0.1
Referer: https://127.0.0.1
Content-Length: 0


GET /api/status HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive

Response:

HTTP/1.1: 401 Unauthorized
Set-Cookie: ....
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Cache-Control: must-revalidate,no-cache,no-store
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Content-Length: 74

HTTP Status 401 - Full authentication is required
HTTP/1.1: 401 Unauthorized
Date: Wed, ...
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Content-Length: 74

HTTP Status 401 - Full authentication is required

401 is expected for both endpoints because no token is passed.

query 2:

POST /login HTTP/1.1
Host: 127.0.0.1
Referer: https://127.0.0.1


GET /api/status HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive

Response:

HTTP/1.1: 401 Unauthorized
Date: Wed, ...
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Content-Length: 74

HTTP Status 401 - Full authentication is required

I am not sure for query 1, if it is just normal pipelining behavior. What’s the difference anyway?

Thank you.

The CL.0 desync attack leverages discrepancies between the way servers process Content-Length: 0 headers and HTTP request pipelining. Understanding the behavior you are seeing in your tests is crucial to determine whether it is normal or indicative of a potential vulnerability.

Key Concepts to Understand

  1. HTTP Pipelining Behavior:
  • In normal HTTP pipelining, multiple requests are sent over a single TCP connection without waiting for the response to the first request.
  • The server processes requests sequentially and generates responses in the same order.
  1. CL.0 Desync:
  • The CL.0 attack exploits the Content-Length: 0 header in HTTP requests to manipulate how subsequent pipelined requests are interpreted.
  • Vulnerable servers may misinterpret the second request’s boundary, allowing an attacker to smuggle or poison requests.

Analyzing Your Queries

Query 1 Analysis

Request:

POST /login HTTP/1.1
Host: 127.0.0.1
Referer: https://127.0.0.1
Content-Length: 0

GET /api/status HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive

Response:

HTTP/1.1: 401 Unauthorized
...
HTTP Status 401 - Full authentication is required
HTTP/1.1: 401 Unauthorized
...
HTTP Status 401 - Full authentication is required

Here’s what happens:

  • The first POST /login request includes Content-Length: 0. This tells the server that the body of the POST request is empty.
  • The GET /api/status request follows in the same TCP stream.
  • Result: The server correctly interprets the two requests as separate, responds with 401 Unauthorized for both requests, and the responses are in order.

This behavior appears normal for HTTP pipelining.


Query 2 Analysis

Request:

POST /login HTTP/1.1
Host: 127.0.0.1
Referer: https://127.0.0.1

GET /api/status HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive

Response:

HTTP/1.1: 401 Unauthorized
...
HTTP Status 401 - Full authentication is required

Key differences from Query 1:

  • The POST /login request omits the Content-Length header.
  • This makes the request ambiguous because the server may not know when the POST request ends and the GET request begins.
  • Result: If the server is not vulnerable, it processes the requests correctly and interprets the boundary of the two requests based on the Content-Length header of the second request or other heuristics.

What to Watch For

The difference between normal pipelining behavior and a desync vulnerability lies in whether the server misinterprets where one request ends and the next begins.

Vulnerability Indicators

  • If the server incorrectly treats the second request as part of the first, leading to:
    • Unexpected responses (e.g., 200 OK for /api/status instead of 401 Unauthorized).
    • Merged or malformed responses.
  • If the server adds unintended behavior in the response due to smuggling or request poisoning.

In your case:

  1. Query 1 appears normal and expected for pipelining, as the server processes both requests independently.
  2. Query 2 could be problematic, but only if the server misinterprets the boundary of requests (e.g., treating GET /api/status as part of the body of POST /login). Since you received 401 Unauthorized for both, it likely processed the requests correctly.

Confirming Vulnerability

To be sure, perform these additional checks:

  1. Add a body to the POST request in Query 2 (e.g., Content-Length: 5 and include test as the body). Observe how the server handles the GET request.
  2. Test a malicious payload, such as deliberately smuggling part of the GET request into the body of the POST request. For example:
POST /login HTTP/1.1
Host: 127.0.0.1
Content-Length: 30

GET /api/status HTTP/1.1

Host: 127.0.0.1

If the server responds unexpectedly (e.g., treating /api/status as part of /login), it may be vulnerable.
3. Use automated tools: Tools like Burp Suite’s “HTTP Request Smuggling” module can automate detection of desync vulnerabilities.


Mitigations

If your server is vulnerable:

  1. Disable HTTP/1.1 pipelining if not strictly required.
  2. Enforce proper request parsing:
  • Ensure every request has an explicit Content-Length or is terminated by \r\n\r\n.
  • Reject ambiguous requests (e.g., POST requests without Content-Length).
  1. Upgrade your reverse proxy/load balancer (e.g., NGINX, Apache) to the latest version, as newer releases often mitigate these attacks.

If the behavior aligns with normal pipelining, your server is likely not vulnerable, but periodic testing remains important.