Nov 11, 2026·11 min read·3 visits
CVE-2026-114146 is a critical (CVSS 9.0) elevation of privilege vulnerability in ASP.NET Core (`Microsoft.AspNetCore.Http.Connections`). Because `HttpConnectionDispatcher` did not bind or verify the `ClaimsPrincipal` across the SignalR negotiation, transport initiation, and message transmission phases, an authenticated attacker possessing a victim's connection token can hijack their connection or inject arbitrary hub invocations under the victim's identity.
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.
ASP.NET Core includes Microsoft.AspNetCore.Http.Connections, the foundational library that manages persistent, real-time bidirectional communication for SignalR over HTTP-based transports. This subsystem exposes endpoints that handle connection negotiation, transport handshakes (WebSockets, Server-Sent Events, and Long Polling), and asynchronous message dispatching between connected clients and server-side SignalR Hubs. Because SignalR Hubs frequently expose privileged application logic and rely on connection-level authentication state, the integrity of the underlying HttpConnectionContext is critical to maintaining session isolation.
CVE-2026-114146 is an Incorrect Authorization vulnerability (CWE-863) located within the Microsoft.AspNetCore.Http.Connections.Internal namespace, specifically inside HttpConnectionDispatcher and HttpConnectionContext. During the multi-step lifecycle of a SignalR connection, the server allocates a state object upon receiving an initial negotiation request and assigns a unique connectionId and connectionToken. Prior to the security update, the dispatcher did not bind the authenticated user identity (HttpContext.User) from the negotiation request to the newly created connection context, nor did it verify that subsequent HTTP requests referencing that connection originated from the same principal.
Because the connection state remained decoupled from the negotiating caller's identity, an authenticated attacker who acquires a victim's connection identifier can interact with the connection endpoint directly. The attacker can bind their own transport channel to the victim's negotiated session or inject message payloads via the /send or /delete endpoints. When the application processes these injected requests, the SignalR hub executes the operations under the security context associated with the hijacked connection, resulting in a cross-session elevation of privilege.
To establish a SignalR connection, a client executes a multi-phase protocol managed by HttpConnectionDispatcher. First, the client sends an HTTP POST request to the /negotiate endpoint. The dispatcher invokes ProcessNegotiate, which calls HttpConnectionManager.CreateConnection to instantiate an HttpConnectionContext and returns the generated connectionId and connectionToken in the JSON response. Second, the client initiates the active transport (such as an HTTP GET request for Server-Sent Events or WebSockets, or repeated requests for Long Polling) by supplying the token in the query string. Third, when using HTTP-based transports like Long Polling or Server-Sent Events, client-to-server messages are transmitted via separate HTTP POST requests to the /send endpoint, while session termination uses HTTP DELETE requests.
The root cause of CVE-2026-114146 is the absence of identity binding during ProcessNegotiate and the lack of principal verification across subsequent state transitions in HttpConnectionDispatcher. When ProcessNegotiate created the connection via CreateConnection(options), the method signature and implementation ignored context.User. The resulting HttpConnectionContext existed in an unbound state until the first transport request called EnsureConnectionStateAsync, which blindly assigned connection.User = context.User without verifying whether that caller matched the user who negotiated the session.
Furthermore, even after connection.User was populated during transport initialization, subsequent out-of-band HTTP requests—specifically ProcessSend (handling POST payloads), ProcessDeleteAsync (handling DELETE termination requests), and subsequent Long Polling GET requests passing through EnsureConnectionStateAsync—did not compare the incoming request's HttpContext.User against the existing connection.User.In EnsureConnectionStateAsync, every new Long Polling request overwrote connection.User with the incoming context.User, whereas ProcessSend left connection.User untouched while processing the attacker's payload.
This design flaw breaks the state invariant between HTTP request authentication and persistent SignalR connection identity. In Hub method invocations where authorization logic or business operations inspect HubCallerContext.User, an attacker sending requests to /send executes commands under the victim's ClaimsPrincipal. Conversely, if an attacker intercepts a connection token immediately after /negotiate or between Long Polling cycles, the attacker can overwrite the principal or hijack the bidirectional stream.
The remediation, implemented across commits 514c86030195fd6b649bb26c48975a043d96b791, db8579880b30153dbd601fa079f49e7e3ce42e08, and e1ae50f427a49c76528376a5b3af8216bfda55bc, introduces a deterministic identity snapshot mechanism via the new UserIdentitySnapshot class and enforces verification across all dispatcher entry points.
In the vulnerable implementation of HttpConnectionDispatcher.cs, ProcessNegotiate allocated the connection without passing the caller's principal, and EnsureConnectionStateAsync unconditionally assigned connection.User:
// Vulnerable code in HttpConnectionDispatcher.cs
private async Task ProcessNegotiate(HttpContext context, HttpConnectionDispatcherOptions options)
{
context.Response.ContentType = "application/json";
// Establish the connection without recording context.User
var connection = CreateConnection(options);
await WriteNegotiatePayloadAsync(context.Response.BodyWriter, connection.ConnectionId, context, options, connection.ConnectionToken);
}
private async Task<bool> EnsureConnectionStateAsync(HttpConnectionContext connection, HttpContext context, HttpTransportType transportType, HttpConnectionDispatcherOptions options)
{
// ... transport and state validation omitted ...
// Overwrites or sets the user without checking against prior state
connection.User = context.User;
return true;
}The patch modifies ProcessNegotiate to capture the negotiating ClaimsPrincipal and pass it to CreateConnection. It also injects a guard method, RejectIfConnectionUserChangedAsync, into ProcessSend, ProcessDeleteAsync, and EnsureConnectionStateAsync:
// Patched code in HttpConnectionDispatcher.cs
private async Task ProcessNegotiate(HttpContext context, HttpConnectionDispatcherOptions options)
{
context.Response.ContentType = "application/json";
// Bind the negotiating user's identity snapshot to the connection
var connection = CreateConnection(options, context.User);
await WriteNegotiatePayloadAsync(context.Response.BodyWriter, connection.ConnectionId, context, options, connection.ConnectionToken);
}
private async Task ProcessSend(HttpContext context)
{
// ... connection lookup omitted ...
if (await RejectIfConnectionUserChangedAsync(connection, context))
{
return;
}
// ... process payload ...
}
private async Task<bool> EnsureConnectionStateAsync(HttpConnectionContext connection, HttpContext context, HttpTransportType transportType, HttpConnectionDispatcherOptions options)
{
// ... state validation omitted ...
if (await RejectIfConnectionUserChangedAsync(connection, context))
{
return false;
}
// Seal the user principal and release the temporary negotiation snapshot
connection.SetUserAndReleaseNegotiatedUser(context.User);
return true;
}
private async Task<bool> RejectIfConnectionUserChangedAsync(HttpConnectionContext connection, HttpContext context)
{
if (connection.IsUserAssociatedWithConnection(context.User))
{
return false;
}
Log.UserNameChanged(
_logger,
UserIdentitySnapshot.GetUserIdentityValue(connection.User),
UserIdentitySnapshot.GetUserIdentityValue(context.User));
context.Response.ContentType = "text/plain";
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("The user associated with this connection changed.");
return true;
}To compare identities reliably across distinct HTTP requests (where object reference equality on ClaimsPrincipal fails because each request constructs a new principal instance), the patch introduces UserIdentitySnapshot. Rather than retaining the entire ClaimsPrincipal from the /negotiate request in memory, UserIdentitySnapshot.Capture extracts a minimal immutable representation:
// Key logic in UserIdentitySnapshot.cs
internal sealed class UserIdentitySnapshot
{
private static readonly string[] _preferredClaimTypes =
new[] { "sub", ClaimTypes.NameIdentifier, ClaimTypes.Upn };
public static UserIdentitySnapshot Capture(ClaimsPrincipal? user)
{
if (!IsAuthenticated(user))
{
return Anonymous;
}
// 1. Check preferred unique identifiers in priority order: sub, NameIdentifier, Upn
foreach (var preferredType in _preferredClaimTypes)
{
var claim = FindFirstClaim(user!, preferredType);
if (claim is not null)
{
return new UserIdentitySnapshot(SnapshotKind.PreferredClaim,
new ClaimSnapshot(claim.Type, claim.Value, claim.Issuer), claims: null, name: null);
}
}
// 2. Fallback: Capture and sort all claims by (Type, Value, Issuer)
var claims = CaptureClaims(user!);
if (claims.Length > 0)
{
return new UserIdentitySnapshot(SnapshotKind.Claims, preferredClaim: default, claims, name: null);
}
// 3. Fallback: Use Identity.Name if no claims exist
// ...
}
}The patch addresses the vulnerability across all request entry points in HttpConnectionDispatcher (ProcessNegotiate, EnsureConnectionStateAsync, ProcessSend, and ProcessDeleteAsync). By comparing claim type, value, and issuer (via StringComparison.Ordinal), it prevents cross-issuer token substitution attacks. Furthermore, transitions between anonymous (IsAuthenticated == false) and authenticated states in either direction are explicitly rejected.
One edge case involves custom authentication handlers that construct an authenticated IIdentity where IsAuthenticated is true, but populate zero claims and leave Identity.Name null or empty (SnapshotKind.AuthenticatedWithoutClaimsOrName). In UserIdentitySnapshot.Matches, two distinct principals falling into this specific state will match each other because neither possesses distinguishing attributes. However, standard ASP.NET Core authentication schemes (JWT Bearer, Cookies, OpenID Connect, Windows Authentication) always populate NameIdentifier, sub, Upn, or Name, making the fix robust for standard deployments.
Exploiting CVE-2026-114146 requires the target application to expose an authenticated SignalR endpoint and requires the attacker to obtain a valid connectionToken (or connectionId in versions/configurations where they are equivalent) belonging to another session. Because PR:L (Privileges Required: Low) and UI:R (User Interaction: Required) apply, the attacker typically authenticates to the application using a low-privilege account and acquires the victim's connection token through shared network observation, browser history/referer leakage, diagnostic logs, or proxy access logs (since SignalR passes the token via the ?id= URL query parameter).
/send (Long Polling / Server-Sent Events)When a victim establishes a SignalR connection using Server-Sent Events or Long Polling, connection.User on the server is populated with the victim's ClaimsPrincipal. While this session is active, the attacker sends an HTTP POST request to the SignalR /send endpoint using the victim's id query parameter, authenticated with the attacker's own session cookie or bearer token:
POST /chatHub/send?id=VictimConnectionToken HTTP/1.1
Host: vulnerable-target.example.com
Authorization: Bearer <Attacker_Low_Priv_JWT>
Content-Type: text/plain;charset=UTF-8
Content-Length: 56
{"type":1,"target":"ExecuteAdminAction","arguments":["pwn"]}In vulnerable versions, ProcessSend looks up the connection by id, skips identity verification, and writes the Hub invocation payload directly into the connection's application pipe. The SignalR HubDispatcher reads the message from the pipe and executes ExecuteAdminAction using connection.User (which remains set to the victim's principal), bypassing hub-level authorization checks.
If an attacker intercepts or predicts the connectionToken returned by a victim's POST /negotiate request before the victim completes the transport handshake (GET /chatHub?id=...), or if a victim shares a pre-negotiated link, the attacker can issue the transport GET request first. Conversely, if the attacker negotiates a connection under their own account and tricks a privileged victim into connecting to that pre-negotiated URL, EnsureConnectionStateAsync overwrites connection.User with the victim's ClaimsPrincipal, allowing the attacker who knows the connectionId to interact with the session.
CVE-2026-114146 carries a CVSS v3.1 base score of 9.0 (Critical) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H. The Scope metric is marked as Changed (S:C) because the vulnerability resides in the underlying ASP.NET Core HTTP connection dispatcher, while the impact crosses security boundaries into application-layer SignalR hubs and individual user security contexts.
/send, an attacker can execute arbitrary hub methods under the victim's identity (HubCallerContext.User). If the victim holds administrative privileges within the application, the attacker achieves full application-level privilege escalation.DELETE request to the SignalR endpoint (ProcessDeleteAsync) or send malformed protocol frames to forcibly terminate the victim's active real-time connection.At the time of publication, CVE-2026-114146 is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and its Exploit Prediction Scoring System (EPSS) score shows no active automated exploitation telemetry. However, the commit history in the dotnet/aspnetcore repository contains comprehensive unit tests (HttpConnectionDispatcherTests.ConnectionBinding.cs) that clearly demonstrate the exact request sequences required to trigger the vulnerability.
To remediate CVE-2026-114146, organizations must update their .NET runtimes, SDKs, NuGet packages, and development environments to the patched servicing releases issued by Microsoft.
11.0.0-preview.1 to 11.0.0 or later.> [!NOTE]
> Self-contained .NET deployments bundle the vulnerable Microsoft.AspNetCore.Http.Connections.dll directly within the application artifact. These applications must be rebuilt and redeployed using the patched .NET SDK, whereas framework-dependent deployments can be remediated by updating the host server's shared ASP.NET Core runtime.
If immediate patching is not feasible, apply the following compensating controls to reduce the likelihood of exploitation:
HttpConnectionDispatcherOptions.Transports on the server to allow only HttpTransportType.WebSockets. While this does not eliminate the pre-transport race window between /negotiate and the WebSocket upgrade request, it disables the /send endpoint and recurring Long Polling HTTP GET requests, eliminating post-connection message injection.SkipNegotiation for WebSockets: Where supported by the client architecture, configure clients to connect via WebSockets with SkipNegotiation = true. This bypasses the /negotiate HTTP round-trip entirely and establishes the connection in a single atomic WebSocket handshake.connectionToken values are passed in the ?id= query parameter, ensure that reverse proxies, Web Application Firewalls (WAFs), and application telemetry pipelines scrub query strings from shared logs to prevent low-privilege insiders from harvesting active tokens.CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/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.23 | 9.0.23 |
.NET 10.0 Microsoft | >= 10.0.0, < 10.0.15 | 10.0.15 |
.NET 11.0 Microsoft | >= 11.0.0-preview.1, < 11.0.0 | 11.0.0 |
Microsoft.AspNetCore.Http.Connections Microsoft | >= 1.2.0, < 1.2.14 | 1.2.14 |
Microsoft Visual Studio 2022 Microsoft | >= 17.14.0, < 17.14.45 | 17.14.45 |
Microsoft Visual Studio 2026 Microsoft | >= 18.11.0, < 18.11.4 | 18.11.4 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | CWE-863: Incorrect Authorization |
| Affected Component | Microsoft.AspNetCore.Http.Connections (HttpConnectionDispatcher) |
| Attack Vector | Network (Authenticated SignalR HTTP Endpoints) |
| CVSS v3.1 Score | 9.0 Critical (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H) |
| Security Impact | Elevation of Privilege / Cross-User SignalR Session Hijacking |
| Exploit Status | Proof-of-Concept in Public Test Suite (No Weaponized Exploits) |
| CISA KEV Status | Not Listed |
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
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.
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.
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.