Earlier this year, a vulnerability was disclosed in ASP.NET Core's
Kestrel HTTP server — CVE-2025-55315. As part of my coursework in
SWE-681 at George Mason University, my teammate Sehaj Gill and I
analyzed this vulnerability and proposed a formal case study for
MITRE's public secure coding repository.
Here's a technical breakdown of what went wrong, how it could be
exploited, and how it was fixed.
What is HTTP Request Smuggling?
HTTP Request Smuggling is an attack that exploits disagreements between
two servers — typically a reverse proxy and an origin server — about
where one HTTP request ends and the next begins.
When a proxy forwards traffic to a backend server, both need to agree
on message boundaries. If they parse the same request differently, an
attacker can "smuggle" a malicious request hidden inside a legitimate
one — bypassing security controls, poisoning shared caches, or
hijacking other users' sessions.
The Coding Mistake in Kestrel
Kestrel is ASP.NET Core's built-in HTTP server. The vulnerability was
rooted in how Kestrel parsed chunked HTTP request bodies.
In chunked transfer encoding, each chunk must be terminated with CRLF
(\r\n) as required by RFC 9112. Kestrel's parser was permissive — it
accepted malformed chunk-extension line endings that did not strictly
enforce this CRLF requirement.
This single parsing decision created a differential between Kestrel
and upstream reverse proxies. The proxy would parse the request one
way. Kestrel would parse it another way. That gap is exactly where
request smuggling lives.
The vulnerable code was in Http1ChunkedEncodingMessageBody.cs —
the file responsible for parsing chunked request bodies in Kestrel's
HTTP/1.1 pipeline.
How the Attack Works
An attacker sends a carefully crafted HTTP request with malformed
chunk-size line endings — using bare LF instead of CRLF.
The reverse proxy normalizes it and forwards it to Kestrel as one
request. But Kestrel's permissive parser interprets the boundaries
differently — seeing two requests where the proxy saw one.
The "second" request — the smuggled one — is now sitting in Kestrel's
pipeline, processed as if it came from the next legitimate user.
Depending on the application, this can lead to:
- Security control bypass
- Cache poisoning
- Session hijacking
- Unauthorized access to other users' data
The Fix — PR #64037
The fix was committed in PR #64037 in the dotnet/aspnetcore repository.
Three key changes were made:
1. Strict CRLF enforcement
The parser now strictly rejects chunk-size lines that do not terminate
with CRLF as required by RFC 9112. Bare LF and bare CR are no longer
accepted.
2. Fail-closed error handling
Malformed framing now returns HTTP 400 Bad Request immediately. The
old behavior normalized or forwarded ambiguous input — the new behavior
rejects it hard. This is the fail-closed principle: when in doubt,
refuse.
3. Negative regression tests
The fix added dedicated tests for malformed line terminators and chunk
extensions. These tests verify that malformed input is rejected — not
just that valid input is accepted.
Prevention Principles
This vulnerability illustrates several universal secure coding
principles:
Strict RFC conformance over permissive parsing
Be a strict reader, lenient writer. Parse input exactly as the spec
requires. Any deviation from the spec is potential ambiguity — and
ambiguity between components is where smuggling attacks live.
Fail-closed design
Malformed input should fail loudly and immediately. Never normalize,
forward, or silently accept input that violates protocol rules.
Parser-differential testing
If your system sits behind a proxy or load balancer, test how both
components parse the same edge-case inputs. A differential is a
vulnerability waiting to be exploited.
Negative test coverage
Your test suite should verify what your parser REJECTS, not just what
it accepts. Malformed inputs, boundary violations, and protocol
deviations all need explicit negative tests.
Why This Matters
HTTP Request Smuggling is not a niche attack. It has been used to
bypass WAFs, hijack sessions on major platforms, and escalate
server-side request forgery vulnerabilities.
A single permissive line in a parser — accepting bare LF where CRLF
was required — was enough to open the door.
The lesson: security vulnerabilities often live in the gap between
what the spec says and what the implementation accepts. Strict
parsing, fail-closed design, and comprehensive negative testing are
not optional — they are the baseline.
This case study was proposed to MITRE's secure coding case studies
repository as part of SWE-681 at George Mason University.
Full proposal: github.com/mitre/secure-coding-case-studies/issues/72
CVE reference: CVE-2025-55315
Top comments (0)