# CVE-2026-33098 — Windows Container Isolation FS `wcifs.sys` Reference-Count Underflow in `WcScheduleExpansionWorkItem` → Use-After-Free

---

## Summary

| | |
|---|---|
| **Product** | Windows — `wcifs.sys` (Windows Container Isolation File System minifilter) |
| **CVE ID** | CVE-2026-33098 |
| **Impact** | Elevation of Privilege |
| **MSRC severity** | Important |
| **CVSS** | 7.8 / 6.8 — `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-416: Use After Free (reference-count underflow via cleanup asymmetry) |
| **Delivery** | Local — force allocation failures under memory pressure to underflow a refcount |
| **KB / Fixed build** | KB5083769 — `wcifs.sys` 10.0.26100.8246 (Win11 24H2 x64) |
| **Patch Date** | April 14, 2026 (2026-Apr) |
| **Pre-patch binary** | `wcifs.sys` 10.0.26100.7920 — SHA256 `586e4753c651381ee945a92c3b7b3ce044515f2b19daabfdad6c827f8eadcb5e` |
| **Post-patch binary** | `wcifs.sys` 10.0.26100.8246 — SHA256 `b17e6ee478e8614fb7dca82410f88b7d97e9acaa1ea059cc4a3aaa370206a19a` |
| **Feature flag** | `Feature_1773654331` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`wcifs.sys` is the Windows **Container Isolation** file-system minifilter (used by
containers / app isolation). `WcScheduleExpansionWorkItem` asynchronously enqueues
"expansion" work items: it allocates a work item, takes a reference on the target
object, and queues the work.

---

## Vulnerability Summary

`WcScheduleExpansionWorkItem` has two failure points that shared **one** cleanup
block (`LABEL_10`/`LABEL_14`), which **unconditionally** called
`ObfDereferenceObject`:

1. `FltAllocateGenericWorkItem` — if this **allocation fails**, the code `goto`s
   the shared cleanup **before `ObfReferenceObject` was ever called**.
2. `FltQueueGenericWorkItem` — if the **queue insert fails**, `ObfReferenceObject`
   *has* already run, so the cleanup dereference is correctly symmetric.

```c
// WcScheduleExpansionWorkItem (10.0.26100.7920) — PRE-PATCH, from our diff
GenericWorkItem = FltAllocateGenericWorkItem();
if (!GenericWorkItem) { v4 = 0xC000009A; goto LABEL_10; }   // *** alloc fail: no ref taken yet ***
ObfReferenceObject(Object);                                 // ref +1 (only reached on alloc success)
v4 = FltQueueGenericWorkItem(...);
if (v4 < 0) FltFreeGenericWorkItem(GenericWorkItem);
LABEL_10:
LABEL_14:
    _InterlockedDecrement((int *)(a3 + 32));
    ObfDereferenceObject(Object);                           // *** unconditional deref ***
```

On the **allocation-failure** path the object is dereferenced **without a matching
reference having been taken**, so its refcount ends up **one lower than it should
be**. In a container doing heavy file I/O, an attacker can deliberately exhaust
**NonPagedPoolNx** to make the allocation fail repeatedly; each failure drops the
count by one until it reaches **0 while other paths still hold pointers**, freeing
the object. Subsequent use through those pointers is a kernel **use-after-free**
(CWE-416) → EoP.

---

## Prerequisites and Constraints

- Local, low-privileged (`PR:L`, `AV:L`, `AC:L`), typically inside a container /
  isolation context that reaches the expansion path.
- Drive memory pressure on NonPagedPoolNx so `FltAllocateGenericWorkItem` fails
  repeatedly; each failure underflows the target object's refcount by one.
- Once the count hits zero with live pointers outstanding → free → UAF.

---

## Vulnerability Details

### Root Cause

Two error paths with **different resource-acquisition states** (reference taken vs
not) merged into one cleanup block via `goto`, so the shared unconditional
`ObfDereferenceObject` over-releases on the allocation-failure path — a
cleanup-asymmetry reference-count underflow.

### The patch (confirmed — diff, .7920 → .8246)

Gated behind `Feature_1773654331`, the fix introduces a **boolean flag** (init
`false`) that is set **true only immediately after `ObfReferenceObject`**, and the
shared-cleanup `ObfDereferenceObject` is now guarded so it only runs when a
reference was actually taken:

```c
// WcScheduleExpansionWorkItem (10.0.26100.8246) — PATCHED, feature-enabled branch
bVar13 = false;                                             // ref-taken flag (reference's v6)
...
ObfReferenceObject(Object);
uVar11 = Feature_1773654331__private_IsEnabledDeviceUsage();
bVar13 = (int)uVar11 != 0;                                  // *** set true only after the ref ***
...
LABEL_14:
    _InterlockedDecrement((int *)(a3 + 32));
    if (!Feature_1773654331__private_IsEnabledDeviceUsage() || bVar13)   // *** deref only if ref taken ***
        ObfDereferenceObject(Object);
```

So on allocation failure (`bVar13 == false`) the dereference is skipped, keeping
the refcount balanced. The control flow was also restructured from the
`goto`-merge into an `if/else` with an **early return on success**, so the same
asymmetry cannot easily recur. Our diff of `wcifs.sys` 10.0.26100.7920 → .8246
confirms `WcScheduleExpansionWorkItem` is the changed function with the added
`Feature_1773654331`-gated flag/guard.

### Patch Completeness Assessment

**CFR-gated behind `Feature_1773654331`.** The guarded dereference runs only when
the flag is enabled; the original unconditional deref still ships when disabled.
Verify `Feature_1773654331` is enabled to confirm the fix is live.

---

## Detection Guidance

**Behavioural.** Container/isolation workloads driving NonPagedPoolNx exhaustion
during file expansion; refcount-underflow / UAF bugchecks in
`wcifs!WcScheduleExpansionWorkItem` and the target object teardown on
unpatched/flag-disabled builds.

**Config.** The fix is CFR-gated — confirm `Feature_1773654331` is enabled.

---

## References

- MSRC advisory — CVE-2026-33098 (Windows Container Isolation FS Filter Driver Elevation of Privilege), released 2026-04-14, KB5083769.
- Full binary diff: `/data/patch_diffs/wcifs_sys-cve-2026-33098-ghidriff.md`
