October 28, 2025 ~24 min read
I admit, that's a very click-baity headline, but Microsoft have given the vulnerability a CVSS score of 9.9, their highest ever. Time to panic, right?
In this post I try to provide a bit more context. I explain how request smuggling vulnerabilities work in general, how it works in this case, what attackers could use it for, how the vulnerability was fixed, what you can do to protect yourself.
WARNING: I am not a security professional, so do not take anything in this post as gospel or advice. I'm just a developer trying to make sense of things. 😄 All of the details in this post are based on information that was provided or referenced in the original announcement.
What is the CVE-2025-55315 vulnerability?
On October 14th 2025, on a standard Microsoft "patch Tuesday", Microsoft released new versions of all their supported versions of .NET, and also published a security advisory: Microsoft Security Advisory CVE-2025-55315: .NET Security Feature Bypass Vulnerability. The high level summary from that announcement said:
Inconsistent interpretation of http requests ('http request/response smuggling') in ASP.NET Core allows an authorized attacker to bypass a security feature over a network.
The advice was "patch all of your things", but the real headline was that this vulnerability was given a CVSS score of 9.9 our of 10, which you know, sounds pretty bad! Barry Dorrans AKA blowdart, .NET security head honcho, gave an explanation of the reasoning behind the score in a comment on the original issue:
The bug enables HTTP Request Smuggling, which on its own for ASP.NET Core would be nowhere near that high, but that's not how we rate things...
Instead, we score based ~~~~h~~ow~~ the bug might affect applications built on top of ASP.NET.
Request Smuggling allows an attacker to hide an extra request inside an another, and what that hidden request can do is very application specific.
The smuggled request could cause your application code to
- Login as a different user (EOP)
- Make an internal request (SSRF)
- Bypass CSRF checks
- Perform an injection attack
But we don't know what's possible because it's dependent on how you've written your app.
That does all sound pretty scary! 😱 So you can understand the consternation that the issue has caused, especially given the hesitation to explain exactly what "how you've written your app" means.
Out of curiosity, I decided to dig in further to really understand this vulnerability, how it could impact you, and what "how you've written your app" could mean.
How does request smuggling work?
Before we get to the actual patched vulnerability in ASP.NET Core and how the vulnerability works, I think it's important to have some background about the general class of exploits known as HTTP request smuggling.
HTTP request smuggling is a security exploit that has been known about for a long time (according to Wikipedia, it was first documented in 2005). It fundamentally arises when you have two different servers processing an HTTP request (e.g. a server and a proxy server), and where those two servers differ in how they handle "invalid" HTTP requests.
In all cases of HTTP request smuggling, the exploit works by creating an invalid HTTP request (or sometimes just an ambiguous request), that looks a bit like two HTTP requests glued together. In summary, the exploit then works a bit like this:
- The proxy server receives the ambiguous HTTP request
- The proxy server forwards the request (unmodified) to the destination server
- The server interprets the ambiguous request as two pipelined HTTP requests sent to the server, and processes them separately.
I think it's easiest to understand the problem with an example, so the request below shows an example from the original 2005 paper.
Note that this is not an example of the request smuggling vulnerability in CVE-2025-55315, it's just a representative example of request smuggling in general.
Let's imagine the attacker sends an HTTP request that looks like this:
The important feature of this request is that there are two Content-Length headers, with different values: 9 or 204. This is the core of the exploit; the difference between which of the these two headers the HTTP proxy and HTTP server honour is what causes the vulnerability.
Let's walk through how the exploit works, step-by-step:
- The attacker sends the above HTTP request.
- The HTTP proxy receives the request, notes the duplicate Content-Length headers, and accepts the second header, the 204 length. That means the whole rest of the request is treated as the message body, and seems fine as far as the proxy is concerned.
- The HTTP proxy forwards the request on to the destination server.
- This server also notes the duplicate Content-Length header, but it takes the first of the headers, with the length of 9.
- The server reads 9 bytes of the body (i.e. this=that) and treats that as the whole request. As far as the server is concerned, the whole (valid) request has been received, and it sees the rest of the data as a whole new request.
- That means that the destination server sees an entirely new HTTP request to process, POST /vuln_page.jsp, and treats it as a new request.
That's the core of the issue; the proxy saw one request, while the destination server saw two—the second request has been "smuggled" past the proxy to the server.
The request smuggling technique shown here, where you have multiple Content-Length headers isn't the "canonical" example you'll generally see referenced, but I used it here because it's simpler to understand in a lot of ways.
The canonical request smuggling attack is where you send both a Content-Length header and a Transfer-Encoding: chunked header (which specifies the length of the body as part of the body itself). As before, the request smuggling exploit relies on differences in how proxy and destination servers interpret these conflicting headers.
So as you've seen, request smuggling enables sending a secret request to a destination server that an intermediate proxy server hasn't seen. In the next section we'll look at why that's a bad thing, and how it can be exploited.
How can an attacker exploit request smuggling?
On the face of it, request smuggling might not seem like a big deal. So the server sees two requests, so what? You could always send two requests to the server anyway, right? Well, yes and no.
The issue with request smuggling is really all about the mismatch between the proxy and destination servers. Thanks to this mismatch, and depending on what behaviours and expectations the target application has, attackers can use request smuggling to
- Reflect malicious data to other users on sites that are vulnerable to cross-site scripting.
- Poison caches with bad data.
- Exfiltrate authentication credentials or other data from client requests.
- Invoke endpoints that shouldn't be publicly accessible (because the proxy would block external access to them).
- Replace/override authentication controls handled by the proxy.
- Redirect users to malicious sites on sites vulnerable to open-redirect attacks.
- And more…
As you can see, these are all Bad™️, so you can kind of understand why the 9.9 rating was given! 😱
That said, it's worth mentioning that not all of these attacks will be fruitful against all applications. Some of the easiest to understand versions of these exploits are where the proxy is not just doing "dumb" forwarding of requests, but rather it's validating or enhancing the request in some way.
For example, if you have a proxy sat in front of your server which is responsible for handling TLS termination and client-authentication and identification using certificates, then request smuggling could be used to bypass these checks and insert your own identification.
As an example of that attack, the HTTP request below demonstrates using a Content-Length and Transfer-Encoding request smuggling attack to "hide" the request to /admin from the front-end proxy, and insert a malicious X-SSL-CLIENT-CN header, which would normally be added by the front-end proxy:
In this example, the server assumes that the X-SSL-CLIENT-CN: administrator header was added by the proxy, and so the server assumes that the proxy already did all the necessary authentication and authorization. The attacker is able to perform a request as an entirely different user.
Request smuggling is clearly a big problem whenever you have a front-end proxy that does some functionality, but even when it's essentially a dumb proxy, request smuggling can still be used to steal and exfiltrate data from other user's requests, even if the attacked site is not vulnerable to cross-site scripting or other vulnerabilities.
In these attacks, simply having functionality that displays data provided by a user (even sanitised) can be sufficient to steal the credentials of other users. So something as simple as displaying a user name or a comment could be sufficient.
This post is long enough, and there are so many different attacks, that I'm going to leave it there for looking at exploits. If you'd like to learn more about what's possible, along with simple explanations and examples of exploits, I recommend the PortSwigger documentation on exploiting request smuggling.
Does request smuggling only apply if I have a proxy?
In general, whenever people talk about request smuggling, they normally talk about the case where you have multiple servers: the canonical example is a proxy server and a destination server, as I've discussed so far. But don't be fooled, these issues and vulnerabilities can apply even if you aren't strictly using a proxy.
The key feature of the vulnerability is that there's an opportunity for confusion between two "systems", whether they're full "servers" or not. This obviously applies to proxy servers, but could also apply to your application if you're doing anything where you're reading/manipulating/forwarding request streams, or where there's the possibility for confusion inside the same application.
For ASP.NET Core applications, if you're working with HttpRequest.Body or HttpRequest.BodyReader, or other similar methods then you may be vulnerable to attacks even if you're not explicitly using a proxy server. Even if you don't think of your application as a proxy or as using a proxy, if you're doing "proxy-like" things, then you could be vulnerable.
Put in other words, if you're reading, manipulating, or forwarding request streams directly in ASP.NET Core, as opposed to just relying on the built-in model binding, then you could be at risk to request smuggling attacks. It's very hard to enumerate all the attack vectors, so you should consider any code that does so as a potential avenue of exploitation.
We've now covered how request smuggling works and can be exploited in general, so it's time to look at the specific version of request smuggling that is targeted in the .NET CVE-2025-55315 vulnerability.
How does the request smuggling in CVE-2025-55315 work?
As we've seen, HTTP request smuggling is a general technique that relies on differences between proxies and servers in how they parse HTTP requests. I've shown two specific versions of this so far: duplicate Content-Length headers, and Content-Length/Transfer-Encoding confusion, but these are not exhaustive. There are variations on these approaches which also lead to request smuggling.
The request smuggling vulnerability in CVE-2025-55315 relies on a variation which (as far as I can tell) was first reported in June 2025 by Jeppe Bonde Weikop on their blog. This variation relies on Transfer-Encoding and the Chunk Extensions feature.
All the details and images in this section are based on the descriptions and examples in the original post. That post is excellent, so if you want even more detail and explanation than here, you should definitely read it, and then you can skip the abbreviated version I provide here.
To understand the vulnerability, we'll first look at how chunked transfer encoding works and what chunk extensions are. We'll then look at how invalid line-endings can lead to differences in interpretation of a request. Finally, we'll look at how this difference in interpretation can open the way for request smuggling, and how ASP.NET Core fixed the problem.
Transfer-Encoding: chunked and chunk extensions
To understand the vulnerability, we first need to understand how Transfer-Encoding: chunked works, and how chunk extensions complicate things.
When you're sending a request, you might not always know up-front how big the request is that you're sending. Let's take a practical example of serializing a .NET object to JSON into a request body. The only way to know for sure how big the serialized data is going to be is to actually serialize it. So you could serialize the data to memory before writing the request, but if the data is very big, then that could cause issues with allocating big arrays.
Instead, Transfer-Encoding: chunked allows sending the request data in multiple "chunks". You need to know the size of each individual chunk, but not the overall size of the data, or how many chunks there are. This works well for serializing to a small buffer, sending that small buffer as a chunk, and then re-using the buffer to serialize the next part, until you have serialized the whole object.
In terms of the HTTP request itself, each chunk consists of a header and a body. The header consists of a hexadecimal-formatted number of bytes, followed by a \r\n (CRLF) line ending. The chunk body is then the specified number of bytes, followed by another \r\n. You can have as many chunks as you need, and the request will keep being passed until you send a 0 length chunk, which indicates the end of the request.
As an example, the following HTTP POST shows posting some JSON to an endpoint, but the JSON is sent as three distinct chunks:
- Chunk 1: The header is 9 indicating 9 bytes will be sent (followed by \r\n), and then the 9 bytes of the start of the JSON document in the chunk body, again followed by \r\n.
- Chunk 1: The header is e indicating 14 bytes (14 in hexadecimal is e) will be sent (followed by \r\n), and then the remaining 14 bytes of the end of the JSON document, followed by \r\n.
- The final chunk is an "empty" chunk, 0\r\n\r\n, indicating the end of the request.
We're going to see shortly that line endings are very important, so the following diagram shows the same as the above HTTP request, but with the line endings included:
That's "normal" chunked transfer encoding, so now we come to chunk extensions. Chunk extensions are part of the HTTP 1.1 protocol which allows for adding key-value pairs of metadata to individual chunks. The following example shows the same request as before, but with a chunk extension, ;foo=bar in the second chunk:
A chunk extension is indicated by a ; after the chunk header length, followed by one or more key-value pairs in the form key=value. It's important to understand that chunk extensions are not part of the data that's seen by a request handler; chunk extensions are just metadata about the individual chunk. And tl;dr; they're completely useless 😅
To the closest approximation, no-one cares about chunk extensions; client implementations don't send them, and servers just ignore them. If that's the case, how can they be the cause of such a problematic bug in .NET?
The problem is how the implementation ignores them…
Invalid chunk extensions with incorrect line endings
In general with HTTP, clients and server implementations often try to follow the robustness principle of "be conservative in what you send, and lenient with what you accept". Unfortunately, it's this very leniency which can sometimes leave us in hot water. After all, it was leniency around requests containing both a Content-Length and Transfer-Encoding header that was the root cause of the original request smuggling exploit.
Note that the HTTP 1.1 RFC now forbids forwarding both these headers, precisely to avoid request smuggling attacks.
For chunk extensions though, leniency is often accidentally built in to the server implementations. Given that no implementations actually do anything with the chunk extensions, the canonical approach to handling them when parsing a chunk header is just to ignore them. When a ; is parsed, it's common to just look for the end of the line, and ignore everything in between.
For ASP.NET Core (prior to the fix), on finding a ; in the chunk header, Kestrel would "parse" the extension, but in practice, it would search for the carriage return \r and then check for the following \n, skipping everything in between, a little bit like this (very simplified compared to original code):
The implementation in ASP.NET Core wasn't particularly special; most servers simply skip over the bytes until they find a \r\n. The big question is exactly how the servers search for \r\n. What happens if they see a lone \r, or a lone \n? Do they treat that the same as a \r\n? Do they throw an error if they find an un-paired \r or \n? Or do they ignore it and keep looking for a \r\n?
That ambiguity is at the heart of the CVE-2025-55315 request smuggling vulnerability. Differences in how proxy and server implementations treat standalone \r or \n in a chunk header allow for request smuggling exploits that use this ambiguity.
Note that according to the RFC, implementers must not treat \r or \n as "valid" line terminators for a chunk header, and neither \r or \n are allowed elsewhere in chunk headers, so correct implementations must reject requests that include these standalone line endings in chunk headers.
For complete clarity, the following example is the same as the previous implementation but with an invalid chunk header in the chunk extension of the second chunk. Instead of ending with \r\n, the chunk extension ends with a single \n:
That's the root cause of the request smuggling vulnerability, so in the next section we'll look at how this could be used to craft a malicious HTTP request.
Exploiting invalid chunk extensions for request smuggling
Just as with other examples of request smuggling, the chunk extensions approach relies on differences in how a proxy parses a request compared to a subsequent server. This difference means the proxy sees one request, while the destination request sees two requests, and allows for all the same exploits I discussed earlier.
As discussed, these examples come from this excellent blog post, so see that post for more details, variations on the attack, and further ways to exploit the vulnerability.
The following example shows a malicious HTTP request that exploits a difference in line-ending handling between a proxy and the destination server to smuggle a request to the /admin endpoint. We can imagine that the proxy is configured to automatically reject requests to /admin normally, and the server assumes that the proxy handles that for us.
In this example the attacker creates a malformed chunk header with a chunk extension by sending 2;\n. The ; ensures that both the proxy and and server treat the header as a chunk extension, but using \n instead of \r\n results in differential parsing:
- The proxy only sees a single request:
- It treats the \n as a "valid" line-ending for the chunk header
- It then treats the xx as the chunk body
- 47 is the next chunk header
- The next 71 bytes (47 is hex, which is 71 in decimal) are treated as the chunk body.
- Finally there's the empty chunk block
- The server sees two requests:
- The server ignores the lone \n, and skips all the way to xx\r\n
- It then treats the 47 as the chunk body
- It sees an ending chunk,0\r\n\r\n and thinks the request is over
- The remaining data is treated as a completely separate request, which contains only an empty chunk in the body.
This is pretty much the simplest example, but you can essentially exploit this difference in all the ways I described previously. Exactly what the implications are for your application are hard to say, but given that all sorts of security bypass, credential stealing, and injection attacks are possible, it's easy to understand why the vulnerability received a CVSS rating of 9.9.
One very interesting thing I found was looking at the security advisories for the same flaw in other HTTP implementations from other languages. In the python aiohttp and ruby puma servers, for example, give the vulnerability only a moderate severity rating in both cases. In netty it's even given a low severity.
As far as I can tell, these servers are essentially vulnerable in the same way as ASP.NET Core is, so it's just an interesting data point, and I think reflects how Microsoft really want to make sure this gets the visibility it deserves and that customers patch their apps!
How was the vulnerability fixed?
As with most fixes for request-smuggling, the solution is to stop being lenient and/or ambiguous about how standalone line-endings are handled in chunk headers.
In ASP.NET Core, the PR that fixes the issue does so by explicitly checking for any line-endings, instead of just looking for \r. If it finds a line ending and it's not strictly \r\n, then Kestrel now throws a KestrelBadHttpRequestException and returns a 400 response.

