# Root Cause Analysis — CVE-2026-21525

## Summary

| Field | Value |
|---|---|
| **CVE** | CVE-2026-21525 |
| **Binary** | rasman.dll (Remote Access Connection Manager) |
| **Component** | `RpcConnect` — shared RPC binding cache to the RRAS router |
| **Bug Class** | Use-After-Free of shared binding handle, surfaced as NULL Pointer Dereference (CWE-476; MSRC) |
| **Impact** | Denial of Service — RasMan service (svchost) crash; kills all VPN/RAS connectivity |
| **CVSS 3.1** | 6.2 (AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| **Exploited ITW** | Yes |
| **Patch** | February 2026 Patch Tuesday (KB5077181, Win11 24H2 10.0.26100.7309 → 10.0.26100.7705) |
| **Diff evidence** | `rasman_dll-kb5077181.md` — 6 code-changed functions; fix is feature-flagged `Feature_VPN_BugFixes_25B_Use_After_Free_Fix` |

## Vulnerability Overview

`RpcConnect` in rasman.dll establishes an RPC binding from the RasMan service
to the RRAS router, either over the local `ncalrpc:[RasmanLrpc]` endpoint or the
`ncacn_np:\PIPE\ROUTER` named pipe. For the local case, the binding handle is
cached in a **global variable `g_hBinding`** and shared by all callers, with a
global reference count `_g_dwRefCount`, both protected by `g_RpcCs`.

The pre-patch error path frees this **shared global binding** while other
callers still hold it, and increments the reference count even when the
operation failed — so subsequent callers receive a dangling (or NULL) binding
handle and dereference it, crashing the RasMan service.

## Root Cause — error path frees the shared global binding

### Pre-patch (10.0.26100.7309)

```c
// g_hBinding: GLOBAL cached RPC binding shared by all RasMan clients
// _g_dwRefCount: GLOBAL refcount

// Fast path: reuse cached binding
if (g_hBinding != NULL) {
    _g_dwRefCount++;          // refcount++
    *out = g_hBinding;
    goto LAB_1;
}

// ... full connect path; for the local endpoint, Binding = &g_hBinding ...

if (Status != 0) {            // ERROR PATH
    if (bVar1) {              // a binding was created
        if (*Binding != NULL)
            RpcBindingFree(Binding);   // BUG: Binding == &g_hBinding here!
        *Binding = NULL;               // zeroes the GLOBAL, still held by others
    }
    pwVar10 = I_RpcMapWin32Status(Status);
}
if (bVar11) {
    _g_dwRefCount++;          // BUG: refcount++ even on failure
    *local_78 = g_hBinding;   // hands caller the freed/NULL global binding
}
```

Two interlocking defects:

1. **UAF**: on any error after a local binding was created
   (`RpcBindingFromStringBindingW` / `RpcBindingSetAuthInfoExW` failure), the code
   frees `g_hBinding` — the binding every other RasMan client is concurrently
   using through the fast path.
2. **Refcount/NULL confusion**: `_g_dwRefCount` is incremented even on failure,
   and the caller is returned the just-freed (or NULL) `g_hBinding`. The next
   RPC call on that handle dereferences freed RPC runtime state → **NULL pointer
   dereference** inside the RasMan service process (MSRC's CWE-476).

The internal feature flag shipped for this fix is literally named
`Feature_VPN_BugFixes_25B_Use_After_Free_Fix`, confirming the UAF nature of the
bug. MSRC rates the externally observable crash as CWE-476 / DoS.

### Post-patch (10.0.26100.7705)

- The `Feature_817320250` (UAF fix) gate call is **removed entirely** — the fix
  is now always on.
- On error, only the **caller's own** binding handle (`in_stack_00000068`) is
  freed; `g_hBinding` is never freed on the error path.
- `_g_dwRefCount++` only executes on the **success** path, after
  `*out = g_hBinding`.
- The cleanup `FreeSid(local_80)` now runs unconditionally (previously gated
  behind the feature check).

```c
if (Status != 0) {
    if (bVar1) {
        if (*out != NULL)
            RpcBindingFree(out);    // FIXED: caller-local handle only
        *out = NULL;
    }
    pwVar10 = I_RpcMapWin32Status(Status);
    if (!bVar11) goto LAB_1;        // no refcount++, no global hand-out
}
*out = g_hBinding;
_g_dwRefCount++;                    // success path only
```

## Call Flow / Reachability

```
Any local process using RAS APIs (RasDial, VPN connect, IKEv2/PPP/SSTP/L2TP)
  → rasapi32.dll client
    → RPC to RasMan service (svchost -k netsvcs, SYSTEM)
      → RasRPCBind / RasRpcConnect / RemoteSubmitRequestLocal
        → RpcConnect *** VULNERABLE ***
          → RpcBindingFromStringBindingW / RpcBindingSetAuthInfoExW
            → on failure: RpcBindingFree(&g_hBinding)   [pre-patch]
          → next caller's fast path derefs freed g_hBinding → crash
```

- **Attack surface**: local, **no privileges required** (PR:N per MSRC). Any
  local user can initiate RAS connections (e.g. `rasdial`, `RasDialW`), which
  drives the RasMan service through `RpcConnect`.
- **Trigger condition**: force the local-endpoint connect path to fail after a
  binding handle was created, while another thread/process holds or immediately
  re-acquires the cached global binding. Failures in
  `RpcBindingSetAuthInfoExW` (auth/QoS setup) are the most plausible race-free
  error source; a connect/cancel race increases the odds of hitting the error
  path mid-use.
- **Effect**: access violation inside the RasMan service host
  (`svchost.exe` running `RasMan`), service crash → all VPN/RAS connectivity
  drops until the service restarts.

## Exploitation Notes (ITW)

MSRC marks this CVE as **exploited in the wild** as a local DoS. No public
technical writeup exists at time of writing; the analysis above is derived
entirely from the binary diff. Because the crash is in a SYSTEM service, the
primitive is a reliable, low-noise local DoS — valuable as a force-reboot /
availability-impact gadget in an attack chain.

## Patch Analysis

| Function | Match | Change |
|---|---|---|
| `RpcConnect` | 48% | Error path frees caller-local binding only; refcount++ on success only; feature gate retired |
| `Feature_VPN_BugFixes_25B_Use_After_Free_Fix__private_IsEnabledDeviceUsageNoInline` | 96% | Flag retired (no longer called from RpcConnect) |
| `wil_details_FeatureReporting_*`, `wil_details_IsEnabledFallback`, `FeatureStateCache_TryEnableDeviceUsageFastPath` | 90–94% | Mechanical churn from the retired gate |

## Detection

### YARA — patched rasman.dll (fix retired the feature gate)

```yara
rule CVE_2026_21525_RasMan_RpcConnect
{
    meta:
        description = "Detects rasman.dll builds with the CVE-2026-21525 RpcConnect shared-binding UAF"
        cve         = "CVE-2026-21525"

    strings:
        $endpoint = "RasmanLrpc" wide
        $router   = "\\PIPE\\ROUTER" wide
        $sec      = "Security=Impersonation Dynamic True" wide
        $flag     = "Feature_VPN_BugFixes_25B_Use_After_Free_Fix" ascii wide

    condition:
        uint16(0) == 0x5A4D and
        $endpoint and $router and $sec and
        not $flag   // patched: gate retired
}
```

### Sigma — RasMan service crash

```yaml
title: CVE-2026-21525 RasMan Service Crash (Shared Binding UAF)
id: b7e2c1aa-2152-5c00-9e25-cve202621525
status: experimental
description: >
    Detects crashes of the Remote Access Connection Manager service consistent
    with CVE-2026-21525 exploitation (error path frees the shared g_hBinding
    RPC binding; next caller dereferences freed/NULL handle).
author: OnlyFm252
date: 2026/07/28
references:
    - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-21525
logsource:
    product: windows
    service: system
detection:
    selection_restart:
        EventID: 7031
        ServiceName: 'Remote Access Connection Manager'
    selection_app_error:
        EventID: 1000
        FaultingModule|contains: 'rasman.dll'
    condition: 1 of selection_*
level: high
tags:
    - attack.impact
    - attack.t1499
    - cve.2026.21525
```

### Sysmon

```xml
<Sysmon schemaversion="4.90">
  <EventFiltering>
    <!-- Event 1: watch for processes spawned right after rasman crash/restart -->
    <ProcessCreate onmatch="include">
      <CommandLine condition="contains">RasMan</CommandLine>
    </ProcessCreate>
    <!-- Event 18: named-pipe connects to the RRAS router pipe -->
    <PipeEvent onmatch="include">
      <PipeName condition="is">\PIPE\ROUTER</PipeName>
    </PipeEvent>
  </EventFiltering>
</Sysmon>
```

Recommended additional logging: enable **System log** monitoring for
Service Control Manager events 7031/7034 on `RasMan`, and Application Error
1000/1001 with faulting module `rasman.dll`.

## References

- [MSRC Advisory — CVE-2026-21525](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-21525)
- Diff: `rasman_dll-kb5077181.md` (10.0.26100.7309 → 10.0.26100.7705)
- Feature flag evidence: `Feature_VPN_BugFixes_25B_Use_After_Free_Fix` (present in pre-patch build, retired in patch)
