# CVE-2023-36802 — Microsoft Streaming Service Proxy (`mskssrv.sys`) Type Confusion EoP

---

## Summary

| **Product**           | Microsoft Windows — `mskssrv.sys` (Microsoft Kernel Streaming Server) |
|-----------------------|-----------------------------------------------------------------------|
| **Vendor**            | Microsoft Corporation |
| **Severity**          | Important (CVSS 7.8) |
| **Affected Versions** | Windows 10 1809+, Windows 11 21H2/22H2, Windows Server 2019+ |
| **Tested Version**    | Windows 10.0.22621.1848 (pre-patch) vs 10.0.22621.2283 (post-patch) |
| **Impact**            | Elevation of Privilege — Local kernel code execution as SYSTEM |
| **CVE ID**            | CVE-2023-36802 |
| **CWE**               | CWE-843: Access of Resource Using Incompatible Type ('Type Confusion') |
| **PoC Available**     | Yes (trigger PoC — demonstrates the type confusion primitive) |
| **Exploit Available** | Exploited in the wild (Microsoft advisory: "Exploitation Detected") |
| **Patch Available**   | Yes |
| **Patch Date**        | September 2023 — KB5030219 |
| **Exploitation Maturity** | Exploitation Detected |

---

## CVSS 3.1 Scoring

**Base Score:** 7.8 (HIGH)
**Vector String:** `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`

| **Metric** | **Value** | **Rationale** |
|---|---|---|
| **Attack Vector (AV)** | Local | Requires local authenticated session to open KS device |
| **Attack Complexity (AC)** | Low | Standard IOCTLs; no race conditions required |
| **Privileges Required (PR)** | Low | Standard user account; no admin rights needed |
| **User Interaction (UI)** | None | No interaction from any other user required |
| **Confidentiality (C)** | High | Kernel memory read via OOB access |
| **Integrity (I)** | High | Kernel memory corruption via virtual call on wrong-type object |
| **Availability (A)** | High | Kernel crash (BugCheck) on failed exploitation |

---

## Product Description

`mskssrv.sys` is the Microsoft Kernel Streaming Server proxy driver, part of the Kernel Streaming (KS) multimedia framework. It implements a rendezvous mechanism that allows user-mode multimedia pipelines to set up shared-memory streaming channels between a context (producer/consumer endpoint) and a stream (data transport). The driver exposes a device object accessible to any authenticated user via standard `CreateFile` + `DeviceIoControl` calls.

The rendezvous server maintains two separate object lists — one for "context" registrations (`FSContextReg`, type 1, 0x78 bytes) and one for "stream" registrations (`FSStreamReg`, type 2, 0x1D8 bytes). Both object types inherit from a common `FSRegObject` base with a type field at offset `+0x30`, a size field at `+0x34`, and a self-pointer at `+0x20`. Both types are stored in the file object's `FsContext2` field and looked up by `FindObject` on handle close.

---

## Vulnerability Summary

`FSRendezvousServer::FindObject` uses the type field at `*(int*)(param_1 + 0x30)` to decide which object list to search — the context list (type == 1) or the stream list (type != 1, including type == 2). However, it performs **no validation** that the object found in the list actually matches the type that the caller expects. This allows a type confusion between `FSContextReg` (0x78 bytes, type 1) and `FSStreamReg` (0x1D8 bytes, type 2).

When `FSRendezvousServer::Close` is called on handle cleanup, it reads the object pointer from `FsContext2 + 0x20`, calls `FindObject` to "validate" it, then dispatches virtual methods via `__guard_dispatch_icall_fptr` on the returned object. If the object stored in `FsContext2` is a `FSContextReg` (0x78 bytes) but `FindObject` returns it for a stream-type lookup, the virtual dispatch reads the vtable pointer from offset 0x00 of the small object and calls methods that access fields at offsets beyond 0x78 — causing an **out-of-bounds read/write** on adjacent pool memory.

---

## Prerequisites and Constraints