I'll mention here there is an AppContext switch for opting-in to the dangerous/vulnerable parsing behaviour after you have patched your application, but please don't use it, I can't believe there's really a good (or safe) reason to.😅
The vulnerability has been patched in ASP.NET Core, so what should you do?
What should you do?
Obviously the good news here is that there is a fix for ASP.NET Core. As described in the original issue, the important thing is to update to the latest supported version of ASP.NET Core as soon as possible.
There's no announced evidence of the request smuggling vulnerability being exploited in the wild, but given the vast number of ways that request smuggling could be used, would we even know? 🤔
That means you should update your version of .NET 8, .NET 9, or .NET 10:
| .NET 10 | 10.0.0-rc2 | 10.0.0-rc2 |
| .NET 9 | 9.0.0 - 9.0.9 | 9.0.10 |
| .NET 8 | 8.0.0 - 8.0.20 | 8.0.21 |
If you're using ASP.NET Core 2.3 on .NET Framework, then you'll need to update your version of Microsoft.AspNetCore.Server.Kestrel.Core:
| Microsoft.AspNetCore.Server.Kestrel.Core | 2.0.0-2.3.0 | 2.3.6 |
If you are doing self-contained deployments of your applications, you'll need to update to the patched versions and then redeploy your applications.
And if you're using older versions of .NET Core? Well, then you can't patch… HeroDevs provide additional support for out-of-support versions of .NET (and have confirmed they'll be patching it in .NET 6), but this vulnerability is present in basically all versions of .NET Core as far as I can tell. I've personally tested down to .NET Core 3.0 and I can confirm that the vulnerability is there and there are no patches coming for you. The best thing to do is to update to a supported version of .NET.
⚠️ If you are running ASP.NET Core using <=.NET Core 3.0, .NET Core 3.1, .NET 5, .NET 6 (unless supported by HeroDevs), or .NET 7, then you are vulnerable, and there are no patches. You should update to a supported version of .NET as soon as possible. Ironically, if you're stuck on old .NET Framework Web Forms or MVC applications you are apparently not vulnerable.
It's worth noting that if you are stuck on one of these old framework versions and can't upgrade, then probably the best way to protect yourself is to ensure that you have a proxy in front of your application which is confirmed to not be vulnerable (though obviously you are likely vulnerable to other exploits 😅).
For example, Azure App Services (AAS) confirmed that applications running in AAS are no longer vulnerable, even if you haven't updated, because the proxy that AAS uses (itself a YARP based ASP.NET Core proxy) has been patched. By blocking the requests at the proxy level, ambiguous requests will never make it to your application, so you are protected.
Unfortunately, right now, it's not clear exactly where you stand if you're using a service other than AAS for hosting your applications. Even IIS hasn't been confirmed to be safe or vulnerable at this point, but I did some unofficial testing on my Windows 11 box, and as fat as I can tell, it is vulnerable.
Note that various people in the original issue are attempting to test IIS by using the Content-Length/Transfer-Encoding version of request smuggling, which is not applicable here; we're interested in the chunk-extensions based version.
Another interesting point is that this is vulnerability in HTTP/1.0 and HTTP/1.1 only; it is not a vulnerability in HTTP/2 or HTTP/3. HTTP/2 and HTTP/3 do not support chunked transfer encoding, and instead uses a different, more efficient, binary framing layer for data streaming. So another way to protect those applications which you can't upgrade may be to enforce that client's can only use HTTP/2 or HTTP/3. Be aware that's liable to break a lot of clients that are still using HTTP/1.1 though!
You can configure the HTTP protocols allowed by Kestrel by configuring your Kestrel endpoints. The documentation shows various ways to do this.
How to know if you're affected?
The "simplest" way to know if you're affected is to check the version of .NET you're using to run your applications, using dotnet --info and verify that you're using one of the patched versions. If you are, you're safe. That's the only "supported" way to know that you're safe, and it's the one way I would recommend. As far as I can tell, there isn't currently a generalised tool to point at an application to find out if it's vulnerable, though it would likely be possible to write one.
The folks at HeroDevs re-implemented the functional tests from the original ASP.NET Core fix as a console application compiled against multiple versions of ASP.NET Core. They used this to confirm that unpatched versions of .NET 8-.NET 10 are vulnerable, while patched versions are not. They also used this to verify .NET 6 is vulnerable, and I tweaked it to confirm everything down to at least .NET Core 3.0 is vulnerable.
The test in the repro works by sending a chunked transfer encoding request to ASP.NET Core, with an invalid line ending in a chunk extension header. The vulnerability is identified by ASP.NET Core "hanging", waiting for more data, until it eventually times out. The "fixed" version immediately throws the BadRequest exception included in the fix.
I saw some confusion about this test online; the argument was "if both the fixed and broken versions throw an exception, why does it matter"? However, that's not the point of the test. The fact that Kestrel is paused waiting for more data indicates that a smuggled HTTP request would have been executed. You can see how this can be leveraged to exfiltrate data or attack other users both in the chunk extensions blog or on PortSwigger's site.
I used a similar approach to try to understand whether IIS might be vulnerable by sending the same crafted HTTP request to IIS and seeing if it hung until timing out: it did on my version of IIS (10.0.26100.1882):
So does that definitely mean IIS is vulnerable? No, don't trust me, I'm not a security researcher 😅 But until you hear otherwise, I would play it safe and assume that IIS won't protect you from chunk extension request smuggling attacks. And in general, I would apply the same rules to any other proxies you are relying on in your infrastructure.
And as a final reminder, even though request smuggling is typically described and demonstrated using a proxy in front of your server, just not using a proxy does not mean you're automatically safe. If you're reading, manipulating, or forwarding request streams directly in ASP.NET Core, as opposed to just relying on the built-in model binding, then you might be at risk to request smuggling attacks. It's best to play it safe, patch your apps, and wherever possible leave the complexity of manipulating requests to ASP.NET Core.
In general, I would make sure to subscribe to the ASP.NET Core issue on GitHub, as it's likely that any more announcements around the issue will also be reported there.
Summary
In this post I discuss the recent ASP.NET Core vulnerability: Microsoft Security Advisory CVE-2025-55315: .NET Security Feature Bypass Vulnerability. This advisory warns of a request smuggling vulnerability that affects basically all versions of ASP.NET Core.
I described how request smuggling works in general, using a simple example of request smuggling to show how ambiguity in how HTTP is parsed can lead to HTTP proxies and HTTP servers in handling the same HTTP request in different ways. This can lead to the server seeing two requests where the proxy only sees a single request.
After walking through a request smuggling example, I discussed some of the ways attackers could exploit a request smuggling vulnerability. That includes reflecting malicious data to other users of your app, exfiltrating authentication credentials or other data from client requests, invoking endpoints that shouldn't be publicly accessible, and various other attacks.
Next I walked through the specific request smuggling vulnerability identified in CVE-2025-55315. This uses ambiguities in the parsing of chunk extensions when sending requests that use chunked transfer encoding. Chunk extensions are generally ignored by all servers, but lenient handling can lead to differential handling between proxy and server, providing an avenue for request smuggling.
Finally, I walked through the mitigation steps you should take: patching your applications. I described the information we currently have about vulnerable or patched proxy servers, and how old versions of ASP.NET Core are not going to be getting patches, so will remain vulnerable (shout out again to HeroDevs for supporting .NET 6). If you're running in AAS, then you're ok, but otherwise, you need to check with your proxy provider to establish whether you are vulnerable or not.
Previous Adding metadata to fallback endpoints in ASP.NET Core .png)

