Nov 11, 2026·11 min read·6 visits
An O(N^2) algorithmic complexity flaw in ASP.NET Core's ParsingHelpers.GetHeaderSplit method allows unauthenticated remote attackers to cause a Denial of Service via crafted HTTP headers packed with comma-separated tokens. Fixed in .NET 8.0.34 and .NET 9.0.0-preview.4.24266.19.
CVE-2026-109478 is a network-accessible Denial of Service (DoS) vulnerability in ASP.NET Core caused by inefficient algorithmic complexity (CWE-407) when parsing comma-separated HTTP request headers. Prior to the patch, the internal helper method ParsingHelpers.GetHeaderSplit repeatedly called StringValues.Concat inside a loop iterating over each comma-separated token. Because StringValues allocates a new array of size k + 1 and copies all existing elements upon each concatenation, processing N tokens incurs O(N^2) time and space complexity. An unauthenticated remote attacker can send HTTP requests with headers containing thousands of short, comma-separated values to exhaust CPU cycles and induce severe Garbage Collection (GC) pauses, rendering the web application unresponsive.
ASP.NET Core provides a structured abstraction layer for inspecting and manipulating incoming HTTP request data via the Microsoft.AspNetCore.Http.Abstractions namespace. Within this subsystem, HTTP request headers are exposed through the IHeaderDictionary interface, which offers extension methods to normalize and parse complex header formats. A commonly invoked utility method is HeaderDictionaryExtensions.GetCommaSeparatedValues, which extracts and splits multi-value HTTP headers (such as Accept, Accept-Encoding, Cache-Control, Forwarded, or custom application headers) into individual string tokens.
Under the hood, GetCommaSeparatedValues delegates the tokenization and extraction workload to the internal helper method ParsingHelpers.GetHeaderSplit. Because HTTP request headers represent untrusted network input processed early in the request lifecycle—often within authentication, routing, CORS, or telemetry middleware—the computational efficiency of GetHeaderSplit directly dictates the server's resilience against malformed or adversarial traffic.
CVE-2026-109478 is an Inefficient Algorithmic Complexity vulnerability (CWE-407) residing within ParsingHelpers.GetHeaderSplit. When parsing an HTTP header containing $N$ comma-separated values, the unpatched implementation exhibits quadratic time and memory allocation complexity, scaling at $O(N^2)$.
An unauthenticated, remote attacker can exploit this flaw by transmitting standard HTTP requests containing headers densely populated with single-character, comma-separated tokens. Processing even a single 30 KB header crafted in this manner forces the .NET runtime to execute millions of array copy operations and allocate hundreds of megabytes of transient heap memory, starving worker threads and triggering continuous Garbage Collection (GC) pauses that result in a full application Denial of Service (DoS).
The root cause of CVE-2026-109478 lies in the misuse of the Microsoft.Extensions.Primitives.StringValues data structure as a dynamic accumulator inside an iterative parsing loop. StringValues is an immutable value type (readonly struct) designed to efficiently represent zero, one, or multiple string values without allocating an array when only a single string is present. Internally, it stores a single object? reference that points either to a string instance or a string[] array.
Because StringValues is designed as an immutable wrapper rather than a dynamic collection builder like List<T>, it does not maintain spare capacity or geometric growth buffers. When StringValues.Concat(in StringValues values, string value) is invoked on an instance already holding an array of $k$ elements, the method must allocate a brand-new string[k + 1] array on the managed heap, copy all $k$ object references from the source array to the destination array via Array.Copy, and write the new string reference at index $k$.
In the unpatched version of ParsingHelpers.GetHeaderSplit, the method iterates over every token yielded by HeaderSegmentCollection and appends each valid token to a local StringValues variable using StringValues.Concat:
As the loop progresses from $k = 1$ to $N$ tokens, the cumulative number of array reference copy operations follows an arithmetic progression:
$$\text{Total Reference Copies} = \sum_{k=1}^{N} (k - 1) = \frac{N(N - 1)}{2} = O(N^2)$$
Similarly, the total volume of intermediate array allocations on the managed heap grows quadratically. On a 64-bit runtime where each object reference occupies 8 bytes (plus a 24-byte array object header), parsing $N$ tokens allocates $N - 1$ distinct arrays of linearly increasing size. For $N = 15,000$ tokens—which easily fits inside Kestrel's default 32 KB maximum header size (a,a,a,...)—a single call to GetHeaderSplit performs approximately $1.125 \times 10^8$ reference copies and allocates roughly $900\text{ MB}$ of ephemeral heap memory.
> [!WARNING] > Arrays exceeding 85,000 bytes in size (which occurs once $k \ge 10,622$ on 64-bit platforms) are allocated directly on the .NET Large Object Heap (LOH). Consequently, tokens $10,622$ through $15,000$ trigger over 4,300 separate LOH allocations per request. LOH allocations are only reclaimed during full Generation 2 Garbage Collections, which require expensive stop-the-world thread suspensions.
Microsoft addressed CVE-2026-109478 in commits effb34d85fdc5137ee4bd87d52560433c3df02e7 (main branch) and 2523887d4cca7013afa2737e20cb7f6ba1eed8ff (.NET 8.0 release branch). The patch modifies three files: ParsingHelpers.cs, HeaderDictionaryExtensions.cs, and introduces the internal ValueListBuilder<T> structure in ValueListBuilder.cs.
Prior to the patch, ParsingHelpers.GetHeaderSplit accumulated parsed segments into a StringValues struct, and HeaderDictionaryExtensions.GetCommaSeparatedValues subsequently converted that struct into a string[]:
// File: src/Http/Http.Abstractions/src/Extensions/HeaderDictionaryExtensions.cs
public static string[] GetCommaSeparatedValues(this IHeaderDictionary headers, string key)
{
// Redundant .ToArray() call after O(N^2) StringValues accumulation
return ParsingHelpers.GetHeaderSplit(headers, key).ToArray();
}
// File: src/Http/Http.Abstractions/src/Internal/ParsingHelpers.cs
public static StringValues GetHeaderSplit(IHeaderDictionary headers, string key)
{
var values = GetHeaderUnmodified(headers, key);
StringValues result = default;
foreach (var segment in new HeaderSegmentCollection(values))
{
if (segment.Data.HasValue)
{
var value = DeQuote(segment.Data.Value);
if (!string.IsNullOrEmpty(value))
{
// FLAW: Allocates a new array of size (k + 1) and copies all k elements on every iteration
result = StringValues.Concat(in result, value);
}
}
}
return result;
}The fix replaces StringValues accumulation with ValueListBuilder<string>, a stack-allocated ref struct backed by an inline 4-element buffer and ArrayPool<string>.Shared for larger payloads:
// File: src/Http/Http.Abstractions/src/Extensions/HeaderDictionaryExtensions.cs
public static string[] GetCommaSeparatedValues(this IHeaderDictionary headers, string key)
{
// GetHeaderSplit now directly returns the finalized string[]
return ParsingHelpers.GetHeaderSplit(headers, key);
}
// File: src/Http/Http.Abstractions/src/Internal/ParsingHelpers.cs
public static string[] GetHeaderSplit(IHeaderDictionary headers, string key)
{
var values = GetHeaderUnmodified(headers, key);
// Initialize ValueListBuilder with a 4-element inline Span to avoid heap allocation for common small headers
ValueListBuilder<string> strings = new([null!, null!, null!, null!]);
foreach (var segment in new HeaderSegmentCollection(values))
{
if (segment.Data.HasValue)
{
var value = DeQuote(segment.Data.Value);
if (!string.IsNullOrEmpty(value))
{
// Amortized O(1) insertion via geometric capacity doubling
strings.Add(value);
}
}
}
// Allocate exact-sized destination array once in O(N) time
string[] result = strings.AsSpan().ToArray();
// Return any rented intermediate buffer back to ArrayPool<string>.Shared
strings.Dispose();
return result;
}ValueListBuilder<T>The newly added ValueListBuilder<T> guarantees $O(1)$ amortized insertion and zero LOH churn by renting buffers from ArrayPool<T>.Shared and doubling capacity whenever the underlying span is exhausted:
// File: src/Shared/ValueStringBuilder/ValueListBuilder.cs
private void Grow(int additionalCapacityRequired = 1)
{
const int ArrayMaxLength = 0x7FFFFFC7;
// Geometric growth: double the current capacity (minimum 4)
int nextCapacity = Math.Max(_span.Length != 0 ? _span.Length * 2 : 4, _span.Length + additionalCapacityRequired);
if ((uint)nextCapacity > ArrayMaxLength)
{
nextCapacity = Math.Max(Math.Max(_span.Length + 1, ArrayMaxLength), _span.Length);
}
// Rent from shared pool instead of allocating new arrays on the GC heap
T[] array = ArrayPool<T>.Shared.Rent(nextCapacity);
_span.CopyTo(array);
T[]? toReturn = _arrayFromPool;
_span = _arrayFromPool = array;
if (toReturn != null)
{
// Return the previous buffer immediately to the pool
ArrayPool<T>.Shared.Return(toReturn);
}
}The remediation is complete and structurally sound for this code path. By changing the return signature of the internal ParsingHelpers.GetHeaderSplit method from StringValues to string[] and using geometric capacity doubling with pooled arrays, time complexity is strictly bounded to $O(N)$ and unpooled heap allocations are reduced from $N$ arrays to exactly 1 final string[] array. Furthermore, changing GetHeaderSplit to return string[] eliminated a redundant .ToArray() conversion previously present in HeaderDictionaryExtensions.GetCommaSeparatedValues. No exception paths exist inside the foreach loop (DeQuote operates on StringSegment slices without throwing), ensuring strings.Dispose() is reliably reached during standard execution.
Exploiting CVE-2026-109478 requires no authentication, special privileges, or complex state synchronization. The sole prerequisite is that the target ASP.NET Core application—or any middleware, framework component, or third-party library in its request pipeline—invokes IHeaderDictionary.GetCommaSeparatedValues on an attacker-controllable HTTP header.
Within the ASP.NET Core ecosystem and common web application architectures, GetCommaSeparatedValues is frequently called on standard HTTP headers during request pre-processing:
Accept, Accept-Encoding, or Accept-Language headers.X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, or Forwarded headers.Access-Control-Request-Headers, Origin, Vary, or Cache-Control directives.To maximize the ratio of token count ($N$) to byte length ($L$), an attacker alternates single-byte characters with comma delimiters (a,a,a,...). Under Kestrel's default configuration, the maximum permitted total size for all request headers (MaxRequestHeadersTotalSize) and for any single header field (MaxRequestHeaderFieldSize) is 32,768 bytes (32 KB).
GET / HTTP/1.1
Host: vulnerable-target.example.com
Accept-Encoding: a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,... [15,000 tokens (~30 KB)]
Connection: keep-alive
Server-Side Execution: When the request reaches the middleware or controller calling request.Headers.GetCommaSeparatedValues("Accept-Encoding"), ParsingHelpers.GetHeaderSplit iterates 15,000 times:
Resource Exhaustion Amplification: By pipelining or multiplexing these requests over persistent HTTP/1.1 (keep-alive), HTTP/2, or HTTP/3 connections, a single client with minimal outbound bandwidth (e.g., 10–20 requests per second, requiring less than 5 Mbps) forces the server to allocate over $9\text{ GB/sec}$ on the managed heap, half of which lands directly on the LOH. The .NET runtime immediately enters continuous blocking Generation 2 GC cycles, freezing all Kestrel worker threads and dropping legitimate client connections.
CVE-2026-109478 is rated High severity with a CVSS v3.1 base score of 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
Beyond immediate request timeouts, the severe LOH allocation pressure introduced by this vulnerability can cause cascading failures in containerized and cloud-native environments:
dotnet host process (SIGKILL / exit code 137).ThreadPool cannot schedule asynchronous I/O completions, health check responses, or database keep-alives, causing upstream load balancers (such as Kubernetes liveness probes or AWS ALB health checks) to mark instances as unhealthy and cycle them repeatedly.The primary and definitive remediation for CVE-2026-109478 is to upgrade the .NET Runtime, ASP.NET Core Shared Framework, and associated development tooling to the patched versions released by Microsoft.
| Product / Component | Affected Version Range | Patched Version |
|---|---|---|
| Microsoft .NET 8.0 (Linux, macOS, Windows) | >= 8.0.0, < 8.0.34 | 8.0.34 |
| Microsoft .NET 9.0 (Linux, macOS, Windows) | >= 9.0.0, < 9.0.0-preview.4.24266.19 | 9.0.0-preview.4.24266.19 |
| Visual Studio 2022 (Windows) | >= 17.14.0, < 17.14.45 | 17.14.45 |
| Visual Studio 2026 v18.11 (Windows) | >= 18.11.0, < 18.11.4 | 18.11.4 |
| Visual Studio 2026 v18.12 (Windows) | == 18.12.0 | 18.12.1+ |
> [!NOTE]
> For self-contained ASP.NET Core deployments (where the .NET runtime and Microsoft.AspNetCore.Http.Abstractions.dll are bundled directly with the application executable), updating the host server's shared runtime is insufficient. You must rebuild and redeploy the application using the updated .NET SDK.
If immediate patching is not feasible, security engineers can substantially reduce the quadratic amplification factor by constraining header lengths and filtering excessive comma counts at the network edge or within Kestrel:
MaxRequestHeaderFieldSize and MaxRequestHeadersTotalSize from the default 32 KB down to the minimum threshold required by your application (e.g., 4 KB or 8 KB). Because the algorithmic complexity is $O(N^2)$, reducing maximum header length by a factor of $8\times$ reduces worst-case CPU and memory work by a factor of $64\times$:builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxRequestHeaderFieldSize = 4096; // 4 KB max per single header
serverOptions.Limits.MaxRequestHeadersTotalSize = 8192; // 8 KB max across all headers
});CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
.NET 8.0 Microsoft | >= 8.0.0, < 8.0.34 | 8.0.34 |
.NET 9.0 Microsoft | >= 9.0.0, < 9.0.0-preview.4.24266.19 | 9.0.0-preview.4.24266.19 |
Visual Studio 2022 Microsoft | >= 17.14.0, < 17.14.45 | 17.14.45 |
Visual Studio 2026 Microsoft | >= 18.11.0, < 18.11.4 | 18.11.4 |
Visual Studio 2026 Microsoft | == 18.12.0 | 18.12.1 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-109478 |
| Weakness Class | CWE-407 (Inefficient Algorithmic Complexity) |
| Affected Component | Microsoft.AspNetCore.Http.Abstractions (ParsingHelpers.GetHeaderSplit) |
| Attack Vector | Network (Unauthenticated HTTP Request Headers) |
| CVSS v3.1 Score | 7.5 HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| Primary Impact | Application Denial of Service (CPU & GC Exhaustion) |
| Exploit Status | Unproven (No Public PoC / Not in CISA KEV) |
| Patched In | .NET 8.0.34 / .NET 9.0.0-preview.4.24266.19 |
The product has an algorithm whose execution time or memory consumption can be made to grow at a rate that is disproportionate to the size of the input, leading to excessive consumption of CPU resources or memory.
CVE-2026-114150 is a network-accessible information disclosure vulnerability affecting Microsoft ASP.NET Core in .NET 11.0 preview releases and Microsoft Visual Studio 2026 prior to version 18.12.0. The flaw stems from the use of uninitialized resources (CWE-908) within response buffering components, specifically ResponseBufferingStream used by middleware such as HTTP Logging and Output Caching. Because rented memory segments from ArrayPool<byte> or MemoryPool<byte> are not cleared upon return or strictly bounded to the exact written byte count during subsequent reads, residual response payloads from earlier HTTP requests remain in the buffer tail. Downstream consumers or log sinks reading these buffers expose confidential cross-session data, including authentication tokens and personally identifiable information.
An incorrect authorization vulnerability (CWE-863) exists in the ASP.NET Core SignalR HTTP Connection Dispatcher (`Microsoft.AspNetCore.Http.Connections`). The connection negotiation process failed to bind the authenticated caller identity to the generated connection state, and subsequent transport or message dispatch handlers failed to verify caller consistency. Consequently, an authenticated low-privilege attacker who obtains a valid connection identifier or token can inject messages into or hijack another user's SignalR session, achieving an elevation of privilege across security boundaries.
CVE-2026-114147 is a high-severity Denial of Service (DoS) vulnerability affecting ASP.NET Core and specifically the Microsoft.AspNetCore.SignalR.StackExchangeRedis scale-out provider across .NET 8.0, 9.0, 10.0, and 11.0 preview releases. The vulnerability originates from a string comparison mismatch between internal per-connection group collections and the centralized Redis channel subscription registry, compounded by a race condition during connection teardown. Remote unauthenticated attackers can exploit this flaw to leave orphaned connection contexts and active Redis pub/sub listeners pinned in memory, leading to unbounded memory exhaustion and application process termination.
CVE-2026-114156 is an improper resolution of path equivalence vulnerability (CWE-41) in Microsoft ASP.NET Core affecting .NET 11.0 preview releases and Microsoft Visual Studio 2026. The vulnerability stems from a discrepancy between how path prefix validation helpers (such as PathString.StartsWithSegments) and downstream routing mechanisms handle alternate path separators and percent-encoded characters. An unauthenticated remote attacker can exploit this mismatch over the network to bypass path-based authorization middleware and access restricted endpoints.
CVE-2026-114157 is a high-severity local privilege escalation and security feature bypass vulnerability affecting ASP.NET Core on Microsoft Windows. The flaw resides in the Kestrel web server's Named Pipes transport layer (Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes) and stems from a race condition (CWE-362) during broken pipe error recovery. When an active connection throws an IOException during handshake acceptance, the connection listener previously disposed of the broken NamedPipeServerStream instance before acquiring a replacement stream from its internal pool. This sequence created a momentary window where zero server instances of the named pipe existed in the Windows Named Pipe File System (NPFS). A local, low-privileged attacker can exploit this timing window to create a named pipe with the identical name and a permissive Discretionary Access Control List (DACL), hijacking subsequent inter-process communications and escalating privileges across security boundaries.
CVE-2026-124194 is a forced authentication and spoofing vulnerability in Microsoft Office Outlook caused by a missing user interface warning before dereferencing remote network paths (CWE-356). By embedding an attacker-controlled Universal Naming Convention (UNC) path within MAPI message properties such as PidLidReminderFileParameter, an unauthenticated remote attacker can force a victim's system to initiate an outbound SMB or WebDAV connection upon opening or previewing a crafted message. This automatic authentication handshake leaks the user's NetNTLMv2 hash, enabling offline password cracking or real-time NTLM relay attacks. The vulnerability was exploited in the wild as a zero-day prior to its remediation in Microsoft's November 2026 Patch Tuesday updates and is listed in the CISA Known Exploited Vulnerabilities (KEV) catalog.