# RCA — CVE-2026-33824 (ikeext.dll double-free)

- **Binary:** `ikeext.dll` (Windows IKE Extension, hosted in svchost as IKEEXT service)
- **Patch:** KB5083769 (April 2026), `10.0.26100.7920` → `10.0.26100.8246`
- **Class:** CWE-415 double-free | **Impact:** RCE (SYSTEM) | **ITW:** no
- **Diff:** `ghidriff/CVE-2026-33824/output/ikeext-10.0.26100.7920.dll-ikeext-10.0.26100.8246.dll.ghidriff.md`

## Component

The Windows IKE Extension service (`ikeext.dll`) implements IKEv1 and IKEv2 key exchange for IPsec VPNs. It listens on UDP/500 (IKE) and UDP/4500 (NAT-T). The bug is in the IKEv2 fragment reassembly path, which is reachable from any remote host that can send UDP packets to these ports — no authentication required.

## Root cause

During an IKE_SA_INIT exchange, a **Microsoft Security Realm Vendor ID** payload causes `IkeHandleSecurityRealmVendorId()` to allocate a heap blob and store the pointer in the **Main Mode Security Association (MMSA)** structure at offset `0x208`.

When a fragmented IKE_AUTH message arrives, `IkeReinjectReassembledPacket()` copies a slice of the MMSA — offsets `0x178` through `0x21F` — into a local stack struct. This slice includes the blob pointer at `0x208`. The struct is then passed to `IkeQueueRecvRequest()`, which **shallow-copies** it into a heap-allocated work item. While the reassembly buffer at offset `0x10` is deep-copied, the Security Realm blob pointer at offset `0xC8` remains an alias of `MMSA+0x208`.

Later, when the thread pool processes the work item:
1. `IkeDestroyPacketContext()` frees the blob at work-item offset `0xC8` via `WfpMemFree` → **first free**.
2. When the MMSA is torn down, `IkeCleanupMMNegotiation()` → `IkeDerefMMSA()` → `IkeFreeMMSA()` frees the blob at `MMSA+0x208` → **second free** (same allocation).

```
IKE_SA_INIT (Security Realm Vendor ID)
  IkeHandleSecurityRealmVendorId()
    WfpMemAlloc(blob) → MMSA+0x208

IKE_AUTH fragments (SKF)
  IkeReinjectReassembledPacket()
    memcpy(stack_struct, MMSA+0x178, 0xA8)   // includes blob ptr at 0x208
    IkeQueueRecvRequest(stack_struct)
      shallow-copy into heap work-item
      // work-item+0xC8 now aliases MMSA+0x208

Thread-pool processes work item
  IkeDestroyPacketContext()
    WfpMemFree(work_item+0xC8)   // FIRST FREE

MMSA cleanup
  IkeFreeMMSA()
    WfpMemFree(MMSA+0x208)       // SECOND FREE (same pointer)
```

## Patch

Microsoft added feature gate **`Feature_3844326713`** that enables a **deep-copy** of the Security Realm blob instead of aliasing the original pointer.

### `IkeReinjectReassembledPacket` (78% match)

**Before:** The blob pointer at `param_3[0x12]` was copied directly into the stack struct (`local_50 = param_3[0x12]`). After `IkeQueueRecvRequest`, only the reassembly buffer (`local_108`) was freed.

**After:** When the feature is enabled:
1. `local_50` is zeroed and `uStack_58` is masked.
2. If `param_3[0x12]` is non-null, `IkeCopyBlob()` deep-copies the blob into `&uStack_58`.
3. At function exit, if the feature is enabled **and** the queue request succeeded, `WfpMemFree(&local_50)` frees the **copy** — not the original.

```diff
+    if (uVar1 != 0) {          // Feature_3844326713 enabled
+      local_50 = 0;
+      uStack_58 = uStack_58 & 0xffffffff00000000;
+      if (param_3[0x12] != 0) {
+        puVar4 = &uStack_58;
+        lVar3 = IkeCopyBlob((uint *)(param_3 + 0x11), (uint *)puVar4, _Size);
+        ...
+      }
+    }
     ...
+  if ((uVar1 != 0) && (lVar3 != 0)) {
+    WfpMemFree(&local_50);     // frees the COPY
+  }
   WfpMemFree(&local_108);
```

