Security
Nov 13, 2026

CVE-2026-114146: ASP.NET Core SignalR Elevation of Privilege

CVE-2026-114146 lets an attacker run ASP.NET Core SignalR Hub methods as a privileged victim. See affected .NET versions, fixes and options for .NET 6.

Give me the TL;DR
CVE-2026-114146: ASP.NET Core SignalR Elevation of Privilege

CVE-2026-114146 is a Critical ASP.NET Core SignalR elevation of privilege vulnerability (CWE-863: Incorrect Authorization) disclosed by Microsoft on November 10, 2026 and scored 9.0 (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H). SignalR attached each connection to whichever user made its first transport request, not the user who negotiated it, so an authenticated attacker who gets a privileged user to follow a crafted link can inject Hub invocations that run as that user. Affected: .NET 8.0.0 to 8.0.33, 9.0.0 to 9.0.22, 10.0.0 to 10.0.14, 11.0 previews through rc.3, and Microsoft.AspNetCore.Http.Connections 1.2.0 to 1.2.13 (ASP.NET Core 2.3). Fixed in 8.0.34, 9.0.23, 10.0.15, 11.0.0, and 1.2.14.

It is the only CVSS Critical among the 14 ASP.NET Core CVEs Microsoft published that day, and it reaches every app that hosts a SignalR hub: chat, live dashboards, trading and operations screens, multiplayer lobbies, and Blazor Server apps, which run on a hub of their own. The timing makes it worse. .NET 8 and .NET 9 reached end of support on the same November 10, so 8.0.34 and 9.0.23 are the last OSS releases either line will get. .NET 6 and .NET 7 carry the same flaw and have no OSS fix at all. NES (Never-Ending Support) for .NET 6.0.49 resolves it.

Affected and unsupported? See NES for .NET.

What is CVE-2026-114146 in ASP.NET Core SignalR?

A SignalR connection normally starts with two steps. The client POSTs to the hub's /negotiate endpoint, which creates a connection on the server and returns a connection token. The client then opens a transport (WebSockets, Server-Sent Events, or long polling) that presents that token in the id query parameter. On Server-Sent Events and long polling, messages from client to server travel as separate HTTP POSTs to the hub URL, each carrying the same token.

Microsoft's advisory, GHSA-67wp-ghc2-fw2g, describes the flaw this way: "SignalR connections are not consistently bound to the identity negotiated at connection establishment, allowing injected messages to execute Hub invocations as the victim."

In affected versions, the connection layer (HttpConnectionDispatcher in Microsoft.AspNetCore.Http.Connections) recorded nothing about who called /negotiate. The connection's User came from whoever made the first transport request, and later send (POST) and close (DELETE) requests were accepted from anyone holding the token. A Hub reads Context.User from that connection, so every [Authorize] attribute and policy check on a Hub method evaluates whoever the connection says it belongs to. The authorization checks exist and run. They just run against the wrong principal, which is CWE-863.

Microsoft's Security Update Guide entry sums up the attack: "An authenticated attacker could convince a privileged user to follow a specially crafted link to an affected application. Successful exploitation could allow the attacker to perform actions with that user's application privileges."

CVE-2026-114146 severity and exploit conditions

Microsoft is the CNA (CVE Numbering Authority) and, so far, the only scorer. NVD lists the CVE as awaiting analysis with no score of its own. Recomputing Microsoft's vector gives a base score of 9.0 and a temporal score of 7.8 (E:U/RL:O/RC:C: no known exploit code, official fix available, confirmed by the vendor).

The preconditions, from Microsoft's description and the patch:

  • The app hosts a SignalR hub on an affected runtime or package version.
  • The attacker has a valid low-privilege account. Self-service registration is enough if the hub accepts any signed-in user.
  • A more privileged user follows the attacker's link while signed in.
  • The victim's browser sends credentials on its own. Our read of the patch is that the link path depends on ambient credentials, meaning cookie authentication or Windows authentication. A browser navigation to a hub URL is handled as a long polling request, so the path also depends on long polling being enabled, which it is by default (HttpTransports.All). Hubs that accept only bearer tokens supplied by client code are harder to reach this way, but should still be patched.