- Local authenticated session (standard user; no administrative rights required)
- `mskssrv.sys` must be loaded — it is auto-loaded when any process opens the KS proxy device
- The device `\Device\MSKSSRV` (or via the KS proxy interface) is accessible to standard users
- Attacker must be able to make two `CreateFile` calls to the device and issue IOCTLs
- The type confusion gives a kernel pool OOB primitive; weaponization requires heap shaping
- Exploited in the wild prior to the September 2023 patch

---

## Vulnerability Details

### Call Chain (Ghidra MCP–Verified)

The complete kernel call chain from userspace to the vulnerable function, verified via live Ghidra MCP decompilation against the **pre-patch** binary (`mskssrv.sys`, 10.0.22621.1848):

```
User mode:
  CreateFile("\\\\.\\MSKSSRV", ...)     → opens KS proxy device, triggers SrvDispatchCreate
  DeviceIoControl(h1, 0x2f0400, ...)   → IOCTL: FSInitializeContextRendezvous
                                          → allocates FSContextReg (0x78 bytes, type 1)
                                          → stores in FsContext2+0x20
  DeviceIoControl(h2, 0x2f0404, ...)   → IOCTL: InitializeStream
                                          → allocates FSStreamReg (0x1D8 bytes, type 2)
                                          → stores in FsContext2+0x20
  CloseHandle(h1)                       → triggers Close → FindObject type confusion
                                          ↓
Kernel mode:
  SrvDispatchClose()
    → FSRendezvousServer::Close()       [reads FsContext2+0x20 object pointer]
      → FSRendezvousServer::FindObject() [*** VULNERABLE — no type validation ***]
        → dispatches virtual methods on wrong-type object → OOB access
```

### FSRendezvousServer Layout (Singleton, ~0xA0 bytes)

```
+0x00: refcount
+0x08: KMUTEX (0x38 bytes)
+0x40: FSRegObjectList vtable (stream list)
+0x48: FSList vtable
+0x50: stream list head (circular doubly-linked)
+0x60: stream list iterator
+0x68: stream list count
+0x70: FSRegObjectList vtable (context list)
+0x78: FSList vtable
+0x80: context list head (circular doubly-linked)
+0x90: context list iterator
+0x98: context list count
```

### Object Layouts

**FSContextReg (type 1, 0x78 bytes, pool tag 'Creg'):**
```
+0x00: vtable (FSContextReg::_vftable_)
+0x08: list link (Flink)
+0x10: list link (Blink)
+0x18: flags
+0x20: self-pointer (points to this object)
+0x28: ...
+0x30: type = 1
+0x34: size = 0x78
+0x38–0x77: context-specific fields
```

**FSStreamReg (type 2, 0x1D8 bytes, pool tag 'Sreg'):**
```
+0x00: vtable (FSStreamReg::_vftable_)
+0x08: list link (Flink)
+0x10: list link (Blink)
+0x18: flags
+0x20: self-pointer (points to this object)
+0x28: ...
+0x30: type = 2
+0x34: size = 0x1D8
+0x38–0x57: stream header fields
+0x58–0x97: additional stream state
+0x98–0xC7: FSFrameMdlList[0] (at +0xC8)
+0xC8–0x13F: FSFrameMdlList[0]
+0x140–0x1B7: FSFrameMdlList[1]
+0x1B8–0x1D7: trailing fields
```

### FindObject (Pre-Patch — VULNERABLE)

The pre-patch `FindObject` at `0x1c0008f98` (Ghidra decompilation from `mskssrv.sys` 10.0.22621.1848):

