# CVE-2025-49677 — Microsoft Brokering File System `bfs.sys` Policy-Entry Use-After-Free in the SetPolicy Path

---

## Summary

| | |
|---|---|
| **Product** | Windows — `bfs.sys` (Microsoft Brokering File System; policy store) |
| **CVE ID** | CVE-2025-49677 |
| **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-416: Use After Free (policy-entry reference lifecycle) |
| **Delivery** | Local — a `SetPolicy` request over `\Device\Bfs` IOCTL `0x228004` (race) |
| **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` (Microsoft Brokering File System) manages per-user / per-container policy
entries in a hash table (`RTL_DYNAMIC_HASH_TABLE`), each entry allocated with tag
`EsfB` and **reference-counted** (`RefNo`). The policy store is driven from
`\Device\Bfs` IOCTL `0x228004` (`BfsDeviceIoControl` → `BfsProcessSetPolicyRequest`
→ `BfsInsertPolicyEntry` / `BfsGetPolicyEntry` / `BfsDereferencePolicyEntryEx`), and
the entries back a config file under
`\SystemRoot\System32\config\BFS\{usersid}\{containerid}`.

---

## Vulnerability Summary

The policy-entry **reference lifecycle** was mishandled, so an entry (the `EsfB`
pool block) could be **freed while another code path still held / looked it up** —
a use-after-free. The reporter observed a steadily reproducible bugcheck whose
freed block was the `EsfB` policy-entry pool, freed via
`BfsInsertPolicyEntry` → `ExFreePoolWithTag` and then still referenced when a
subsequent `BfsProcessSetPolicyRequest` / `BfsGetPolicyEntry` walked the policy
hash table:

```
nt!ExFreePoolWithTag
bfs!BfsInsertPolicyEntry        // frees the EsfB policy entry (dropped refcount)
bfs!BfsGetPolicyEntry           // later lookup finds/uses the freed entry
bfs!BfsProcessSetPolicyRequest
bfs!BfsDeviceIoControl
```

Because the hash-table slot / entry lifetime was not consistently synchronized with
the `RefNo` reference counting, a concurrent SetPolicy operation could free an entry
another thread was about to use (or double-drop its reference), yielding a
use-after-free of a kernel pool object (CWE-416). BFS runs in kernel and the SetPolicy
surface is reachable locally (from an AppSilo/AppContainer token), so the UAF is an
EoP-to-SYSTEM primitive; MSRC notes exploitation requires **winning a race**.

---

## Prerequisites and Constraints

- Local (`PR:L`, `AV:L`); `AC:H` — the attacker must win a race between SetPolicy
  operations on the policy hash table.
- The BFS interface requires an **AppSilo** token (`BfsIsApplicableToken` checks the
  `SeAppSiloSid` capability); the reporter reached it via an AppContainer/AppSilo
  process.
- Result: a freed policy entry is reused → kernel UAF → SYSTEM.

---

## Vulnerability Details

### Root Cause

The `EsfB` policy-entry lifetime was governed by a `RefNo` reference count that was
not consistently paired with the hash-table membership and lookups, so an entry
could be dropped to zero and freed while still reachable/used by a concurrent
SetPolicy path.

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

Gated behind `Feature_3148938554`, the July 2025 update reworks the policy-entry
reference lifecycle across the SetPolicy path — our diff shows code changes in
`BfsInsertPolicyEntry`, `BfsDereferencePolicyEntryEx`, `BfsInsertNotPresentPolicyEntry`
and `BfsCheckAndApplyPolicy`, all newly gated by `Feature_3148938554`. The reference
handling is corrected so an entry is not freed while still referenced/looked up
(and the local pointer copies are nulled after ownership transfer — see the paired
double-free fix CVE-2025-49693), closing the race that freed a still-in-use policy
entry.

### Patch Completeness Assessment

**CFR-gated behind `Feature_3148938554`.** The corrected lifecycle runs only when
the flag is enabled; the original 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, especially concurrent ones on the same user/container; UAF / pool-
corruption bugchecks in `bfs!BfsInsertPolicyEntry` / `BfsGetPolicyEntry` /
`BfsDereferencePolicyEntryEx` (tag `EsfB`) on unpatched/flag-disabled builds.

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

---

## References

- MSRC advisory — CVE-2025-49677 (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 double-free: CVE-2025-49693.