Why CVE-2026-114146 shows up as Critical, Important, and High

Depending on which tool you read, this CVE carries three different labels:

  • Critical: the CVSS 3.1 base score of 9.0 in Microsoft's CVE record and the GitHub advisory text.
  • Important: Microsoft's own severity rating in the Security Update Guide, which is what most Patch Tuesday dashboards display.
  • High: the reviewed severity in the GitHub Advisory Database, which feeds Dependabot and tools that read GitHub or OSV data.

These come from different rating systems. The base score is 9.0 everywhere it appears. If a SignalR hub in your estate accepts cookie or Windows authentication and exposes privileged methods, triage it as Critical regardless of what your scanner's label says.

Is CVE-2026-114146 being exploited?

Not as of disclosure. Microsoft marks it as not publicly disclosed before the fix, not exploited, and "Exploitation Less Likely." The vector carries E:U (no known exploit code), the CVE is not in the CISA Known Exploited Vulnerabilities (KEV) catalog, and CISA's SSVC enrichment records no exploitation and rates it not automatable, with total technical impact. We have not found a public proof of concept.

Two details argue against waiting. Microsoft's Security Update Guide credits 17 researchers for this one CVE, and a bug that many people reported is a bug that is not hard to find. And the fix commits are public in dotnet/aspnetcore, so anyone can compare the 8.0.33 and 8.0.34 sources and see exactly which checks were missing.

Root cause: the first SignalR transport request decided who owned the connection

In .NET 8.0.33, HttpConnectionDispatcher.EnsureConnectionStateAsync runs on every transport request: the WebSocket upgrade, the Server-Sent Events stream, and each long poll. Near the end, it compares the ClaimTypes.NameIdentifier claim of the connection's current user with the incoming request's user. If they differ, it logs a UserNameChanged warning ("The name of the user changed from ... to ...") and keeps going. The next line is connection.User = connection.HttpContext?.User;, so the connection takes on the new identity. And since nothing was recorded at negotiate time, the connection's user is empty until the first transport request arrives, which means that first request is never compared to anything.

ProcessSend, which handles the POSTs that carry client-to-server messages on Server-Sent Events and long polling, checks that the connection exists and is not a WebSocket connection (WebSocket connections reject POSTs with a 405). It never looks at the caller's identity. ProcessDeleteAsync is the same.

.NET 6.0.36 and .NET 7.0.20, the final OSS releases of those lines, do not even log the warning. They assign connection.User from the transport request with no comparison at all. The ASP.NET Core 2.1, .NET Core 3.1, and .NET 5 sources follow the same pattern, and Microsoft's fix for the ASP.NET Core 2.3 package confirms the flaw predates .NET 6.

Microsoft's fix, merged as "Bind SignalR connections to negotiated identity" (for example, commit db85798), changes three things:

  1. ProcessNegotiate passes context.User into CreateConnection, and HttpConnectionContext stores a UserIdentitySnapshot of the negotiating user.
  2. A new RejectIfConnectionUserChangedAsync check runs on send, delete, and transport requests. When the caller does not match the negotiated identity, the request gets HTTP 403 with the body "The user associated with this connection changed." and the dispatcher logs UserNameChanged.
  3. The first transport request that passes the check binds the connection to its user through SetUserAndReleaseNegotiatedUser.

The snapshot compares the first non-empty sub, ClaimTypes.NameIdentifier, or ClaimTypes.Upn claim, including that claim's issuer. If none of those exist, it compares the full sorted set of authenticated claims, then falls back to the identity name. An anonymous negotiate only matches an anonymous transport request.

How the CVE-2026-114146 attack chain fits together

