# CVE-2025-59207 — Windows Projected File System `prjflt.sys` User-Controlled Placeholder `SecurityDescriptor` Passed to File Creation Under KernelMode → Untrusted Pointer Dereference

---

## Summary

| | |
|---|---|
| **Product** | Windows — `prjflt.sys` (Windows Projected File System / ProjFS minifilter; placeholder copy) |
| **CVE ID** | CVE-2025-59207 |
| **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-822: Untrusted Pointer Dereference (+ CWE-20: Improper Input Validation) |
| **Delivery** | Local — a crafted placeholder message to the ProjFS communication port |
| **KB / Fixed build** | KB5066835 — `prjflt.sys` 10.0.26100.6899 (Win11 24H2 x64) |
| **Patch Date** | October 14, 2025 (2025-Oct) |
| **Pre-patch binary** | `prjflt.sys` 10.0.26100.6725 — SHA256 `ed9ae611924b369392d4ceceef99751da7a972186032480e8ad6258750e9ec74` |
| **Post-patch binary** | `prjflt.sys` 10.0.26100.6899 — SHA256 `185bab380ff59a6ebad6551af33abfa4e00ab37049ac9515c686b6e0f88d0f27` |
| **Feature flag** | `Feature_2983238971` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`prjflt.sys` is the Windows Projected File System (ProjFS) minifilter. A user-mode
ProjFS provider communicates with it over a filter communication port; incoming
messages are dispatched by `PrjfPortMessage`. A "write placeholder information"
message routes through `PrjfWritePlaceholderInformationHandler` to
`PrjfCopyAsPlaceHolder`, which materialises a placeholder file by calling
`FltCreateFileEx2`. The `_PRJ_PLACEHOLDER_INFO` message carries an inline
**`SecurityDescriptor`** for the new file, located at `PlaceholderInfo + SdOffset`:

```c
struct _PRJ_PLACEHOLDER_INFO {
    _PRJ_FILE_BASIC_INFO BasicInfo;
    ULONG EaLength; ULONG EaBufferOffset;
    ULONG SdLength; ULONG SdOffset;      // SecurityDescriptor = (char*)PlaceholderInfo + SdOffset
    ...
    char EaBuffer[]; char SecurityDescriptor[];
};
```

---

## Vulnerability Summary

The `SecurityDescriptor` content is fully attacker-controlled (it comes from the
user input buffer). Pre-patch, `PrjfCopyAsPlaceHolder` passed that raw
`SecurityDescriptor` straight to `FltCreateFileEx2`. File creation runs with
`PreviousMode == KernelMode`, so when the object manager reaches
`nt!SeCaptureSecurityDescriptor` it does **not** range-check the descriptor's
pointer fields — `Owner`, `Group`, `Sacl`, `Dacl` — which in an *absolute*
security descriptor are pointers. An attacker can therefore point any of them at
an **arbitrary kernel address**, which the kernel then dereferences while
capturing the descriptor:

```
nt!SeCaptureSecurityDescriptor      // PreviousMode == KernelMode -> Owner/Group/Sacl/Dacl NOT range-checked
nt!ObpCaptureObjectCreateInformation
nt!ObOpenObjectByNameEx
nt!IopCreateFile -> nt!IoCreateFileEx
FLTMGR!FltCreateFileEx2
prjflt!PrjfCopyAsPlaceHolder        // passes the raw user SecurityDescriptor
prjflt!PrjfWritePlaceholderInformationHandler
```

Because the descriptor's pointer fields are trusted as-is, a crafted placeholder
message yields an untrusted-pointer dereference of an attacker-chosen kernel
address (CWE-822). ProjFS placeholder creation runs in the kernel and the port is
reachable by a low-privileged local user, so this is a local EoP primitive.

---

## Prerequisites and Constraints

- Local, low-privileged (`PR:L`, `AV:L`, `AC:L`): connect to the ProjFS
  communication port and send a "write placeholder information" message.
- Craft `_PRJ_PLACEHOLDER_INFO` with a valid `SdOffset`/`SdLength` and an *absolute*
  `SECURITY_DESCRIPTOR` whose `Owner`/`Group`/`Sacl`/`Dacl` fields point at the
  target kernel addresses.
- Result: `SeCaptureSecurityDescriptor` (running as KernelMode) dereferences the
  attacker-chosen pointers without validation.

---

## Vulnerability Details

### Root Cause

The user-supplied placeholder `SecurityDescriptor` was forwarded to
`FltCreateFileEx2` without first being captured/validated as user data. Since the
file-create path executes with `PreviousMode == KernelMode`,
`SeCaptureSecurityDescriptor` skipped the address-scope checks on the descriptor's
`Owner`/`Group`/`Sacl`/`Dacl` pointers, letting them reference arbitrary kernel
memory.

### The patch (confirmed — diff, .6725 → .6899)

Gated behind `Feature_2983238971`, `PrjfCopyAsPlaceHolder` now **pre-captures the
user `SecurityDescriptor` itself, as UserMode**, before file creation — forcing the
pointer-field validation that the KernelMode path had skipped — and releases the
captured copy afterwards:

```c
// PrjfCopyAsPlaceHolder (10.0.26100.6899) — PATCHED, feature-enabled branch (from our diff)
// param_1 = PlaceholderInfo; *(uint*)(param_1 + 0x44) = SdOffset
iVar4 = SeCaptureSecurityDescriptor((ulonglong)*(uint *)(param_1 + 0x44) + param_2, 1, 1);
if (-1 < iVar4) goto LAB_5;      // proceed with the captured, validated SD
...
// cleanup:
uVar5 = Feature_2983238971__private_IsEnabledDeviceUsageNoInline();
if (((int)uVar5 != 0) && (local_f8 != 0))
    SeReleaseSecurityDescriptor(local_f8, 1, 1);   // free the captured copy
```

`SeCaptureSecurityDescriptor(SecurityDescriptor, PreviousMode = UserMode /*1*/, ...)`
validates the descriptor's `Owner`/`Group`/`Sacl`/`Dacl` against the user-address
range and produces a sanitised, self-relative kernel copy, which is then used for
`FltCreateFileEx2` in place of the raw user pointer. With the descriptor captured
as UserMode, an out-of-range (kernel) pointer is rejected rather than dereferenced,
closing the untrusted-pointer dereference. `SeCaptureSecurityDescriptor` /
`SeReleaseSecurityDescriptor` are newly imported and called by the function, and
the surrounding placeholder handlers (`PrjfWritePlaceholderInformationHandler`,
`PrjfUpdatePlaceholderIfNeededHandler`) carry the same gate.

### Patch Completeness Assessment

**CFR-gated behind `Feature_2983238971`.** The capture-as-UserMode path runs only
when the flag is enabled; the original raw-SD path still ships when disabled.
Verify `Feature_2983238971` is enabled to confirm the fix is live.

---

## Detection Guidance

**Behavioural.** ProjFS "write placeholder information" messages whose
`_PRJ_PLACEHOLDER_INFO` carries an absolute `SECURITY_DESCRIPTOR` with
kernel-range `Owner`/`Group`/`Sacl`/`Dacl` pointers; arbitrary-address /
pool-read bugchecks in `nt!SeCaptureSecurityDescriptor` reached from
`prjflt!PrjfCopyAsPlaceHolder` → `FltCreateFileEx2` on unpatched/flag-disabled
builds.

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

---

## References

- MSRC advisory — CVE-2025-59207 (Windows Projected File System Elevation of Privilege), released 2025-10-14, KB5066835.
- Full binary diff: `/data/patch_diffs/prjflt_sys-cve-2025-59207-ghidriff.md`
