Automated Threat Modeling

Brief Summary: CVE-2026-114147, an ASP.NET Core SignalR Redis Subscription Leak Leading to Denial of Service

A brief summary of CVE-2026-114147, a CWE-401 resource leak in the ASP.NET Core SignalR Redis backplane where case-mismatched group removal leaves subscriptions alive. Covers the root cause, patched versions, deployment pitfalls, and detection approaches.

CVE Analysis

11 min read

Gaurav Sarraf
Gaurav Sarraf

2026-11-10

Brief Summary: CVE-2026-114147, an ASP.NET Core SignalR Redis Subscription Leak Leading to Denial of Service
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

Teams that scale ASP.NET Core SignalR across several servers commonly use the Redis backplane, which keeps per-connection group membership in step with Redis pub/sub subscriptions. CVE-2026-114147 breaks that bookkeeping. When a group removal uses different letter casing from the original join, the Redis subscription, and the server memory that tracks it, stay alive indefinitely. A remote, unauthenticated client can use this to accumulate resources over time and degrade availability.

Microsoft rates the issue 7.5 (High) and classifies it as CWE-401, Missing Release of Memory after Effective Lifetime. Fixes shipped in the November 10, 2026 .NET servicing updates. The exposure is narrower than "every ASP.NET Core app". The vulnerable code lives in Microsoft.AspNetCore.SignalR.StackExchangeRedis, so for most teams the practical question is whether they run the SignalR Redis backplane. As of the dated intelligence we reviewed, there is no confirmed exploitation, no indexed public exploit, and no CISA KEV listing.

Technical Information

What the advisory says

Microsoft's developer advisory, GHSA-wr8v-9x5q-ggcm, states that SignalR Redis scale-out can mishandle group removal when the casing of the group name does not match, leaving subscriptions "in a state that permits persistent resource amplification and denial of service." The bug is a resource-lifetime failure rather than a memory corruption primitive, and the published CVSS assessment scores no confidentiality or integrity impact.

Root cause: two lookups that disagreed on case

Before the fix, each connection's RedisFeature tracked its group names in a case-insensitive set:

private sealed class RedisFeature : IRedisFeature { public HashSet<string> Groups { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase); }

The Redis channel for a group, built by _channels.Group(groupName), is case sensitive, and so is the subscription dictionary in RedisSubscriptionManager, a ConcurrentDictionary<string, HubConnectionStore> keyed with StringComparer.Ordinal. Removal unsubscribed first and then removed the name from the local set:

private async Task RemoveGroupAsyncCore(HubConnectionContext connection, string groupName) { var groupChannel = _channels.Group(groupName); await _groups.RemoveSubscriptionAsync(groupChannel, connection, this, static (state, channelName) => { ... }); var feature = connection.Features.GetRequiredFeature<IRedisFeature>(); var groupNames = feature.Groups; if (groupNames != null) { lock (groupNames) { groupNames.Remove(groupName); } } }

A commit in dotnet/aspnetcore describes the root cause: "Casing was different across group collections resulting in inconsistent cleanup of connection state."

How the state drifts apart

  1. A connection is added to group Chat. The set stores Chat and the backplane subscribes to the channel for Chat.
  2. The same connection is removed from group chat. Unsubscribing from the chat channel does nothing, because that channel does not exist.
  3. groupNames.Remove("chat") still succeeds, because the set ignores case. The connection forgets the group, but it stays inside the HubConnectionStore for the Chat channel, and the Redis subscription stays open.
  4. On disconnect, OnDisconnectedAsync only cleans up groups still listed in feature.Groups. The orphaned entry is never released, which is the CWE-401 condition named in the advisory.

Microsoft's regression test reproduces this sequence. A connection joins customgroup and then leaves CUSTOMGROUP. The group disappears from the connection's own list, but the connection is never removed from the subscription store, so the Redis channel is never unsubscribed.

Attack flow

Based on the advisory and the corrected source, abuse would follow this pattern:

  1. The attacker connects to a hub on an application that uses the SignalR Redis backplane. The most exposed hubs are those whose methods pass client-supplied strings straight into group join and leave calls.
  2. The attacker joins a group using one casing.
  3. The attacker leaves the group using a different casing. The connection's group set drops the name while the subscription and its HubConnectionStore entry remain.
  4. The attacker repeats the cycle with further casing variants (CHAT, cHat, and so on). Each variant maps to a separate Redis channel, so each cycle adds subscriptions that never go away.
  5. Disconnecting does not help the server, because cleanup reads only feature.Groups and leaked entries survive the connection. Resource use grows cumulatively, not through any single malicious request.

Every step is an ordinary, well-formed SignalR hub call. The advisory does not publish a request sequence, payload, required rate, exhaustion threshold, or time to failure, and we do not speculate about them here.

Severity scoring

Microsoft's CNA vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H:

  • Attack vector: Network
  • Attack complexity: Low
  • Privileges required: None
  • User interaction: None
  • Scope: Unchanged
  • Confidentiality / Integrity: None
  • Availability: High

The 7.5 score is Microsoft's assessment. NVD lists its own assessment as not yet provided, with the record awaiting enrichment. The temporal vector includes E:U, meaning exploit maturity is unproven.

Patch Information

Microsoft released the official fix in the November 10, 2026 .NET servicing updates. The fix is in the SignalR Redis scale-out backplane, Microsoft.AspNetCore.SignalR.StackExchangeRedis.

Fixed versions

  • .NET 8.0: 8.0.34 (KB5135243 on Windows)
  • .NET 9.0: 9.0.23 (KB5135242 on Windows)
  • .NET 10.0: 10.0.15
  • .NET 11.0: 11.0.0 GA (every preview and RC up to 11.0.0-rc.3.26522.148 is affected)

The fix ships in two places, and you need both:

  • the shared framework (Microsoft.AspNetCore.App.Runtime.* for every RID)
  • the standalone NuGet package Microsoft.AspNetCore.SignalR.StackExchangeRedis

The advisory warns that updating the machine-wide runtime or SDK does not update an application's own package reference. In practice, that means:

  • Centrally installed runtime: install the corrected ASP.NET Core runtime and restart affected applications.
  • Direct reference to the standalone package: bump the resolved package version, then rebuild and redeploy.
  • Self-contained applications: rebuild against the patched runtime, update the package, and redeploy.
  • Only the host runtime or SDK updated: do not assume the package reference changed. Update it separately.

Microsoft documents no separate workaround or configuration switch.

How the fix works

Microsoft did not link a fix commit from the advisory or the release notes, which still list the CVE as TBD. The public dotnet/aspnetcore source does show the correction. What follows is our reading of the patched code on the main branch.

1. One comparison rule everywhere. The group set now uses ordinal (case-sensitive) comparison, matching the Redis channel names and the subscription dictionary. The feature also gains a disconnect flag:

private sealed class RedisFeature : IRedisFeature { public HashSet<string> Groups { get; } = new HashSet<string>(StringComparer.Ordinal); public bool IsDisconnected { get; set; } }

2. The group set and the subscription change together, under one lock. RedisHubLifetimeManager no longer edits feature.Groups itself. It passes the group name into new RedisSubscriptionManager methods. Removal now updates the local set first and only touches the subscription if that succeeds, so the two cannot drift apart:

public async Task RemoveGroupSubscriptionAsync(string channelName, string groupName, HubConnectionContext connection, object state, Func<object, string, Task> unsubscribeMethod) { await _lock.WaitAsync(); try { var groupNames = connection.Features.GetRequiredFeature<IRedisFeature>().Groups; if (!groupNames.Remove(groupName)) { // Group not tracked by this connection, so no need to remove it from the subscription list return; } await RemoveSubscriptionAsyncCore(channelName, connection, state, unsubscribeMethod); } finally { _lock.Release(); } }

The add path, AddGroupSubscriptionAsync, mirrors this. It records the name, attempts the subscription, and removes the name again if the subscription fails or throws, so the set only lists groups that actually have a live subscription.

3. No adds after disconnect. OnDisconnectedAsync now calls _groups.SetConnectionDisconnectedAndGetGroupNamesAsync(connection). Under the same lock, that sets IsDisconnected = true and snapshots the groups to tear down. AddGroupSubscriptionAsync returns immediately when feature.IsDisconnected is set, so a late cross-server group add cannot register a dead connection again after cleanup has read the list.

After the patch, a remove request with the wrong casing is a harmless no-op. Every subscription the backplane creates stays recorded in the connection's group set, so disconnect always frees it.

Detection Methods

There are no public exploit signatures for CVE-2026-114147: no IoCs, YARA, Sigma, or Snort/Suricata rules. ZDI's November 2026 review lists the bug as neither public nor exploited, and Senserva reported no observed exploitation and no EPSS score as of 2026-11-11. Detection therefore works on two levels. Exposure detection, which means finding vulnerable builds, is the most reliable. Behavioral hunting looks for the leak itself.

Exposure detection

The advisory FAQ gives the check. Compare both the deployed ASP.NET Core runtime and the application's resolved Microsoft.AspNetCore.SignalR.StackExchangeRedis version against the affected ranges. The runtime packages and the standalone package share the same thresholds (8.0.34, 9.0.23, 10.0.15, 11.0.0).

  • Scan the resolved dependency graph. The advisory is published as GHSA-wr8v-9x5q-ggcm for the NuGet ecosystem, so SCA tools that consume GitHub Advisory data can flag vulnerable references. Look at transitive resolution, not only direct references.
  • Inventory self-contained apps separately. They carry their own runtime, so a host-level runtime scan misses them. Inspect build output and container images.
  • Correlate host and app data. A host can show a fixed runtime while an application still loads a vulnerable package.
  • Windows patch checks. The fixes ship as KB5135241, KB5135242 (.NET 9.0), and KB5135243. Senserva notes that the PowerShell hotfix-listing cmdlet does not show them because .NET is not a Windows component. Compare installed product versions against the fixed builds instead.
  • SDK hint. The .NET 9.0.23 release notes list SDKs 9.0.321 and 9.0.124 as carrying the 9.0.23 runtime. The SDK version only tells you which runtime the SDK bundles, so confirm the runtime and package each app actually uses.
  • Scanner content. Rapid7's Vulnerability Database has an entry for the CVE. Confirm whether your scanner's authenticated check covers NuGet package references or only installed runtimes.

Behavioral hunting

No vendor has published exploitation telemetry. These signals follow from the bug's mechanics and should be tuned against your own baselines:

  • Redis channels that never go away. Each SignalR group maps to a Redis pub/sub channel. Watch active channel and subscription counts on the backplane. Growth that does not fall when connected client numbers drop is the strongest leak signal.
  • Memory that is never released. Track SignalR host memory against active connection count. A working set that keeps rising across connection churn and only recovers after a process restart matches the CWE-401 pattern.
  • Subscribe and unsubscribe imbalance. When the last member leaves a group, the Redis lifetime manager logs an unsubscribe event (RedisLog.Unsubscribe). With verbose SignalR Redis logging, group channels that are subscribed but never unsubscribed after their connections end are worth investigating.
  • Group names that differ only by case. If hubs let clients choose group names, log the names passed to AddToGroupAsync and RemoveFromGroupAsync. A client that repeatedly joins one casing and leaves another closely mirrors Microsoft's regression test.
  • Code review. Find hub methods that pass client-supplied strings directly into group join or leave calls. Those are the endpoints an attacker would use.

Limits of network detection

The trigger is an ordinary, well-formed hub call, usually inside an established WebSocket. Generic WAF or IDS rules based on request rate or malformed requests are unlikely to separate an attack from normal traffic. Application and Redis backplane telemetry is far more useful here than perimeter signatures.

Affected Systems and Versions

Affected standalone package Microsoft.AspNetCore.SignalR.StackExchangeRedis versions (inclusive), plus the corresponding ASP.NET Core runtime packages on Windows, Linux, and macOS across all listed architectures:

  • .NET 8: 8.0.0-preview.1.23112.2 through 8.0.33, fixed in 8.0.34
  • .NET 9: 9.0.0-preview.1.24081.5 through 9.0.22, fixed in 9.0.23
  • .NET 10: 10.0.0-preview.1.25120.3 through 10.0.14, fixed in 10.0.15
  • .NET 11: 11.0.0-preview.1.26104.118 through 11.0.0-rc.3.26522.148, fixed in 11.0.0

The vulnerable configuration is any application using the SignalR Redis scale-out backplane. Risk is highest where clients can influence group names.

NVD additionally lists Visual Studio entries:

  • Visual Studio 2022 version 17.14: 17.14.0 before 17.14.45
  • Visual Studio 2026 version 18.11: 18.11.0 before 18.11.4
  • Visual Studio 2026 version 18.12: displayed as 18.12.0 before 18.12.0, which is internally inconsistent and does not define a usable range

Vendor Security History

Microsoft services ASP.NET Core under its .NET support policy, which requires the latest patch for continued support and normally ships cumulative updates on the second Tuesday of the month. For independent historical context, the Cyber Safety Review Board called the 2023 Exchange Online intrusion preventable, concluded that Microsoft's security culture was inadequate, and noted Microsoft relied on a customer to detect the compromise. That incident concerns a different product and is not connected to this CVE. Microsoft's July 2026 Secure Future Initiative report states the company remediated more than 550,000 instances of critical and high-risk open-source vulnerabilities, addresses about 3 million container vulnerability instances monthly through automated patching, and published 1,989 CVEs with CWE and CPE annotations. These are company-reported figures.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization