The Microsoft Windows kernel suffers from a 64-bit pool memory disclosure vulnerability via REG_RESOURCE_LIST registry values (videoprt.sys descriptors).
a3291f506262cdeab2f6590a2c2a2c56
Windows Kernel 64-bit pool memory disclosure via REG_RESOURCE_LIST registry values (videoprt.sys descriptors)
CVE-2018-0899
We have discovered a Windows kernel memory disclosure vulnerability through the body of "AllocConfig" registry values (of type REG_RESOURCE_LIST) corresponding to devices handled by videoprt.sys, which can be found under HKLM\SYSTEM\CurrentControlSet\Enum\*\*\*\Control\AllocConfig. The vulnerability affects 64-bit versions of Windows 7 to 10, and is very similar to <a href="/p/project-zero/issues/detail?id=1473" title="Windows Kernel 64-bit pool memory disclosure via REG_RESOURCE_LIST registry values (CmResourceTypeDevicePrivate entries)" class="closed_ref" rel="nofollow"> Issue #1473 </a>.
The leak was originally detected under the following stack trace (Windows 7):
--- cut ---
kd> k
# Child-SP RetAddr Call Site
00 fffff880`020a68a8 fffff800`0295bdaa nt!memcpy+0x3
01 fffff880`020a68b0 fffff800`0295c30f nt!CmpQueryKeyValueData+0xea
02 fffff880`020a6940 fffff800`0296107f nt!CmQueryValueKey+0x1af
03 fffff880`020a6a20 fffff800`0268d093 nt!NtQueryValueKey+0x37d
04 fffff880`020a6bb0 00000000`772abeaa nt!KiSystemServiceCopyEnd+0x13
05 00000000`007aeb18 00000000`77153dec ntdll!ZwQueryValueKey+0xa
06 00000000`007aeb20 00000000`77153ff2 kernel32!LocalBaseRegQueryValue+0x17c
07 00000000`007aeca0 000007fe`fc281dd1 kernel32!RegQueryValueExW+0xf2
08 00000000`007aed40 000007fe`fc2735e4 umpnpmgr!OpenLogConfKey+0xb3
09 00000000`007aedc0 000007fe`ff25e9d5 umpnpmgr!PNP_GetFirstLogConf+0x8d
[...]
--- cut ---
and more specifically in the copying of the CM_RESOURCE_LIST structure:
--- cut ---
kd> db rdx rdx+r8-1
fffff8a0`049de874 01 00 00 00 01 00 00 00-00 00 00 00 00 00 00 00 ................
fffff8a0`049de884 05 00 00 00 01 03 01 00-b0 03 00 00 00 00 00 00 ................
fffff8a0`049de894 0c 00 00 00 aa aa aa aa-01 03 01 00 c0 03 00 00 ................
fffff8a0`049de8a4 00 00 00 00 20 00 00 00-aa aa aa aa 03 03 00 00 .... ...........
fffff8a0`049de8b4 00 00 0a 00 00 00 00 00-00 00 02 00 aa aa aa aa ................
fffff8a0`049de8c4 01 03 01 00 ce 01 00 00-00 00 00 00 02 00 00 00 ................
fffff8a0`049de8d4 aa aa aa aa 01 03 01 00-e8 02 00 00 00 00 00 00 ................
fffff8a0`049de8e4 08 00 00 00 aa aa aa aa ........
--- cut ---
In the above example, the 0xaa values are unitialized bytes originating from a pool allocation made in videoprt!pVideoPortReportResourceList.
If we dive deeper into the layout of the memory area, we can see that in all cases, the leaked bytes reside at the end of a nested CM_PARTIAL_RESOURCE_DESCRIPTOR structure:
--- cut ---
kd> dt _CM_RESOURCE_LIST @rdx
ntdll!_CM_RESOURCE_LIST
+0x000 Count : 1
+0x004 List : [1] _CM_FULL_RESOURCE_DESCRIPTOR
kd> dx -r1 ((ntdll!_CM_FULL_RESOURCE_DESCRIPTOR (*)[1])0xfffff8a0049de878)
((ntdll!_CM_FULL_RESOURCE_DESCRIPTOR (*)[1])0xfffff8a0049de878) : 0xfffff8a0049de878 [Type: _CM_FULL_RESOURCE_DESCRIPTOR (*)[1]]
[0] [Type: _CM_FULL_RESOURCE_DESCRIPTOR]
kd> dx -r1 ((ntdll!_CM_FULL_RESOURCE_DESCRIPTOR *)0xfffff8a0049de878)
((ntdll!_CM_FULL_RESOURCE_DESCRIPTOR *)0xfffff8a0049de878) : 0xfffff8a0049de878 [Type: _CM_FULL_RESOURCE_DESCRIPTOR *]
[<Raw View>] [Type: _CM_FULL_RESOURCE_DESCRIPTOR]
[0] : Port Resource: 0x3b0 of length 0xc [Type: _CM_PARTIAL_RESOURCE_DESCRIPTOR]
[1] : Port Resource: 0x3c0 of length 0x20 [Type: _CM_PARTIAL_RESOURCE_DESCRIPTOR]
[2] : Memory Resource: 0xa0000 of length 0x20000 [Type: _CM_PARTIAL_RESOURCE_DESCRIPTOR]
[3] : Port Resource: 0x1ce of length 0x2 [Type: _CM_PARTIAL_RESOURCE_DESCRIPTOR]
[4] : Port Resource: 0x2e8 of length 0x8 [Type: _CM_PARTIAL_RESOURCE_DESCRIPTOR]
kd> dx -r1 ((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*0))
((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*0)) : 0xfffff8a0049de88c [Type: unsigned long (*)[4]]
[0] : 0x3b0 [Type: unsigned long]
[1] : 0x0 [Type: unsigned long]
[2] : 0xc [Type: unsigned long]
[3] : 0xaaaaaaaa [Type: unsigned long]
kd> dx -r1 ((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*1))
((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*1)) : 0xfffff8a0049de8a0 [Type: unsigned long (*)[4]]
[0] : 0x3c0 [Type: unsigned long]
[1] : 0x0 [Type: unsigned long]
[2] : 0x20 [Type: unsigned long]
[3] : 0xaaaaaaaa [Type: unsigned long]
kd> dx -r1 ((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*2))
((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*2)) : 0xfffff8a0049de8b4 [Type: unsigned long (*)[4]]
[0] : 0xa0000 [Type: unsigned long]
[1] : 0x0 [Type: unsigned long]
[2] : 0x20000 [Type: unsigned long]
[3] : 0xaaaaaaaa [Type: unsigned long]
kd> dx -r1 ((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*3))
((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*3)) : 0xfffff8a0049de8c8 [Type: unsigned long (*)[4]]
[0] : 0x1ce [Type: unsigned long]
[1] : 0x0 [Type: unsigned long]
[2] : 0x2 [Type: unsigned long]
[3] : 0xaaaaaaaa [Type: unsigned long]
kd> dx -r1 ((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*4))
((ntdll!unsigned long (*)[4])(0xfffff8a0049de88c+sizeof(_CM_PARTIAL_RESOURCE_DESCRIPTOR)*4)) : 0xfffff8a0049de8dc [Type: unsigned long (*)[4]]
[0] : 0x2e8 [Type: unsigned long]
[1] : 0x0 [Type: unsigned long]
[2] : 0x8 [Type: unsigned long]
[3] : 0xaaaaaaaa [Type: unsigned long]
--- cut ---
There are two types of descriptors here: port and memory resources. Their corresponding structures within the CM_PARTIAL_RESOURCE_DESCRIPTOR.u union are:
--- cut ---
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Port;
--- cut ---
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Memory;
--- cut ---
The size of both these structures is 12 (0xc), but on x64 builds, the size of the overall union is 16 (0x10). This leaves the 4 bytes directly after the Port/Memory structures unused.
A proof-of-concept program is not provided for this issue, but it has been observed at normal system runtime, and is quite evident in the code.
Repeatedly triggering the vulnerability could allow local authenticated attackers to defeat certain exploit mitigations (kernel ASLR) or read other secrets stored in the kernel address space.
This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available, the bug report will become visible to the public.
Found by: mjurczyk