# CVE-2025-49693 — Microsoft Brokering File System `bfs.sys` Double Free of `pUserId`/`pContainerId` on the `BfsInsertPolicyEntry` Failure Path

---

## Summary

| | |
|---|---|
| **Product** | Windows — `bfs.sys` (Microsoft Brokering File System; policy store) |
| **CVE ID** | CVE-2025-49693 |
| **Impact** | Elevation of Privilege (to SYSTEM) |
| **MSRC severity** | Important |
| **CVSS** | 7.0 / 6.1 — `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-415: Double Free (→ pool corruption) |
| **Delivery** | Local — a policy `SetPolicy` request over `\Device\Bfs` IOCTL `0x228004` whose storage setup fails |
| **KB / Fixed build** | KB5062553 — `bfs.sys` 10.0.26100.4652 (Win11 24H2 x64) |
| **Patch Date** | July 8, 2025 (2025-Jul) |
| **Pre-patch binary** | `bfs.sys` 10.0.26100.4484 — SHA256 `0fb28e775b40c35f96ab4cb22df9c5ea5873fbaff012fa2268a253aa94361626` |
| **Post-patch binary** | `bfs.sys` 10.0.26100.4652 — SHA256 `678c670cb8ba6b2a02260ccb27540f5894d5668b411404d07c16a262ab074411` |
| **Feature flag** | `Feature_3148938554` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`bfs.sys` is the Microsoft Brokering File System driver. A user process reaches it
via `\Device\Bfs` `NtDeviceIoControlFile` (the only IOCTL is `0x228004`,
`BfsDeviceIoControl`). Handling a set-policy request, `BfsInsertPolicyEntry`
allocates a policy entry (tag `EsfB`) plus a `pUserId` and `pContainerId` (tag
`SsfB`), reference-counts the entry, inserts it into the policy hash table, and
provisions on-disk storage (`BfsOpenPolicyDirectory` / `BfsCreateStorage`).

---

## Vulnerability Summary

On the storage-provisioning **failure** path, `pUserId`/`pContainerId` were freed
**twice**:

```c
// BfsInsertPolicyEntry — from the reporter analysis (pre-patch)
pUserId      = ExAllocatePool2('SsfB');
pContainerId = ExAllocatePool2('SsfB');
NewPolicyEntry = ExAllocatePool2('EsfB');
NewPolicyEntry->pUserId      = pUserId;         // ownership transferred to the entry
NewPolicyEntry->pContainerId = pContainerId;
_InterlockedIncrement(&NewPolicyEntry->RefNo);  // RefNo = 1
BfsInsertEntryHashTable(&NewPolicyEntry->TableEntry);
_InterlockedIncrement(&NewPolicyEntry->RefNo);  // RefNo = 2

if (BfsOpenPolicyDirectory() != 0 || BfsCreateStorage() != 0) {   // *** failure ***
    BfsDereferencePolicyEntryEx(NewPolicyEntry);   // RefNo 2 -> 1
    if (bNewInsertedEntry)
        BfsDereferencePolicyEntryEx(NewPolicyEntry);   // RefNo 1 -> 0: frees pUserId, pContainerId, and the entry
    if (pUserId)      ExFreePoolWithTag(pUserId, 0);       // *** double free ***
    if (pContainerId) ExFreePoolWithTag(pContainerId, 0);  // *** double free ***
}
```

When `BfsCreateStorage` fails (e.g. a pool allocation inside it fails), the second
`BfsDereferencePolicyEntryEx` drops `RefNo` to 0, so it frees
`PolicyEntry->pUserId`, `PolicyEntry->pContainerId` and the entry. The failure path
**then also** calls `ExFreePoolWithTag(pUserId)` / `ExFreePoolWithTag(pContainerId)`
on the same (now-freed) buffers — a **double free** that corrupts the pool
(CWE-415). Because BFS runs in kernel and the SetPolicy request is reachable
locally, the resulting pool corruption is an EoP-to-SYSTEM primitive.

---

## Prerequisites and Constraints

- Local (`PR:L`, `AV:L`); `AC:H` — the attacker must make `BfsCreateStorage` /
  `BfsOpenPolicyDirectory` fail (e.g. induce a pool-allocation failure) on a new
  policy-entry insert.
- The BFS interface requires an **AppSilo**-token process to pass
  `BfsIsApplicableToken`; the reporter reached it via an AppContainer/AppSilo.
- Result: `pUserId`/`pContainerId` freed twice → kernel pool corruption.

---

## Vulnerability Details

### Root Cause

Ownership of `pUserId`/`pContainerId` is transferred to the policy entry (which
frees them when its refcount reaches zero), but the failure path also frees the
original local pointers, so a `BfsCreateStorage` failure frees the same buffers
twice.

### The patch (confirmed — diff, .4484 → .4652)

Gated behind `Feature_3148938554`, `BfsInsertPolicyEntry` **nulls the local
`pUserId`/`pContainerId` pointers once ownership is transferred to the policy
entry**, so the failure-path `ExFreePoolWithTag(pUserId/pContainerId)` is skipped
(the pointers are now NULL) — leaving only the entry's dereference to free them:

```c
// BfsInsertPolicyEntry (10.0.26100.4652) — PATCHED, feature-enabled branch (from our diff)
NewPolicyEntry->pUserId = pUserId;
if (Feature_3148938554__private_IsEnabledDeviceUsage()) pUserId = 0;        // *** null local copy ***
NewPolicyEntry->pContainerId = pContainerId;
if (Feature_3148938554__private_IsEnabledDeviceUsage()) pContainerId = 0;   // *** null local copy ***
...
// failure path: if (pUserId) ExFreePoolWithTag(...) now sees NULL -> no double free
```

With the local copies zeroed after ownership transfer, only
`BfsDereferencePolicyEntryEx` frees them (once), closing the double free. The July
2025 update reworks the surrounding policy-entry reference lifecycle
(`BfsInsertPolicyEntry`, `BfsDereferencePolicyEntryEx`, `BfsCheckAndApplyPolicy`,
`BfsInsertNotPresentPolicyEntry`) under the same flag; the related UAF in this path
is CVE-2025-49677.

### Patch Completeness Assessment

**CFR-gated behind `Feature_3148938554`.** The null-after-transfer runs only when
the flag is enabled; the original double-free path still ships when disabled.
Verify `Feature_3148938554` is enabled to confirm the fix is live.

---

## Detection Guidance

**Behavioural.** BFS `SetPolicy` requests (IOCTL `0x228004`) from AppSilo/AppContainer
processes that fail storage provisioning; double-free / pool-corruption bugchecks
(`BfsDereferencePolicyEntryEx` → `ExFreePoolWithTag`, tag `SsfB`/`EsfB`) on
unpatched/flag-disabled builds.

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

---

## References

- MSRC advisory — CVE-2025-49693 (Microsoft Brokering File System Elevation of Privilege), released 2025-07-08, KB5062553.
- Full binary diff: `/data/patch_diffs/bfs_sys-cve-2025-49677-49693-ghidriff.md`
- Related same-KB BFS policy-entry UAF: CVE-2025-49677.