```c
// Pre-patch FindObject @ 0x1c0008f98
FSRegObject * FSRendezvousServer::FindObject(
        FSRendezvousServer *this, FSRegObject *param_1)
{
    longlong list_head, current;

    // Type-based list selection — NO VALIDATION of found object's type
    if (*(int *)(param_1 + 0x30) == 1) {
        // type == 1: search context list (this+0x80)
        list_head = *(longlong *)(this + 0x80);
        current = list_head;
        while (current != (longlong)(this + 0x80)) {
            // Match by self-pointer at +0x20
            if (*(longlong *)(current + 0x20) == (longlong)param_1) {
                return (FSRegObject *)current;  // Found — but no type check!
            }
            current = *(longlong *)(current + 8);  // next link
        }
    } else {
        // type != 1 (includes type 2): search stream list (this+0x50)
        list_head = *(longlong *)(this + 0x50);
        current = list_head;
        while (current != (longlong)(this + 0x50)) {
            if (*(longlong *)(current + 0x20) == (longlong)param_1) {
                return (FSRegObject *)current;  // Found — but no type check!
            }
            current = *(longlong *)(current + 8);
        }
    }
    return NULL;  // Not found
}
```

**The bug:** `FindObject` reads `*(int*)(param_1 + 0x30)` to choose which list to search, but `param_1` is the object pointer from `FsContext2 + 0x20` — it could be *either* type. The function searches the corresponding list and returns any object whose self-pointer (`+0x20`) matches `param_1`. It never verifies that the returned object's type field matches the expected type for the operation being performed.

### Close (Triggers the Bug)

```c
// FSRendezvousServer::Close @ 0x1c000894c (simplified)
void FSRendezvousServer::Close(FSRendezvousServer *this, longlong fileObj) {
    longlong fsContext2 = *(longlong *)(fileObj + 0x30);

    // Read object pointer from FsContext2+0x20
    FSRegObject *obj = *(FSRegObject **)(fsContext2 + 0x20);

    if (obj != NULL) {
        // "Validate" via FindObject — but FindObject has the type confusion bug
        FSRegObject *found = FindObject(this, obj);

        if (found != NULL) {
            // Dispatch virtual method via CFG-protected indirect call
            // Reads vtable from found+0x00, calls method that accesses
            // fields at offsets valid for FSStreamReg (0x1D8) but NOT
            // for FSContextReg (0x78) → OOB if wrong type
            (*__guard_dispatch_icall_fptr)(*(void **)(*(longlong *)found));
        }
    }
}
```

### SrvDispatchIoControl — IOCTL Dispatch Table

```c
// SrvDispatchIoControl @ 0x1c0008520 — IOCTL routing
switch (ioctl_code) {
    case 0x2f0400: FSInitializeContextRendezvous();  // creates FSContextReg (type 1)
    case 0x2f0404: InitializeStream();                // creates FSStreamReg (type 2)
    case 0x2f0408: PublishTx();
    case 0x2f040c: PublishRx();
    case 0x2f0410: ConsumeTx();
    case 0x2f0414: ConsumeRx();
    case 0x2f0418: NotifyContext();
    case 0x2f041c: RegisterContext();                 // looks up context by PID+key
    case 0x2f0420: RegisterStream();                  // looks up stream by PID+key+flags
    case 0x2f0424: DrainTx();
}
```

### InitializeContext (Allocates FSContextReg)

```c
// Via FSInitializeContextRendezvous → allocates FSContextReg
void InitializeContext(FSRendezvousServer *server, longlong fileObj) {
    // Allocate 0x78 bytes from PagedPool, tag 'Creg'
    FSContextReg *obj = ExAllocatePoolWithTag(PagedPool, 0x78, 'Creg');
    memset(obj, 0, 0x78);

    *(void ***)(obj + 0x00) = &FSContextReg::_vftable_;
    *(FSRegObject **)(obj + 0x20) = obj;           // self-pointer
    *(int *)(obj + 0x30) = 1;                       // type = 1
    *(int *)(obj + 0x34) = 0x78;                    // size = 0x78

    // Insert into context list at server+0x70
    InsertTailList(&server->contextListHead, &obj->listEntry);

    // Store in FsContext2+0x20
    *(FSRegObject **)(*(longlong *)(fileObj + 0x30) + 0x20) = obj;
}
```

### InitializeStream (Allocates FSStreamReg)

