# cng.sys Patch Diff — CVE-2020-17087

| | |
|---|---|
| Binary | cng.sys (Kernel Cryptography Next Generation driver) |
| Pre-patch version | 10.0.19041.264 (Windows 10 2004/20H2 family — unchanged through 19041.572 per winbindex) |
| Post-patch version | 10.0.19041.630 |
| KB | KB4586781 (November 10, 2020) |
| CVE | CVE-2020-17087 — pool buffer overflow (16-bit truncation), Elevation of Privilege, CVSS 7.8, **exploited in the wild** (Chrome sandbox escape chain with CVE-2020-15999) |
| Diff tool | ghidriff (Ghidra VersionTrackingDiff engine) |
| Functions changed | **1** with code changes (`CfgAdtpFormatPropertyBlock`, 79% similarity) out of 2833 |
| Functions added | **1** — `RtlUShortMult` (safe 16-bit multiply with overflow error) |
| Independent analysis | [Google Project Zero 0days-in-the-wild RCA](https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2020/CVE-2020-17087.html), [PixiePoint Security exploitation writeup](https://www.pixiepointsecurity.com/blog/nday-cve-2020-17087/) |
| Proof-of-concept | [GP0 issue 2104](https://bugs.chromium.org/p/project-zero/issues/detail?id=2104) (crash trigger); blue-team trigger variant in this repo |

## Summary

One function changed, one helper added — a textbook surgical fix of a textbook
integer truncation.

`cng!CfgAdtpFormatPropertyBlock` converts a caller-supplied byte buffer into a
space-separated hex dump in Unicode (`"XX 00 XX 00 20 00"` per input byte → 6
output bytes). The allocation size is computed as `len * 6` **in 16-bit
arithmetic**, while the conversion loop iterates `len` times:

```c
// cng.sys 10.0.19041.264 (pre-patch) — CfgAdtpFormatPropertyBlock @ 0x1c006245c
uVar2 = param_2 * 6;                          // ushort: 0x2AAB*6 wraps to 2
puVar3 = (ushort *)BCryptAlloc((ulonglong)uVar2);
...
do {                                          // but the loop runs param_2 times
    *puVar4     = "0123456789abcdefT"[*param_1 >> 4];
    puVar4[1]   = "0123456789abcdefT"[bVar1 & 0xf];
    puVar4[2]   = 0x20;                         // L' '
    puVar4     += 3;                            // 6 bytes per input byte
} while (uVar6 != 0);
```

With `len = 0x2AAB`, `(USHORT)(0x2AAB * 6) = 2` bytes are allocated and
`0x10002` bytes are written — a linear NonPagedPoolNx overflow whose content
is constrained to the pattern `XX 00 XX 00 20 00` (`XX` in
`0x30–0x39 / 0x61–0x66`). The length itself already reaches the function
truncated to 16 bits (`movzx ecx, di` at the call site in
`CfgAdtReportFunctionPropertyOperation`), so the dangerous input range is
`0x2AAB – 0xFFFF`.

The patch (10.0.19041.630) replaces the inline multiply with the newly added
safe-arithmetic helper `RtlUShortMult` and **fails the request on overflow**
before any allocation:

```c
// cng.sys 10.0.19041.630 (post-patch) — same function, same address
lVar2 = RtlUShortMult(param_2, /* 6 */, local_res8);   // decompiler shows the
if (lVar2 < 0)                                          // constant operand
    return lVar2;                                       // ambiguously; output
puVar3 = (ushort *)BCryptAlloc((ulonglong)local_res8[0]);// bookkeeping proves
...                                                      // out = len*6
```

The output-length bookkeeping is unchanged (`param_3[1] = out; param_3[0] =
out - 2`), which pins the helper's semantics to `out = len * 6` with an error
return on overflow. A `BCryptFree` cleanup path on failure was also added.

## Diff stats (ghidriff)

| Metric | Value |
|---|---|
| Functions matched | 2832 / 2833 (99.96%) |
| Matched with code changes | 1 — `CfgAdtpFormatPropertyBlock` @ `0x1c006245c` (79% similarity) |
| Added | 1 — `RtlUShortMult` |
| Modified, no code changes | 1 — `BCryptFree` (metadata only) |
| Strings added/deleted | 0 / 0 |

## Reachability recap

Userspace reaches the bug via `DeviceIoControl` on `\\.\GLOBALROOT\Device\Cng`
with IOCTL `0x390400` (the same path is exercised by the documented
`BCryptSetContextFunctionProperty()` API). Dispatch:
`KsecDispatch → cng!CngDispatch → cng!CngDeviceControl →
cng!ConfigIoHandler_Safeguarded → cng!_ConfigFunctionIoHandler →
cng!CfgAdtReportFunctionPropertyOperation → cng!CfgAdtpFormatPropertyBlock`.
See the [RCA](/data/patch_diffs/rca-cve-2020-17087.md) for the full call flow,
message layout, exploitation notes (Segment Heap BlockSize attack + named-pipe
DQE abuse), and detection guidance.

---

<sub>Diff produced 2026-07-22 with ghidriff against Win10 2004/20H2 binaries
from the Microsoft symbol server; findings verified by headless Ghidra
decompilation of both builds (`CfgAdtpFormatPropertyBlock` @ 0x1c006245c in
both).</sub>
