Microsoft Windows Kernel - Registry Hive Loading Negative RtlMoveMemory Size in nt!CmpCheckValueList (MS16-124)

EDB-ID:

40600




Platform:

Windows

Date:

2016-10-20


Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=873

We have encountered Windows kernel crashes in the memmove() function called by nt!CmpCheckValueList while loading corrupted registry hive files. An example of a crash log excerpt generated after triggering the bug is shown below:

---
ATTEMPTED_WRITE_TO_READONLY_MEMORY (be)
An attempt was made to write to readonly memory.  The guilty driver is on the
stack trace (and is typically the current instruction pointer).
When possible, the guilty driver's name (Unicode string) is printed on
the bugcheck screen and saved in KiBugCheckDriver.
Arguments:
Arg1: b008d000, Virtual address for the attempted write.
Arg2: 45752121, PTE contents.
Arg3: a5d9b590, (reserved)
Arg4: 0000000b, (reserved)

Debugging Details:
------------------

[...]

STACK_TEXT:  
a5d9b60c 81820438 b008cb40 b008cb44 fffffffc nt!memmove+0x33
a5d9b670 8181f4f0 ab3709c8 00000000 b008cb34 nt!CmpCheckValueList+0x520
a5d9b6bc 8181fc01 03010001 0000b3b8 00000020 nt!CmpCheckKey+0x661
a5d9b6f4 818206d0 ab3709c8 03010001 00000001 nt!CmpCheckRegistry2+0x89
a5d9b73c 8182308f 03010001 8000057c 80000498 nt!CmCheckRegistry+0xfb
a5d9b798 817f6fa0 a5d9b828 00000002 00000000 nt!CmpInitializeHive+0x55c
a5d9b85c 817f7d85 a5d9bbb8 00000000 a5d9b9f4 nt!CmpInitHiveFromFile+0x1be
a5d9b9c0 817ffaae a5d9bbb8 a5d9ba88 a5d9ba0c nt!CmpCmdHiveOpen+0x50
a5d9bacc 817f83b8 a5d9bb90 a5d9bbb8 00000010 nt!CmLoadKey+0x459
a5d9bc0c 8168edc6 0025fd58 00000000 00000010 nt!NtLoadKeyEx+0x56c
a5d9bc0c 77806bf4 0025fd58 00000000 00000010 nt!KiSystemServicePostCall
WARNING: Frame IP not in any known module. Following frames may be wrong.
0025fdc0 00000000 00000000 00000000 00000000 0x77806bf4
---

The root cause of the bug seems to be that the nt!CmpCheckValueList function miscalculates the number of items to be shifted to the left in an array with 4-byte entries, resulting in the following call:

RtlMoveMemory(&array[x], &array[x + 1], 4 * (--y - x));

Here, the eventual value of the size parameter becomes negative (--y is smaller than x), but is treated by RtlMoveMemory as an unsigned integer, which is way beyond the size of the memory region, resulting in memory corruption. In a majority of observed cases, the specific negative value ended up being 0xfffffffc (-4), but we have also seen a few samples which crashed with size=0xfffffff8 (-8).

The issue reproduces on Windows 7. Considering the huge memory copy size, the crash should manifest both with and without Special Pools enabled. In order to reproduce the problem with the provided samples, it is necessary to load them with a dedicated program which calls the RegLoadAppKey() API.

Attached are three proof of concept hive files.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40600.zip