
POC for cve-2019-1458
In December Kaspersky published a blogpost about [0day exploit used in the wild][1]. It piqued my interest because although they described how the exploit was working, they didn't provide any POC in their analysis.
This is why I decided to try writing POC for this vulnerability based on Kaspersky's blogpost and patch analysis.
This post describes my journey doing that.
First thing was to collect as much information about this vulnerability as I could. Reading through mentioned blogpost I extracted following information:
NtUserMessageCall APIwin32k!DrawSwitchWndHiliteBeside that there is nice screenshot of decompiled code showing some of previously listed things.
To be exact it shows: creation of switch window, call to function named toggle_alt_key and multiple calls to NtUserMessageCall.
[Image source][1]
A lot of useful information, but it still doesn't describe how exactly this vulnerability works and how to trigger it.
[Affected module was win32k.sys][2]. I downloaded both patched and unpatched versions of this module.
For win7 x64 those were:
They can be downloaded from [Microsoft Update Catalog][3]
Here is bindiff result of comparing both versions

After ruling out functions related to DebugHook functionality all we are really left with is this slightly changed function InitFunctionTables()

Definitely not the biggest patch out there.
This won't help to immediately identify root cause of this vulnerability. But it's worth noting that some initial values for variables at
*(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180) have been added. So this might be a bug related to uninitialized variable.
In this section I will present how I progressively build up POC that triggers this vulnerability, while simultaneously figuring out what the vulnerability actually was.
Patch diffing didn't give too much useful info at the beginning, so I relied mostly on Kaspersky's blogpost at first stage of development.
To have a good testing environment I prepared Win7 SP1 x64 VM with last vulnerable version of win32k running. On top of that I attached Windbg to this VM to do kernel debugging and while doing that I also set up symbol server path.
I started my investigation by looking at win32k!DrawSwitchWndHilite which was mentioned in blogpost. It is being called from two places: xxxMoveSwitchWndHilite and xxxPaintSwitchWindow, latter one immediately got my attention, because of surrounding GetKeyState/GetAsyncKeyState calls that were mentioned in original report. What is more those calls are checking for ALT key being pressed.

Call to DrawSwitchWndHilite from xxxPaintSwitchWindow
Further following call cross references (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite) I found that first element in that chain is referenced in InitFunctionTables, function that was fixed in patch.
Next I looked into NtUserMessageCall from screenshot of decompiled code.
Here is declaration of this function
NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)
Exploit is calling it with msg = 0x14 and dwType = 0xE0. Let's see what it does.
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
printf("[-] Failed registering SploitWnd window class\n");
exit(-1);
}
printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create SploitWnd window\n");
exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);
Here I registered simple window class and created window of that class. Then called NtUserMessageCall with same parameters as exploit. To see what happens under the hood I setup breakpoint kd> ba e 1 win32k!NtUserMessageCall and run the code.
There are quite a few calls being made to this function so I had to catch the right one, but it wasn't that difficult, it was the one with really short callstack.

NtUserMessageCall
Stepping through code revealed that it calls function from gapfnMessageCall array, index is calculated based on msg value and is equal to 0, so the call is made to NtUserfnDWORD

NtUserfnDWORD
Next call is made using dwType value, and now gpsi offset equals to 0x40, and call leads to xxxWrapSwitchWndProc (this function already appeared when I was checking DrawSwitchWndHilite call chain).
xxxWrapSwitchWndProc simply calls xxxSwitchWndProc.

xxxSwitchWndProc
And this is the end, code fails here, not going any further to xxxPaintSwitchWindow, which is where we want to get based on msg value (0x14). Let's check why.
Code fails at this stage because, as highlighted on previous image, fnid of our window is not equal to 0x2A0 (FNID_SWITCH) and message we are sending is not equal to 1, hence we end up in xxxDefWindowProc. To avoid this scenario we have to call xxxSwitchWndProc with fnid set to FNID_SWITCH, so that we will go straight to switch statement and later to xxxPaintSwitchWindow.
How to set correct fnid? Actually the same function does it in the first if block, we just have to fail all checks inside it to get to the instruction setting fnid.
Here are conditions we need to meet, to fail all three if checks: