You send a normal-looking POST request through Burp at a target sitting behind a load balancer, and the response that comes back doesn't match anything you asked for. Not an error, not a timeout, an actual response body that reads like it belongs to a completely different request, one you never sent. Reissue the exact same request and it's fine again. Most people click retry, shrug, and move on, because "it works on the second try" doesn't feel like a finding. It's usually the single most interesting thing that happened in the assessment.
What you likely walked into is a request smuggling desync, and the mechanism behind it is almost embarrassingly simple once you see it: two different systems in the request path, usually a front-end proxy or load balancer and a back-end application server, disagreeing about where one HTTP request ends and the next one begins. That disagreement almost always comes down to one specific ambiguity: a request carrying both a Content-Length header and a Transfer-Encoding: chunked header at the same time. The spec says Transfer-Encoding should win when both are present, but plenty of real server software doesn't enforce that consistently, and the two systems in front of and behind the load balancer don't have to agree on which one they're parsing by.
Say the front-end proxy trusts Content-Length and the back-end trusts Transfer-Encoding, the classic CL.TE case. Craft a request where Content-Length says the body is short, but a chunked-encoded body inside it actually contains a second, hidden request tacked onto the end. The front-end reads exactly Content-Length bytes, decides the request is done, and forwards it. The back-end, parsing by Transfer-Encoding instead, processes the chunked body, finds the "extra" bytes at the end, and treats them as the start of the next request on that same connection. Since back-end connections are routinely pooled and reused across different users to save overhead, that leftover fragment doesn't disappear, it sits there and gets prepended to whatever the next real user's request turns out to be. Their request gets mangled by your fragment, and the response you see, the one that "belongs to someone else," is a side effect of exactly that: you're watching the queue get poisoned in real time.
The reason this is worth more than a shrug is what it can do once you can trigger it reliably: hijack another user's session by poisoning their next request, bypass a front-end security control (an auth check or a WAF rule the proxy enforces) because the back-end never sees the request the way the proxy thought it forwarded it, or poison a cache so the wrong response gets served to everyone hitting that path afterward. None of that requires a new vulnerability in the application itself. It's entirely a disagreement about where a request ends, and the interview-style version of the question, "why did this response not match my request," is really just asking whether you know that HTTP/1.1 request framing has an ambiguity built into it that two pieces of software can resolve two different ways.
Recognizing the smell matters more than memorizing a specific payload: a response that doesn't correspond to the request you sent, a request that seems to "eat" part of the following one, or timing that suggests the back-end is waiting on bytes that never arrive are all the same signal. Codelivly's HTTP Protocol Book covers exactly the header and request-structure fundamentals underneath this, how Content-Length and chunked Transfer-Encoding are each supposed to behave and where HTTP/1.1 and HTTP/2 handle framing differently, which is the actual prerequisite for understanding why this ambiguity exists in the first place instead of just pattern-matching a payload you found online. The free HTTP Headers CTF challenge on codelivly.com is a good place to get comfortable reading headers closely before chasing something this subtle.
Top comments (0)