```c
// Via IOCTL 0x2f0404 → allocates FSStreamReg
void InitializeStream(FSRendezvousServer *server, longlong fileObj) {
    // Allocate 0x1D8 bytes from PagedPool, tag 'Sreg'
    FSStreamReg *obj = ExAllocatePoolWithTag(PagedPool, 0x1D8, 'Sreg');

    // Constructor sets type=2, size=0x1D8, vtable, self-pointer, inits MDL lists
    FSStreamReg::FSStreamReg(obj);

    // Insert into stream list at server+0x40
    InsertTailList(&server->streamListHead, &obj->listEntry);

    // Store in FsContext2+0x20
    *(FSRegObject **)(*(longlong *)(fileObj + 0x30) + 0x20) = obj;
}
```

---

## Exploitation Scenario

### Step 1 — Open Two Handles to the KS Proxy Device

```
h1 = CreateFile("\\\\.\\MSKSSRV", GENERIC_READ|GENERIC_WRITE, ...)
h2 = CreateFile("\\\\.\\MSKSSRV", GENERIC_READ|GENERIC_WRITE, ...)
```

Both handles get independent `FsContext2` structures via `SrvDispatchCreate`.

### Step 2 — Create a Context Registration on h1

```
DeviceIoControl(h1, 0x2f0400, input, ...)  // FSInitializeContextRendezvous
```

This allocates a `FSContextReg` object (0x78 bytes, type 1) and stores it in h1's `FsContext2 + 0x20`. The object is inserted into the **context list** at `server + 0x80`.

### Step 3 — Create a Stream Registration on h2

```
DeviceIoControl(h2, 0x2f0404, input, ...)  // InitializeStream
```

This allocates a `FSStreamReg` object (0x1D8 bytes, type 2) and stores it in h2's `FsContext2 + 0x20`. The object is inserted into the **stream list** at `server + 0x50`.

### Step 4 — Manipulate FsContext2 to Point to Wrong-Type Object

Using `RegisterContext` (IOCTL `0x2f041c`) or `RegisterStream` (IOCTL `0x2f0420`), the attacker can re-register h2's `FsContext2 + 0x20` to point to the `FSContextReg` object created in Step 2, or vice versa. These IOCTLs search the object lists by PID + key and overwrite `FsContext2 + 0x20` with whatever object they find.

### Step 5 — Close Handle to Trigger Type Confusion

```
CloseHandle(h2);  // triggers SrvDispatchClose → Close → FindObject
```

`Close` reads the object from h2's `FsContext2 + 0x20`. If this now points to a `FSContextReg` (0x78 bytes) but `Close` expects a `FSStreamReg` (0x1D8 bytes), the virtual dispatch reads method pointers from the FSContextReg vtable and calls methods that access fields at offsets `+0x98`, `+0xC8`, `+0x140` — all beyond the 0x78-byte allocation. This gives **controlled out-of-bounds read/write** on adjacent pool memory.

### Step 6 — Weaponize the OOB Primitive

With pool grooming (spray 0x78-byte allocations to control adjacent memory), the attacker can:
- Read kernel pointers from adjacent pool allocations (info leak)
- Corrupt adjacent pool objects to achieve arbitrary read/write
- Overwrite a `_TOKEN` or `_SEP_TOKEN_PRIVILEGES` to escalate to SYSTEM
- Chain with `NtQuerySystemInformation` for KASLR bypass

### Impact

Full local privilege escalation from any authenticated user to SYSTEM. Exploited in the wild before the September 2023 patch.

---

## Patch Analysis

### Mechanism

The patch applies two changes:

1. **Rename `FindObject` → `FindStreamObject`** and add a type check: the post-patch function only searches the stream list and requires `*(int*)(param_1 + 0x30) == 2`. If the type is not 2 (stream), it immediately returns NULL.

2. **Inline context-list search in `Close`**: for type-1 objects (contexts), `Close` now performs its own inline search of the context list directly, without calling `FindStreamObject`. This separates the two code paths entirely.

### Post-Patch FindStreamObject

```c
// Post-patch: FindStreamObject (renamed from FindObject)
FSRegObject * FSRendezvousServer::FindStreamObject(
        FSRendezvousServer *this, FSRegObject *param_1)
{
    // *** NEW: type validation — only handles stream objects ***
    if (*(int *)(param_1 + 0x30) != 2) {
        return NULL;  // Reject non-stream objects
    }

    // Search stream list only
    longlong current = *(longlong *)(this + 0x50);
    while (current != (longlong)(this + 0x50)) {
        if (*(longlong *)(current + 0x20) == (longlong)param_1) {
            return (FSRegObject *)current;
        }
        current = *(longlong *)(current + 8);
    }
    return NULL;
}
```

### Post-Patch Close

```c
// Post-patch Close — separate paths for context vs stream
void FSRendezvousServer::Close(FSRendezvousServer *this, longlong fileObj) {
    FSRegObject *obj = *(FSRegObject **)(*(longlong *)(fileObj + 0x30) + 0x20);

    if (obj != NULL) {
        if (*(int *)(obj + 0x30) == 1) {
            // Type 1 (context): inline search of context list — never calls FindStreamObject
            longlong current = *(longlong *)(this + 0x80);
            while (current != (longlong)(this + 0x80)) {
                if (*(longlong *)(current + 0x20) == (longlong)obj) {
                    // Found in context list — dispatch context cleanup
                    break;
                }
                current = *(longlong *)(current + 8);
            }
        } else {
            // Type 2+ (stream): use FindStreamObject with type==2 check
            FSRegObject *found = FindStreamObject(this, obj);
            if (found != NULL) {
                // Dispatch stream cleanup
            }
        }
    }
}
```

### Files Changed

| Function | Change |
|---|---|
| `FSRendezvousServer::FindObject` | Renamed to `FindStreamObject`; `type==2` check added; context-list search path removed |
| `FSRendezvousServer::Close` | Context-list search (type==1) inlined; now calls `FindStreamObject` instead of `FindObject` |

---

## Trigger PoC

A proof-of-concept is available at [`poc/poc_cve_2023_36802.c`](/data/patch_diffs/poc/poc_cve_2023_36802.c). It demonstrates the type confusion by:

1. Opening two handles to the MSKSSRV device
2. Creating a `FSContextReg` (type 1, 0x78 bytes) on handle 1 via IOCTL `0x2f0400`
3. Creating a `FSStreamReg` (type 2, 0x1D8 bytes) on handle 2 via IOCTL `0x2f0404`
4. Using `RegisterContext`/`RegisterStream` IOCTLs to cross-link the FsContext2 pointers
5. Closing a handle to trigger `Close → FindObject` on the wrong-type object

On a **pre-patch** system, this will cause a kernel BugCheck (BSOD) due to the OOB access — this confirms the type confusion primitive is reachable. On a **post-patch** system, `FindStreamObject` returns NULL for type-1 objects and the close path handles each type separately.

> **Note:** This PoC is a **trigger/detector** only. It demonstrates reachability of the type confusion but does not include heap grooming or token manipulation for full EoP. It is designed for blue teams to validate detection rules.

---

## Detection Rules

### YARA Rule — Detecting PoC Binaries