Putting Microsoft's FAQ, the advisory summary, and the patch side by side, the attack has four stages:

  1. The attacker, signed in with any account the hub accepts, negotiates a connection and keeps the token. Before the fix, the server records nothing about who asked for it.
  2. The attacker gets a privileged user to open a crafted link to the application. The victim's browser makes the connection's first transport request, carrying the victim's own credentials.
  3. The dispatcher sets the connection's user to the victim. The Hub pipeline starts, and OnConnectedAsync runs as the victim.
  4. The attacker sends Hub invocation messages to the send endpoint using the token. The dispatcher writes them into the connection without checking the sender, and the Hub executes them with Context.User set to the victim.

The fix breaks the chain at stage 3. The victim's transport request no longer matches the identity captured at negotiate, so it gets a 403 and the connection never takes on the victim's identity.

What an attacker can do with CVE-2026-114146

  • Call any Hub method the victim can call. Method-level [Authorize(Roles = "Admin")] attributes and policy checks evaluate Context.User, which is now the victim. On an operations dashboard, that could be a method that approves a transfer or pushes a system-wide alert. In a chat app, one that bans users or deletes messages. On a trading front end, one that submits or cancels orders.
  • Run connection-time logic as the victim. Code in OnConnectedAsync that adds the connection to groups based on the caller's roles or tenant (Groups.AddToGroupAsync), records presence, or writes audit entries uses the victim's claims.
  • Put the victim's name on the attacker's actions. Anything a Hub logs with Context.UserIdentifier or Context.User.Identity.Name attributes the attacker's activity to the victim, which muddies incident response.
  • Receive traffic meant for the victim. The connection is registered under the victim's user ID and any groups joined at connect time, so pushes sent with Clients.User(...) or to those groups are written to a connection whose token the attacker holds.
  • Reach systems behind the Hub. Hub methods that call internal APIs, databases, or message queues on behalf of the caller carry the victim's authority past SignalR. That is why the Scope metric is Changed.

Which ASP.NET Core and .NET versions are affected by CVE-2026-114146?

The vulnerable code ships in the ASP.NET Core shared framework (Microsoft.AspNetCore.App), so any app on an affected runtime that hosts a SignalR hub carries it without a package reference. That includes Blazor Server apps and Blazor Web Apps with interactive server rendering, which run on a SignalR hub mapped by MapBlazorHub or AddInteractiveServerRenderMode. The flaw is in the server's connection dispatcher. Apps that only use the SignalR client are not exposed.

Microsoft only assesses supported versions, so .NET 6 and .NET 7 do not appear in its advisory, and scanners keyed to that advisory will not flag them. .NET 6 reached end of support on November 12, 2024, .NET 7 on May 14, 2024, and .NET 8 and .NET 9 on November 10, 2026, per endoflife.date. .NET 10 and .NET 11 are both supported until November 14, 2028.

Microsoft's record also lists Visual Studio 2022 17.14 (fixed in 17.14.45) and Visual Studio 2026 18.11 (fixed in 18.11.4) and 18.12 (fixed in 18.12.0), because those installs carry the affected runtimes. Updating Visual Studio patches developer machines, not the servers your hubs run on.

How to check if you are exposed to CVE-2026-114146

  • Run dotnet --list-runtimes on each host and compare the Microsoft.AspNetCore.App versions against the table.
  • Self-contained deployments embed their own runtime. Updating the machine-wide runtime does not touch them; they must be rebuilt and redeployed.
  • For containers, check the version of the mcr.microsoft.com/dotnet/aspnet base image each service was built on.
  • On ASP.NET Core 2.3 for .NET Framework, check the resolved version of Microsoft.AspNetCore.Http.Connections, which usually arrives transitively through Microsoft.AspNetCore.SignalR.
  • Find the hubs themselves by searching for MapHub<, MapBlazorHub, and AddInteractiveServerRenderMode, then note which ones accept cookie or Windows authentication and expose privileged methods. Those go first.

How to fix CVE-2026-114146

What changes after you patch

