Microsoft Windows Kernel nt!PiUEventHandleGetEven Stack Memory Disclosure

The Microsoft Windows kernel suffers from a stack memory disclosure from nt!RawMountVolume via nt!PiUEventHandleGetEvent (\Device\DeviceApi\CMNotify device).

MD5 | 60cf9a8ec04755f71c4247b0446d8196

Windows Kernel stack memory disclosure from nt!RawMountVolume via nt!PiUEventHandleGetEvent (\Device\DeviceApi\CMNotify device) 


We have discovered that it is possible to disclose portions of uninitialized kernel stack memory to user-mode applications in Windows 10 through the PiUEventHandleGetEvent IOCTL (0x470804) sent to the \Device\DeviceApi\CMNotify device. The analysis shown below was performed on Windows 10 1709 32-bit.

The copying of the disclosed data from kernel to user-mode memory was detected under the following stack trace:

--- cut ---
kd> k
# ChildEBP RetAddr
00 9ecfeafc 81e3b15a nt!memcpy+0x35
01 9ecfeb54 81e3a67c nt!IopCompleteRequest+0x43a
02 9ecfeba8 81e3a31a nt!IopfCompleteRequest+0x35c
03 9ecfebb4 8212c5fb nt!IofCompleteRequest+0x1a
04 9ecfebc0 8212c2b4 nt!PiUEventHandleIoctl+0x39
05 9ecfebd4 8212affc nt!PiUEventDispatch+0x30
06 9ecfebf8 81e97e98 nt!PiDaDispatch+0x28
07 9ecfec14 820e16da nt!IofCallDriver+0x48
08 9ecfecf8 820e113a nt!IopXxxControlFile+0x59a
09 9ecfed24 81f51ca7 nt!NtDeviceIoControlFile+0x2a
0a 9ecfed24 77bc1670 nt!KiSystemServicePostCall
0b 033df550 77bbfeca ntdll!KiFastSystemCallRet
0c 033df554 750b712e ntdll!NtDeviceIoControlFile+0xa
0d 033df5b4 75dd9e9e KERNELBASE!DeviceIoControl+0x6e
0e 033df5e4 752bd243 KERNEL32!DeviceIoControlImplementation+0x3e
0f 033df620 77b861d0 cfgmgr32!CmNotifyWnfNotificationCallback+0x83
10 033df6d4 77b85ee2 ntdll!RtlpWnfWalkUserSubscriptionList+0x245
11 033df6f8 77b85db5 ntdll!RtlpWnfProcessCurrentDescriptor+0xae
12 033df720 77b859e1 ntdll!RtlpWnfNotificationThread+0x65
13 033df754 77b858c0 ntdll!TppExecuteWaitCallback+0xca
14 033df770 77b6b269 ntdll!TppWaitCompletion+0x80
15 033df930 75dd9564 ntdll!TppWorkerThread+0x5d9
16 033df944 77b9293c KERNEL32!BaseThreadInitThunk+0x24
17 033df98c 77b92910 ntdll!__RtlUserThreadStart+0x2b
18 033df99c 00000000 ntdll!_RtlUserThreadStart+0x1b
--- cut ---

The total size of the output buffer is 0x5a (90) bytes. The last 10 bytes of the memory region are as follows:

--- cut ---
9aaad050 52 00 41 00 57 00 bb bb-bb bb R.A.W.....
--- cut ---

Here, the four 0xbb bytes indicate uninitialized values from the kernel stack. These bytes originate from the stack frame of the nt!RawMountVolume function. Inside of it, we can find the following assembly code:

--- cut ---
PAGE:0073746F xor ecx, ecx
PAGE:00737471 inc ecx
PAGE:00737472 mov [ebp+var_54], cx
PAGE:00737476 mov [ebp+var_40], edi
PAGE:00737479 push 36h
PAGE:0073747B pop eax
PAGE:0073747C mov [ebp+var_52], ax
PAGE:00737480 or [ebp+var_3C], 0FFFFFFFFh
PAGE:00737484 mov [ebp+var_38], ecx
PAGE:00737487 mov [ebp+var_34], edi
PAGE:0073748A push 6
PAGE:0073748C pop ecx
PAGE:0073748D mov [ebp+var_30], ecx
PAGE:00737490 mov [ebp+var_2C], 10h
PAGE:00737497 mov eax, dword ptr ds:aRaw ; "RAW"
PAGE:0073749C mov [ebp+var_28], eax
PAGE:0073749F mov ax, word ptr ds:aRaw+4 ; "W"
PAGE:007374A5 mov [ebp+var_24], ax
PAGE:007374A9 lea eax, [ebp+var_54]
PAGE:007374AC push eax ; NotificationStructure
PAGE:007374AD push ecx ; int
PAGE:007374AE push [ebp+Object] ; int
PAGE:007374B1 call FsRtlNotifyVolumeEventEx(x,x,x)
--- cut ---

In the above snippet, we can see that a local structure is first initialized, and then passed via the 3rd argument to nt!FsRtlNotifyVolumeEventEx. While the size of the structure is presumably 0x36, and it starts at EBP-0x54, the last four bytes at EBP-0x22 are never written to. The last initialized portion of memory is the 6-byte L"RAW" string at EBP-0x28, which we also saw in the dump of memory copied to userland. We suspect that the four bytes correspond to a partially filled volume type string buffer, or some kind of padding inserted by the compiler. They are saved in internal kernel structures by nt!FsRtlNotifyVolumeEventEx, and can be later acquired by a user-mode client through the PiUEventHandleGetEvent IOCTL.

A proof-of-concept program is not provided for this issue, but it has been observed and confirmed 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

Related Posts