```yara
rule CVE_2023_36802_MSKSSRV_TypeConfusion_PoC {
    meta:
        description = "Detects compiled PoC for CVE-2023-36802 mskssrv.sys type confusion"
        author = "OnlyFm252"
        date = "2026-07-17"
        cve = "CVE-2023-36802"
        severity = "critical"
        tlp = "white"

    strings:
        $dev1 = "\\\\.\\MSKSSRV" ascii wide
        $dev2 = "\\Device\\MSKSSRV" ascii wide
        $ioctl_ctx = { 00 04 2f 00 }          // 0x002f0400 (InitializeContextRendezvous)
        $ioctl_stream = { 04 04 2f 00 }       // 0x002f0404 (InitializeStream)
        $ioctl_regctx = { 1c 04 2f 00 }       // 0x002f041c (RegisterContext)
        $ioctl_regstr = { 20 04 2f 00 }       // 0x002f0420 (RegisterStream)
        $api1 = "DeviceIoControl" ascii wide
        $api2 = "CreateFileW" ascii wide
        $api3 = "CreateFileA" ascii wide
        $msg1 = "type confusion" ascii wide nocase
        $msg2 = "mskssrv" ascii wide nocase

    strings:
        $create = "CreateFile" ascii wide

    condition:
        uint16(0) == 0x5A4D and
        filesize < 500KB and
        ($dev1 or $dev2) and
        ($ioctl_ctx or $ioctl_stream) and
        ($ioctl_regctx or $ioctl_regstr) and
        ($api1 or $api2 or $api3 or $create)
}
```

### YARA Rule — Detecting Vulnerable mskssrv.sys (Pre-Patch)

```yara
rule CVE_2023_36802_Vulnerable_MSKSSRV {
    meta:
        description = "Detects pre-patch mskssrv.sys vulnerable to CVE-2023-36802 type confusion"
        author = "OnlyFm252"
        date = "2026-07-17"
        cve = "CVE-2023-36802"

    strings:
        $driver_name = "mskssrv.sys" wide nocase
        $find_object = "FindObject" ascii
        $not_find_stream = "FindStreamObject" ascii

    condition:
        uint16(0) == 0x5A4D and
        $driver_name and
        $find_object and
        not $not_find_stream
}
```

### Sigma Rule — MSKSSRV Device Access

```yaml
title: Suspicious MSKSSRV Device Access (CVE-2023-36802)
id: c4d2e5f6-1a2b-3c4d-5e6f-7a8b9c0d1e2f
status: experimental
description: >
    Detects processes accessing the MSKSSRV Kernel Streaming proxy device
    outside of expected multimedia applications. The device is used by the
    Kernel Streaming framework and is rarely accessed directly by user
    applications. Direct access may indicate exploitation of CVE-2023-36802.
author: OnlyFm252
date: 2026/07/17
references:
    - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36802
    - https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-36802.html
    - https://onlyfm252.starlabs.sg/patch-tuesday/2023-09/cve-2023-36802/
logsource:
    category: process_creation
    product: windows
detection:
    selection_device:
        CommandLine|contains:
            - 'MSKSSRV'
            - 'mskssrv'
    filter_legitimate:
        Image|endswith:
            - '\svchost.exe'
            - '\audiodg.exe'
            - '\mstsc.exe'
            - '\teams.exe'
            - '\zoom.exe'
            - '\obs64.exe'
    condition: selection_device and not filter_legitimate
falsepositives:
    - Legitimate multimedia applications using Kernel Streaming
    - Remote Desktop audio redirection
    - Video capture/streaming software
level: high
tags:
    - attack.privilege_escalation
    - attack.t1068
    - cve.2023.36802
```

### Sigma Rule — Kernel Streaming IOCTL Sequence

```yaml
title: Kernel Streaming Type Confusion IOCTL Sequence (CVE-2023-36802)
id: d5e3f6a7-2b3c-4d5e-6f7a-8b9c0d1e2f3a
status: experimental
description: >
    Detects the specific IOCTL sequence used to trigger CVE-2023-36802:
    opening the MSKSSRV device followed by both InitializeContext (0x2f0400)
    and InitializeStream (0x2f0404) IOCTLs from the same process, then a
    Register IOCTL. This sequence creates both object types needed for the
    type confusion.
author: OnlyFm252
date: 2026/07/17
logsource:
    category: driver_load
    product: windows
detection:
    selection_driver:
        ImageLoaded|endswith: '\mskssrv.sys'
    selection_suspicious:
        Image|endswith:
            - '.exe'
        Image|contains:
            - '\Users\'
            - '\Temp\'
            - '\AppData\'
    condition: selection_driver and selection_suspicious
falsepositives:
    - Legitimate KS proxy usage from user-installed multimedia software
level: medium
tags:
    - attack.privilege_escalation
    - attack.t1068
    - cve.2023.36802
```

