
POC für cve-2019-1458
Im Dezember veröffentlichte Kaspersky einen Blogbeitrag über [einen in freier Wildbahn eingesetzten 0day-Exploit][1]. Das weckte mein Interesse, denn obwohl sie beschrieben, wie der Exploit funktionierte, lieferten sie in ihrer Analyse keinen POC.
Deshalb beschloss ich, basierend auf Kasperskys Blogbeitrag und der Patch-Analyse einen POC für diese Schwachstelle zu schreiben.
Dieser Beitrag beschreibt meinen Weg dabei.
Zuerst musste ich so viele Informationen wie möglich über diese Schwachstelle sammeln. Beim Durchlesen des erwähnten Blogbeitrags habe ich folgende Informationen extrahiert:
NtUserMessageCall-API erforderlichwin32k!DrawSwitchWndHiliteAußerdem gibt es einen schönen Screenshot des dekompilierten Codes, der einige der zuvor aufgeführten Punkte zeigt.
Genauer gesagt zeigt er: die Erstellung des Wechselfensters, einen Aufruf der Funktion toggle_alt_key und mehrere Aufrufe von NtUserMessageCall.
[Image source][1]
Viele nützliche Informationen, aber es wird immer noch nicht beschrieben, wie genau diese Schwachstelle funktioniert und wie man sie auslöst.
[Das betroffene Modul war win32k.sys][2]. Ich habe sowohl die gepatchte als auch die ungepatchte Version dieses Moduls heruntergeladen.
Für Win7 x64 waren das:
Sie können aus dem [Microsoft Update Catalog][3] heruntergeladen werden.
Hier ist das Bindiff-Ergebnis des Vergleichs beider Versionen.

Nachdem wir Funktionen im Zusammenhang mit der DebugHook-Funktionalität ausgeschlossen haben, bleibt uns im Grunde nur diese leicht veränderte Funktion InitFunctionTables().

Definitiv nicht der größte Patch.
Das hilft nicht, die Ursache dieser Schwachstelle sofort zu identifizieren. Aber es ist erwähnenswert, dass einige Anfangswerte für Variablen bei
*(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180) hinzugefügt wurden. Es könnte sich also um einen Fehler im Zusammenhang mit einer nicht initialisierten Variablen handeln.
In diesem Abschnitt werde ich darlegen, wie ich schrittweise einen POC aufgebaut habe, der diese Schwachstelle auslöst, während ich gleichzeitig herausfand, worin die Schwachstelle tatsächlich bestand.
Patch-Diffing lieferte anfangs nicht allzu viele nützliche Informationen, daher stützte ich mich in der ersten Entwicklungsphase hauptsächlich auf Kasperskys Blogbeitrag.
Um eine gute Testumgebung zu haben, habe ich eine Win7 SP1 x64 VM mit der letzten verwundbaren Version von win32k vorbereitet. Darüber hinaus habe ich Windbg an diese VM angehängt, um Kernel-Debugging durchzuführen, und dabei auch den Symbolserver-Pfad eingerichtet.
Ich begann meine Untersuchung mit der Funktion win32k!DrawSwitchWndHilite, die im Blogbeitrag erwähnt wurde. Sie wird von zwei Stellen aufgerufen: xxxMoveSwitchWndHilite und xxxPaintSwitchWindow. Letztere erregte sofort meine Aufmerksamkeit wegen der umgebenden GetKeyState/GetAsyncKeyState-Aufrufe, die im ursprünglichen Bericht erwähnt wurden. Außerdem prüfen diese Aufrufe, ob die ALT-Taste gedrückt ist.

Aufruf von DrawSwitchWndHilite aus xxxPaintSwitchWindow
Als ich den Aufruf-Querverweisen weiter folgte (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite), stellte ich fest, dass das erste Element in dieser Kette in InitFunctionTables referenziert wird, einer Funktion, die im Patch korrigiert wurde.
Als Nächstes habe ich mir NtUserMessageCall aus dem Screenshot des dekompilierten Codes angesehen.
Hier ist die Deklaration dieser Funktion.```cpp
NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)
Der Exploit ruft es mit `msg = 0x14` und `dwType = 0xE0` auf. Mal sehen, was es tut.```cpp
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);
Hier habe ich eine einfache Fensterklasse registriert und ein Fenster dieser Klasse erstellt. Dann habe ich NtUserMessageCall mit denselben Parametern wie beim Exploit aufgerufen. Um zu sehen, was unter der Haube passiert, habe ich einen Haltepunkt kd> ba e 1 win32k!NtUserMessageCall gesetzt und den Code ausgeführt.
Es gibt ziemlich viele Aufrufe dieser Funktion, daher musste ich den richtigen erwischen, aber das war nicht allzu schwierig – es war der mit einem sehr kurzen Callstack.

NtUserMessageCall
Beim Durchschreiten des Codes zeigte sich, dass eine Funktion aus dem gapfnMessageCall-Array aufgerufen wird. Der Index wird anhand des msg-Werts berechnet und ist gleich 0, daher erfolgt der Aufruf an NtUserfnDWORD.

NtUserfnDWORD
Der nächste Aufruf erfolgt über den dwType-Wert, und der gpsi-Offset ist nun 0x40. Der Aufruf führt zu xxxWrapSwitchWndProc (diese Funktion tauchte bereits auf, als ich die Aufrufkette von DrawSwitchWndHilite geprüft habe).
xxxWrapSwitchWndProc ruft einfach xxxSwitchWndProc auf.

xxxSwitchWndProc
Und hier ist Schluss: Der Code scheitert an dieser Stelle und geht nicht weiter zu xxxPaintSwitchWindow, wohin wir basierend auf dem msg-Wert (0x14) gelangen wollen. Schauen wir uns an, warum.