# CVE-2025-53142 — Windows Brokering File System `bfs.sys` Use-After-Free of a Policy Entry in `BfsGetPolicyEntry`

---

## Summary

| | |
|---|---|
| **Product** | Microsoft Windows — `bfs.sys` (Microsoft Brokering File System) |
| **CVE ID** | CVE-2025-53142 |
| **Impact** | Elevation of Privilege |
| **MSRC severity** | Important |
| **CWE** | CWE-416: Use After Free |
| **Patch Date** | August 12, 2025 |
| **Pre-patch binary** | `bfs.sys` 10.0.26100.4768 (Jul 22 2025) — SHA256 `b48b506471d541a47125d3017d458b0ed736f4dacb693ed5db0af36f82bc0974` |
| **Post-patch binary** | `bfs.sys` 10.0.26100.4946 (Aug 12 2025 fix) — SHA256 `34c6bec796e53af24c22c969fca04e363b289ffffb103fac1cd9fd49c40b567d` |
| **Feature flag** | `Feature_1122292024` — **the fix is CFR-gated** |

---

## Product Description

The **Brokering File System** (`bfs.sys`) is the kernel component behind Windows'
file/storage brokering for AppContainer and packaged apps — it evaluates
per-container storage **policy entries** to decide what a sandboxed process may
access. Its policy and storage operations are reachable from low-privileged
(including AppContainer) callers, so a memory-safety bug in the policy path is a
local elevation-of-privilege primitive.

Policy entries are reference-counted objects carrying a **type tag at `+0x38`**
(observed values `0x10000001` and `0x10000000`) and an embedded object pointer at
`+0x28`; `BfsDereferencePolicyEntryEx` drops a reference and frees the entry when
the count reaches zero.

---

## Vulnerability Summary

`BfsGetPolicyEntry` looked up / built a policy entry (`local_90`) and, on a
particular type path, both used the entry's embedded pointer and **dropped its
reference (`BfsDereferencePolicyEntryEx`) unconditionally** — freeing the entry
while it was still referenced/returned, so a subsequent use operates on freed
pool (CWE-416).

```c
// BfsGetPolicyEntry (bfs.sys 10.0.26100.4768) — PRE-PATCH, from our diff
if (*(int *)(lVar3 + 0x38) != 0x10000001) {
    ...
    param_1 = *(undefined8 **)(lVar3 + 0x28);           // uses entry's embedded pointer
    if (*(int *)(lVar3 + 0x38) == 0x10000000) goto LAB_0;
    uVar5 = 0xc0000001;
    ...
}
*(undefined8 *)(local_90 + 0x60) = _DAT_3;
BfsDereferencePolicyEntryEx(local_90,'\0');             // *** unconditional deref/free ***
```

The type tag at `+0x38` was not validated before the `+0x28` pointer was
consumed, and the reference on `local_90` was released on a path where the entry
was still in use — a use-after-free of the policy entry.

---

## Prerequisites and Constraints

- Local authenticated session — reachable from low-privileged / AppContainer
  callers that drive Brokering File System policy/storage operations.
- No user interaction or special hardware.
- The freed object is a kernel-pool policy entry; standard pool grooming makes
  the dangling reference a controllable UAF, hence EoP rather than mere crash.

---

## Vulnerability Details

### Root Cause

Two mistakes combine: the policy entry's **type tag (`+0x38`) is not validated**
before its embedded pointer (`+0x28`) is dereferenced, and the entry's
**reference is dropped (`BfsDereferencePolicyEntryEx`) on a path where it is still
live**. Either the wrong-type pointer use or the premature free yields a
use-after-free on the policy-entry allocation.

### The patch (confirmed — diff, .4768 → .4946)

Gated behind `Feature_1122292024`, `BfsGetPolicyEntry` is restructured to
**validate the type tag first** and to make the dereference **conditional**:

```c
// BfsGetPolicyEntry (10.0.26100.4946) — PATCHED, feature-enabled branch
uVar5 = Feature_1122292024__private_IsEnabledDeviceUsageNoInline();
...
if (*(int *)(lVar3 + 0x38) == 0x10000001) {
    param_1 = *(undefined8 **)(lVar3 + 0x28);       // use pointer ONLY for the valid type
}
else if (*(int *)(lVar3 + 0x38) != 0x10000000) {
    uVar6 = 0xc0000001;                             // reject other types before any use
    goto LAB_3;
}
*(undefined8 *)(local_90 + 0x60) = _DAT_4;
if (<entry still owned>) {
    BfsDereferencePolicyEntryEx(local_90,'\0');      // *** deref now conditional ***
}
```

The embedded pointer is consumed only for the `0x10000001` type, any other type
is rejected with `0xc0000001` before use, and the reference on `local_90` is
released only when the entry is not still handed back — eliminating the
use-after-free. `BfsCreateStorage` receives matching lifetime hardening under the
same flag.

### Patch Completeness Assessment

**The fix is CFR-gated behind `Feature_1122292024`.** The corrected type-validation
and conditional-deref path runs only when the flag is enabled; with it disabled,
the patched binary retains the original unconditional-deref behaviour. Patch state
is not determined by file version alone — the same "both paths ship, gated at
runtime" pattern seen across the CLFS, `tapisrv`, `http.sys` and `ikeext` fixes in
this corpus.

---

## Detection Guidance

**Crash signature.** Pool-corruption / use-after-free bugchecks in
`bfs!BfsGetPolicyEntry` / `BfsDereferencePolicyEntryEx`, on systems exercising
Brokering File System policy from non-administrative or AppContainer processes.
Driver Verifier (Special Pool) on `bfs.sys` makes the dangling access observable.

**Behavioural.** Repeated Brokering File System policy/storage operations from a
sandboxed process aimed at racing/reusing a freed policy entry — a UAF trigger is
repetition-driven rather than a single malformed call.

**Config.** Because the fix is CFR-gated, confirming `Feature_1122292024` is
enabled is the only way to verify the corrected path is live.

---

## References

- MSRC advisory — CVE-2025-53142 (Microsoft Brokering File System Elevation of Privilege)
- Full binary diff: `/data/patch_diffs/bfs_sys-cve-2025-53142-ghidriff.md`