### Sysmon Configuration

```xml
<!-- Sysmon config addition for CVE-2023-36802 detection -->
<RuleGroup name="CVE-2023-36802" groupRelation="or">

  <!-- DriverLoad: mskssrv.sys loaded by non-system process -->
  <DriverLoad onmatch="include">
    <Rule name="MSKSSRV_DriverLoad" groupRelation="and">
      <ImageLoaded condition="end with">mskssrv.sys</ImageLoaded>
    </Rule>
  </DriverLoad>

  <!-- FileCreate: Detect creation of KS proxy handle artifacts -->
  <FileCreate onmatch="include">
    <Rule name="KS_Handle_Create" groupRelation="and">
      <TargetFilename condition="contains">MSKSSRV</TargetFilename>
    </Rule>
  </FileCreate>

  <!-- ProcessAccess: Unusual process accessing mskssrv device -->
  <ProcessAccess onmatch="include">
    <Rule name="MSKSSRV_Access" groupRelation="and">
      <CallTrace condition="contains">mskssrv</CallTrace>
      <SourceImage condition="excludes">C:\Windows\System32\</SourceImage>
      <SourceImage condition="excludes">C:\Program Files\</SourceImage>
    </Rule>
  </ProcessAccess>

  <!-- ImageLoad: KS-related DLLs loaded by suspicious process -->
  <ImageLoad onmatch="include">
    <Rule name="KS_DLL_Suspicious" groupRelation="and">
      <ImageLoaded condition="end with">ksuser.dll</ImageLoaded>
      <Image condition="excludes">C:\Windows\</Image>
      <Image condition="excludes">C:\Program Files\</Image>
      <Image condition="excludes">C:\Program Files (x86)\</Image>
    </Rule>
  </ImageLoad>

</RuleGroup>
```

---

## Remediation

1. **Apply KB5030219** (September 2023 cumulative update) immediately — this CVE was exploited in the wild
2. **Verify patch**: Check `mskssrv.sys` version is ≥ 10.0.22621.2283
3. **Monitor**: Deploy the Sysmon and Sigma rules above to detect exploitation attempts
4. **Restrict**: Consider blocking access to `\Device\MSKSSRV` via a WDAC or device guard policy in environments that don't require Kernel Streaming
5. **Audit**: Review systems for unusual processes loading `mskssrv.sys` or `ksuser.dll`
6. **Hunt**: Search for evidence of pool spray patterns (large numbers of small kernel allocations from user-mode)

---

## Timeline

| Date | Event |
|---|---|
| 2023-09-12 | Microsoft releases September 2023 Patch Tuesday (KB5030219), marks as "Exploitation Detected" |
| 2023-09-12 | Google Project Zero / TAG credits discovery to internal threat intel |
| 2026-07-17 | This Ghidra MCP–verified analysis published |

---

## References

- [Microsoft Security Response Center — CVE-2023-36802](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36802)
- [Google Project Zero — CVE-2023-36802 RCA](https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-36802.html)
- [IBM X-Force — Critically Close to Zero-Day: Exploiting Microsoft Kernel Streaming Service](https://securityintelligence.com/x-force/critically-close-to-zero-day-exploiting-microsoft-kernel-streaming-service/)
- [OnlyFm252 Diff Report](/data/patch_diffs/mskssrv_sys-kb5030219.md)

---

<sub>Analysis: OnlyFm252 — Ghidra MCP–verified call chain tracing against pre-patch mskssrv.sys 10.0.22621.1848.
Binary diff: ghidriff of mskssrv.sys (10.0.22621.1848, pre-patch) vs mskssrv.sys (10.0.22621.2283, post-patch).
[Download pre-patch](/data/patch_diffs/binaries/mskssrv-2023-08.sys) / [Download post-patch](/data/patch_diffs/binaries/mskssrv-2023-09.sys).</sub>
