This past Patch Tuesday Microsoft released MS15-010: Vulnerabilities in Windows Kernel-Mode Driver Could Allow Remote Code Execution. This patch addressed multiple privately reported vulnerabilities in win32k.sys and one publicly disclosed vulnerability in cng.sys.
The first notable thing we noticed was that several handlers for TrueType instructions, @irtp_*, were touched. While we did analyze these changes, they will not be the topic of this post.
The next interesting thing we noticed was that there is a relationship between _FindSystemTimer and _xxxDispatchMessage in that the latter calls the former. We are going to take a closer look at these two functions.
The diff for _xxxDispatchMessageA only showed one changed basic-block, so we can visualize it easily with in a combined display.
Based on the altered signature of _FindSystemTimer, it appears as if we are calling it with an extra parameter post-patch. To get more understanding, let's look at _FindSystemTimer .
The changes to _FindSystemTimer are minimal. Before the patch, we have a small function that traverses a global linked list (starting at gtmrListHead) to search for the system timer pTmr associated with the passed message pMsg. Upon successful validation of the pTmr->flags and the pMsg->lParam, the current pTmr is returned to the caller. Otherwise null is returned. The IDA Pro disassembly of the pre-patched version of _FindSystemTimer is shown below:
The patched FindSystemTimer now takes two parameters, a pMsg and a pWnd. This function is similar to the unpatched version, except that it now performs further validation of the wParam member of pMsg against the nID member of pTmr. Additionally the passed in pWnd is validated against the pTimer spWnd member.
Clearly there had been a problem with one of the parameters that is now validated, which is going to be our starting point for fuzzing.
Given that xxxDispatchMessage resides in the kernel, how can we exercise it from userspace?
We begin with USER32!DispatchMessageA, which is an exported function that is basically a wrapper for USER32!DispatchMessageWorker.
Inside DispatchMessageWorker is logic that decides how to handle the incoming message. The above IDA snippet illustrates the handling of WM_SYSTIMER in DispatchMessageWorker. The associated C code might appear as shown below:
So, if our message is WM_SYSTIMER it is then passed to the Windows kernel via WIN32K!NtUserDispatchMessage, the disassembly for which is given below:
win32k!NtUserDispatchMessage is a simple function that performs a sanity check on the message parameter and passes it to _xxxDispatchMessage.
Here is where we finally get to a patched function. In this case win32k!_xxxDispatchMessage contains some logic for processing WM_SYSTIMER messages. The logical flow to the patched code is governed by EAX being a WM_SYSTIMER.
Using IDA, we learn that WM_SYSTIMER is defined as 0x118, which is a good start. Then if we search for WM_SYSTIMER in Google, we get better information from http://www.baiyujia.com/vfpadvanced/f_vfpa_wm_systimer.asp.
From IDA and the Google search result, it looks like WM_SYSTIMER is an undocumented message with value 0x118. Further, we see that the lParam and wParam for this message are also undocumented. The above explanation seems to indicate that WM_SYSTIMER is related to the caret blink rate.
Our starting point for fuzzing is a classic Win32 API program that creates and displays a caret. The code below has been adapted from http://www.winprog.org/tutorial/simple_window.html:
We expect to see WM_SYSTIMER messages emitted when this code is run.
Looking at the output of Spy++, we do indeed see our 0x118 messages. At this point we optimistically then retooled our example code to fuzz the wParam, hwnd, and lParam of our Msg and waited. And waited. But we never got the expected kernel exception. Rather then give up at this point, we thought of drilling back into the code that creates a caret (xxxCreateCaret) to see where it sets its timer.
A combination of luck and intuition indicated that the second parameter to the SetSystemTimer call (0xffff) might specify the type of the timer (see the wParam in the Spy++ output above). So we used IDA to determine all of the callers of the SetSystemTimer function.
XREFS to SetSystemTimer
It looks like we have a few options here. Beginning with some C++ forms code, we exercised SetTooltipTimer and MouseHover and managed to trigger the timer with a new wParam (0xfffa).
More Spy++ output
Fuzzing attempts at SetTooltipTimer and MouseHover also failed to trigger a crash. Fuzzing additional callers (including 20 minutes with mouse sonar APIs) failed until we arrived at xxxFlashWindow, which we had noticed in earlier research but for some reason overlooked until later in the fuzzing exercise.
Fortunately xxxFlashWindow is easy to fuzz since it's called directly from the userspace FlashWindow function. Therefore we can simply add a line of code to our previous template to start generating our timer messages.
FlashWindow Frankenstein code
As a verification check, we look at what we believe to be the timer type parameter of the call to SetSystemTimer inside the xxxFlashWindow function.
WIN32K!xxxFlashWindow call to SetSystemTimer
Based on the above disassembly fragment, we hope to see a wParam of 0xfff8.
Even more Spy++ output
Firing up Spy++ (again) and interacting with our toy application, we see that indeed we are receiving the expected wParam.
We modify our cobbled together example codes to make a dumb fuzzer, the source code for which is given below:
[caption id="attachment_21016" align="alignnone" width="385"]
Using the above code, we finally got our crash:
We are crashing in _GetProp, with what appears to be a null pointer dereference.
GetProp is a function which searches a list of window properties extracted from the passed PWND for a property which has a string member matching the passed string. The undocumented PWND struct can be seen in the ReactOS source code:
Now it is clear that we are dereferencing a null pointer when trying to access the pWnd->ppropList in _GetProp.
While a discussion of exploitation is outside the scope of this post, it should be noted that mitigation against kernel null pointer dereferences is available for Windows 7 and is in place by default in Windows 8+.