The fix rejects identity mismatches that older versions let through, and a few legitimate setups can trip it:

  • If your app authenticates the negotiate request and the transport request differently (two authentication schemes, or a token handler that only reads the access_token query string on some paths), the two requests can produce different principals. The comparison includes the claim issuer, so the same user ID issued by two different schemes no longer matches.
  • Clients that negotiate while signed in and then connect anonymously, or the reverse, now fail with a 403.
  • Patched servers log UserNameChanged (event ID 17, category Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher) alongside each 403. ASP.NET Core 2.3 logs the same event name under ID 14. Alert on it. Each one is either a configuration problem to fix or a blocked attempt.

Related ASP.NET Core and SignalR CVEs

The same November 10, 2026 release fixed a second identity-binding bug in the SignalR stack. CVE-2026-109481 (7.5, CWE-352) let a cross-site request to Blazor's /_blazor/refresh path bind a victim's authenticated principal to an attacker-owned circuit. It only affects .NET 11 previews, but it is the same class of mistake through a different door. CVE-2026-114147 (7.5) is a SignalR denial of service in Redis scale-out, where case-mismatched group removal leaves subscriptions behind and amplifies load.

Earlier in 2026, CVE-2026-92707 brought memory exhaustion to apps using SignalR's MessagePack hub protocol; see CVE-2026-92707 and SignalR and the 12 MessagePack-CSharp CVEs affecting .NET 6 and SignalR.

The rest of the November batch with directory entries: CVE-2026-109478 (HTTP header parsing denial of service), CVE-2026-114149 (multipart form parsing resource allocation), and CVE-2026-109476 (cookie path output encoding).

The release also added CVE-2026-118741 (8.1, CWE-640), the follow-up to October 2026's CVE-2026-70175 Identity password-reset account takeover, where a crafted email address that the account lookup treats as the victim's lets an attacker take over the account. CVE-2026-118741 fixes the tools that wrote that vulnerable Identity code into apps: the Microsoft.DotNet.Web.ProjectTemplates packages for .NET 8 through .NET 11, dotnet-scaffold, and the Microsoft.VisualStudio.Web.CodeGeneration.Design and Microsoft.VisualStudio.Web.CodeGenerators.Mvc scaffolders. Updating those tools does not rewrite code they already generated, so apps built from the affected templates need a manual repair.

The most recent Critical-scored ASP.NET Core bugs before this one were October 2026's Kestrel request smuggling pair, CVE-2026-88092 (9.4) and CVE-2026-70188 (9.1), covered in our Kestrel post. CVE-2025-55315 (9.9) remains the highest-scored; our CVE-2025-55315 FAQ covers it in depth.

Taking action on CVE-2026-114146

If you run .NET 10 or .NET 11, install 10.0.15 or 11.0.0, rebuild self-contained apps and images, and watch the UserNameChanged log for the next few weeks. Anything it catches is either a client to fix or someone testing the hole you just closed.

If you run .NET 8 or .NET 9, install 8.0.34 or 9.0.23 now. They close CVE-2026-114146, and they are the last patches Microsoft will ship for either line. SignalR needed two security fixes in this release alone. The next one reaches .NET 8 and .NET 9 with no OSS fix, and moving to .NET 10 is a migration with its own test cycle, not a patch you apply the week a CVE drops. The .NET Cliff lays out the options, and our .NET 8 and .NET 9 end of support guide covers the dates.

If you run .NET 6 or .NET 7, you are already past that point. This flaw predates both lines, and Microsoft will not fix either of them.

NES for .NET delivers drop-in, security-patched releases for .NET 6, .NET 8, and .NET 9, including container images, so you stay on the version your application was built against while you migrate on your own schedule. NES for .NET 6.0.49 already resolves CVE-2026-114146. HeroDevs is a member of the .NET Security Group.

‍

Table of Contents
Author
Greg Allen
Chief Technology Officer
Open Source Insights Delivered Monthly