# CVE-2026-69381 — Windows Storage Port Driver `storport.sys` Device-ID Descriptor Out-of-Bounds Read → Kernel Memory Disclosure

---

## Summary

| | |
|---|---|
| **Product** | Windows — `storport.sys` (Storage Port Driver) |
| **CVE ID** | CVE-2026-69381 |
| **Impact** | Information Disclosure (kernel memory) |
| **MSRC severity** | Important |
| **CVSS** | 4.6 / 4.0 — `CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N/E:U/RL:O/RC:C` |
| **CWE** | CWE-125: Out-of-bounds Read |
| **Delivery** | Physical — attacker attaches a malicious storage device (`AV:P`) |
| **KB / Fixed build** | KB5124008 — `storport.sys` 10.0.26100.9444 (Win11 24H2/25H2 x64) |
| **Patch Date** | September 8, 2026 (2026-Sep) |
| **Pre-patch binary** | `storport.sys` 10.0.26100.9278 — SHA256 `6b37d434d0c5b6758af0754778d24ed1be3e8a61e39fa001dac8ba1f04e78b61` |
| **Post-patch binary** | `storport.sys` 10.0.26100.9444 — SHA256 `4a9c1154c586801dc6b823dad04304b3797636e5cb6c968ea41905f5f3c10621` |
| **Feature flag** | velocity feature check (helper unsymbolized as `FUN_14009362c`; numeric flag id not resolved in this diff) |
| **Exploitability** | Exploitation Unlikely; not publicly disclosed; not exploited (per MSRC) |

> The Sep-2026 `storport.sys` build recompiled ~300 functions; the security-relevant code change for this
> CVE is `ReplaceDeviceId` (the VPD-0x83 device-ID descriptor walk). In the post binary that function's
> symbol was not resolved (shown as `FUN_1400c3410`), matched to `ReplaceDeviceId` by implied match.

---

## Product Description

`storport.sys` services `IOCTL_STORAGE_QUERY_PROPERTY`. For `StorageDeviceIdProperty` it builds a
`STORAGE_DEVICE_ID_DESCRIPTOR` in `ReplaceDeviceId` from the device-returned **SCSI VPD page `0x83`**
(device identification) data — walking the identification descriptors and copying each into the output.

---

## Vulnerability Summary

Each VPD identification descriptor carries its own length byte (`pbVar[3]`). `ReplaceDeviceId` walked the
descriptors taking that length, `memcpy`-ing `pbVar[3]` bytes into the output, and advancing by
`4 + pbVar[3]`. Pre-patch the walk was bounded **only by the device-declared page length** and never
verified that a descriptor's header and body stayed within the actual buffer. A crafted device that
returns a VPD `0x83` page whose (last) descriptor length runs past the declared page causes storport to
**read past the end of the device buffer** and copy adjacent **kernel memory** into the returned
descriptor — an out-of-bounds read disclosing kernel memory to the user-mode caller. Because the attacker
must attach a malicious storage device, Microsoft scores it `AV:P` (physical); the MSRC FAQ describes the
leak as "kernel memory read … from a user mode process."

---

## Prerequisites and Constraints

- Physical access to attach a malicious storage device (`AV:P`, `PR:N`, `UI:N`).
- The device returns a crafted VPD page `0x83` with a descriptor length exceeding the buffer.
- Confidentiality-only impact (`C:H`/`I:N`/`A:N`) — an information leak, not corruption.

---

## Vulnerability Details

### Root Cause

The device-ID descriptor walk trusted attacker-supplied per-descriptor lengths and the page-length field
without bounding each read/copy to the real buffer extent, so an overlong descriptor read out of bounds.

### The patch (confirmed — diff, .9278 → .9444)

Gated behind a velocity feature check (helper unsymbolized as `FUN_14009362c`), `ReplaceDeviceId` clamps
the usable length and bounds each descriptor before reading it:

```c
// ReplaceDeviceId (.9444) — PATCHED (from the diff)
uVar11 = (uint)pagelen;                       // device-declared VPD 0x83 length
if (feature()) {                              // FUN_14009362c — velocity gate
    uVar7 = pagelen + 4;
    if (0xffff < uVar7) uVar7 = 0xffff;        // cap
    uVar10 = uVar7 - 4;
    if (uVar11 <= uVar7 - 4) uVar10 = uVar11;  // uVar10 = safe usable length
}
...
do {
    if (feature() && (uVar10 < uVar7 + 4)) break;              // header must fit
    pbVar9 = (byte *)(uVar7 + 4 + lVar2);
    if (feature() && (uVar10 < pbVar9[3] + 4 + uVar7)) break;  // body must fit
    uVar7 = uVar7 + 4 + pbVar9[3];
    ...
    memcpy(dst + 4, pbVar9 + 4, pbVar9[3]);   // now bounded
} while (uVar7 < uVar11);
```

With the header check (`uVar10 < uVar7 + 4`) and body check (`uVar10 < pbVar9[3] + 4 + uVar7`) the parse
stops before it would read past the clamped buffer length, closing the out-of-bounds read.

### Patch Completeness Assessment

**Velocity/CFR-gated.** The bounds checks run only when the feature helper returns nonzero; the original
unchecked walk runs when disabled. Verify the feature is enabled to confirm the fix is live. The vulnerable
function and the added bounds checks are cleanly isolable in the diff; the RCA asserts "reads past the
device-supplied VPD buffer" rather than a specific adjacent allocation.

---

## Detection Guidance

**Behavioural.** Attachment of untrusted storage devices followed by `IOCTL_STORAGE_QUERY_PROPERTY`
(`StorageDeviceIdProperty`) queries; malformed VPD page `0x83` responses (descriptor length exceeding the
page length). There is no crash signature — it is an infoleak, so hunt on device/response anomalies rather
than faults. Restrict attachment of untrusted storage hardware.

**Config.** The fix is velocity/CFR-gated — confirm the feature is enabled and the Sep-2026 update
(`storport.sys` 10.0.26100.9444, KB5124008) is installed.

---

## References

- MSRC advisory — CVE-2026-69381 (Windows Storage Port Driver Information Disclosure), 2026-09-08, KB5124008.
- Full binary diff: `/data/patch_diffs/storport_sys-cve-2026-69381-ghidriff.md`
