CVE-2016-0040 Story of Uninitialized Pointer in Windows Kernel
Update:
This post is a resharing of a blog I wrote about a vulnerability I discovered in Windows kernel almost a decade ago.
this vulnerablity is interesting for three reseaons
- it’s a tweetable vulnerablity :)
- it gave Write arbitrary data to arbitrary address primitive.
- it has an amusing story associated with it. after my discovery of this vulnerability, I shared a tweet about it, which caught the attention of researchers from Microsoft. Remarkably, they were able to discern the root cause of the vulnerability simply by examining my tweet. you can read their story in MSRC blog
- there was another vulnerablity( CVE-2016-0087) in this function I had found and I was expecting MSRC to spot it when then are auditing wmi, but they didn’t.
Original post:
A few months ago, I discovered some vulnerabilities in the Windows kernel, mostly related to local privilege escalation.
Microsoft patched one of the reported vulnerabilities in MS16-014. The vulnerability type is an uninitialized pointer dereference. This vulnerability can be triggered even by a process with “low integrity level”, meaning that successfully exploiting this vulnerability can lead to bypassing the sandbox (for example, the IE sandbox) or generic local privilege escalation for any process.
Here’s a description of the bug:
For handling some WMI functions, Windows NT creates a named device called WMIDataDevice
.
This device is accessible from user mode with any permission (you can check it with WinObj). WMIDataDevice handles some IOCTLs, with the WmipReceiveNotifications
function responsible for the IOCTL_WMI_ENUMERATE_GUIDS IOCTL
. Based on the first DWORD of Irp->AssociatedIrp.SystemBuffer
, WmipReceiveNotifications decides whether to use the stack
or kernel pool
as a buffer for storing data/pointers. If the first DWORD is less than or equal to 0x10, the stack is selected as the buffer.
There’s another important usage of the mentioned DWORD. WmipReceiveNotifications uses this DWORD as a counter for looping and initializing the local buffer. So, if we put 0 in the first DWORD of Irp->AssociatedIrp.SystemBuffer from user mode, the function selects the stack as the buffer. As mentioned earlier, this buffer is initiated in a loop. In this case, since we passed 0, the function skips loop execution, leaving the stack buffer uninitialized.
To reach the vulnerability, we need to bypass some other condition inside WmipReceiveNotifications.
v16 comes from user mode and its value needs to be 2.
Insert a valid handle for ObReferenceObjectByHandle in SystemBuffer.
finally uninitialized local variable used as target pointer and function write a DWORD from SystemBuffer + 8 to it
we can control what is written but for manipulating uninitialized stack we need a good stack spray inside kernel
Utilizing an uninitialized local variable as a pointer to write arbitrary data to its referenced location requires the attacker to employ a stack spraying technique for successful exploitation. This vulnerability presents a “write-what-where” condition, offering multiple ways for exploitation, such as zero ACL or SET token permission.
Advantages of the vulnerability:
- The bug can be triggered even in low integrity contexts.
- It’s unrelated to win32k.sys, meaning it ignores the “Win32k system call disable policy”, for instance in Chrome browser.
- It works with the default OS configuration, providing a universal sandbox bypass.
Sample POC for the vulnerability
in order to exploit this vulnerablity we have to spary kernel stack memory, after talking with Mateusz Jurczyk he told me about using nt!NtMapUserPhysicalPages an excellent technique to Spraying Kernel Stack memory
with help of above method I managed to exploit this vulnerablity and I had a plan to participate to Pwn2Own
in windows kernel catagorty, but Microsoft spotted my vulnerablity with a simple miskate I did by tweeting about it.