### `IkeFreeMMSA` (98% match)

The function was also updated with the feature-gate pattern. The old path freed `MMSA+0x208` directly; the new path ensures the field is only freed when the work item does not own a copy.

### `IkeBeginLifeExpireInplaceRekeyIkeV2` (25% match)

Same pattern: added `IkeCopyBlob` + `WfpMemFree` under the feature gate for the rekey path.

### Added
- `Feature_3844326713__private_IsEnabledDeviceUsageNoInline` — feature gate callable from `IkeReinjectReassembledPacket`, `IkePostAcquire`, and `IkeBeginLifeExpireInplaceRekeyIkeV2`.

## Reaching the bug (network recipe)

| Requirement | Detail |
|---|---|
| Network | UDP/500 (IKE) or UDP/4500 (NAT-T) reachable to the target |
| Auth | None — unauthenticated |
| Packet 1 | **IKE_SA_INIT** request carrying the Microsoft Security Realm Vendor ID payload. Vendor ID bytes: `68 6a 8c bd fe 63 4b 40 51 46 fb 2b af 33 e9 e8` |
| Packet 2+ | **IKE_AUTH** request split into two or more **Encrypted Fragment (SKF)** payloads. At UDP payload offset 16: `35 20 23 08` (SKF type 0x35, IKEv2 ver 0x20, IKE_AUTH 0x23, Initiator 0x08). At offset 20: `00 00 00 01` (fragment number 1). |
| Endian | All multi-byte values are **big endian** |
| NAT-T | On port 4500, IKE packets are prepended with a 4-byte non-ESP marker (`\x00\x00\x00\x00`), shifting all offsets by +4 |

## Call flow

```
Attacker UDP/500 or UDP/4500
  IKE_SA_INIT (Security Realm Vendor ID)
    ikeext!IkeHandleSecurityRealmVendorId
      WfpMemAlloc → MMSA+0x208

  IKE_AUTH fragments (SKF x2+)
    ikeext!IkeInsertFragEntry
    ikeext!IkeReinjectReassembledPacket
      memcpy(stack_struct, MMSA+0x178, 0xA8)  // aliases blob ptr
      ikeext!IkeQueueRecvRequest
        shallow-copy to heap work-item

  Thread pool callback
    ikeext!IkeDestroyPacketContext
      WfpMemFree(work_item+0xC8)   // FIRST FREE

  MMSA teardown
    ikeext!IkeCleanupMMNegotiation
      ikeext!IkeDerefMMSA
        ikeext!IkeFreeMMSA
          WfpMemFree(MMSA+0x208)   // SECOND FREE (double-free)
```

## Detection notes (blue team)

- **Network detection:** Monitor UDP/500 and UDP/4500. Correlation rule:
  1. IKE_SA_INIT from source IP with Vendor ID `68 6a 8c bd fe 63 4b 40 51 46 fb 2b af 33 e9 e8` (offset 17+: `20 22 08` for IKEv2 + IKE_SA_INIT + Initiator flag).
  2. Followed by IKE_AUTH SKF fragments from same source with `35 20 23 08` at offset 16 and `00 00 00 01` at offset 20.
  3. On port 4500, add +4 to all offsets for the non-ESP marker.

- **Crash signature:** Heap corruption or double-free crash in `ikeext!IkeFreeMMSA` or `ikeext!WfpMemFree`. The service runs as SYSTEM in svchost, so a crash may manifest as a service restart (Event ID 7034) rather than a full bugcheck.

- **Behavioral tell:** A remote host sending IKEv2 fragments with SKF payloads to a Windows VPN/gateway role that does not normally expect fragmented IKE_AUTH. Legitimate IKEv2 fragment reassembly is rare in most enterprise environments.

- **ETW:** `Microsoft-Windows-IKEEXT` provider may log anomalous fragment reassembly events; correlate with the network signature above.
