
CVE-2017-8570 Exp und Exploit-Sample-Analyse
Ursache: Beim Öffnen eines Office-Dokuments wird FLTLDR.EXE verwendet, um eine eingebettete EPS-Datei zu rendern, die die Schwachstelle enthält. Die Datei ist in der PostScript-Sprache verfasst und kann vom Angreifer durch eine "save-restore"-Operation ausgenutzt werden. Im Kern handelt es sich um eine UAF-Schwachstelle (Use-After-Free). Wenn ein Benutzer eine Datei mit einem fehlerhaften Grafikbild öffnet oder wenn ein Benutzer ein fehlerhaftes Grafikbild in eine Office-Datei einfügt, kann die Schwachstelle ausgenutzt werden.
Betroffene Versionen: Microsoft Office 2010 Service Pack 2, Microsoft Office 2013 Service Pack 1, Microsoft Office 2016
POC: kcufId's Github
Der Autor suchte lange im Internet, fand jedoch kein Office-Installationspaket, das EPSIMP32.FLT enthielt. Glücklicherweise stellte Meister kcufId ein LoadEps.exe zur Verfügung, um EPS-Dateien zu laden. Vielen Dank an Meister kcufId.
LoadEps.exe lädt zunächst EPSIMP32.FLT:

Anschließend wird ImportGr aufgerufen, um die EPS-Datei zu laden:

Hier direkt mit F7 folgen, dann kann man erfolgreich an den in EPSIMP32.FLT gesetzten Breakpoints anhalten.
Bevor wir zum eigentlichen Thema kommen, zuerst eine Erläuterung der PostScript-Objektstruktur.
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // wenn Array, Zeiger auf userdict, wo das Array-Objekt gespeichert ist
}ps_obj;
Die entsprechenden Werte für verschiedene type sind wie folgt:
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
Am Beispiel einer Zeichenkette wird deren Speicherstruktur erläutert. Wenn man einen Breakpoint auf die forall-Funktion setzt, kann man weiter sehen, wie sie mit Zeichenketten umgeht (wie man die forall-Funktion lokalisiert, siehe https://paper.seebug.org/368/).

Bild 1 entspricht ps_obj, dessen value2-Element auf den entsprechenden Eintrag in der Indexliste zeigt (Bild 2). Der Index-Eintrag zeigt auf eine Struktur der Größe 0x30, deren Offset 0x24 einen Zeiger auf einen Zeiger auf eine Struktur der Größe 0x28 speichert (Bild 5), und Offset 0x2C speichert die Größe der Zeichenkette (Bild 3). In der Struktur in Bild 5 speichert Offset 0x4 die Adresse des entsprechenden Eintrags in der Indexliste dieser Struktur (d.h. 0x01DB5E94 aus Bild 4), Offset 0x20 zeigt auf die endgültige Speicherposition der Zeichenkette (Bild 6), Offset 0x24 ist die tatsächlich belegte Speichergröße – Zeichenkettengröße + 1.
Struktur der Größe 0x30:
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct // Zeiger auf Zeiger auf Struktur der Größe 0x28
+0x28 dword
+0x2c dword size // tatsächliche Größe der Zeichenkette
Struktur der Größe 0x28 (bei einem Array hat diese Struktur die Größe 0x2C, und Offset 0x28 zeigt auf die Array-Elemente, wobei jedes Element ein ps_obj ist):
+0x0 dword
+0x4 dword // speichert die Adresse des entsprechenden Eintrags in der Indexliste dieser Struktur
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object // Zeiger auf die endgültige Speicherposition der Zeichenkette
+0x24 dword size // tatsächlich belegte Speichergröße, tatsächliche Größe der Zeichenkette + 1
Erstes Auslösen der Schwachstelle:

Zuerst wird der VM-Status in der Variablen l62 gespeichert. Danach wird für jedes Zeichen in der Variablen l63 der Verarbeitungsprozess l61 –>> l59 –>> l56 aufgerufen. l62 restore stellt den vorherigen Zustand wieder her. Dadurch wird der von l63 nach der Anweisung /l62 save def angeforderte Speicherplatz freigegeben und wird zu einem hängenden Zeiger.

Die Variablen l95-l99 bestimmen den weiteren Ablauf, ihre Werte sind alle 0 (d.h. 32-Bit):

Zweites Auslösen der Schwachstelle: Zuerst wird ein Speicherbereich der Größe 0x27 (tatsächlich belegt 0x28) angefordert, um l63 zu speichern:

Danach stellt l62 restore den vorherigen Zustand wieder her, was dazu führt, dass der für l63 angeforderte Speicher freigegeben wird und ein hängender Zeiger entsteht. Anschließend wird l100 ausgeführt. Der zuvor von l63 belegte Speicher wird nun verwendet, um die 0x28-Struktur der Zeichenkette l102 (d.h. l136) zu speichern (dies erklärt, warum l63 Speicher der Größe 0x27 anfordert):

Die Werte an den Offsets 0x4, 0x20 und 0x24 dieser Struktur werden abgerufen:

Schließlich wird der Inhalt der Zeichenkette l136 geändert (das Bild zeigt nur einen Teil der Änderungen):

Diese Änderungen sind sorgfältig konstruiert und werden beim dritten Auslösen der Schwachstelle verwendet.
Drittes Auslösen der Schwachstelle: Ein Array mit 0x37 Elementen wird angefordert. Danach wird beim Durchlauf bis zum 0x34. Element l62 restore ausgeführt:

Nach der Ausführung von restore wird die 0x30-Struktur des Arrays mit dem Inhalt der Zeichenkette l193 überschrieben:

Dadurch wird das Objekt, das beim letzten (0x36) forall-Prozess ausgeführt wird, zu der im obigen Bild gezeigten 0x30-Struktur. Und das Abrufen des 0x36. Elements führt zu der beim zweiten Auslösen sorgfältig konstruierten Zeichenkette:

Das abgerufene Array-Element ist ein Array der Größe 4, dessen erstes Element eine Zeichenkette mit Startadresse 0 und Größe 0x7FFFFFFF ist:

Dieses Array wird in der Variablen l159 gespeichert. Sein erstes Element – die Zeichenkette mit Startadresse 0 und Größe 0x7FFFFFFF – wird in der Variablen l201 gespeichert. Danach kann über die Variable l201 auf Werte an beliebigen Adressen zugegriffen werden.
Abrufen der Basisadresse von kernel32.dll:


Somit speichert die Variable l314 die Basisadresse von EPSIMP32.FLT.

Anmerkung: Die Syntax des search-Befehls ist wie folgt:

Suche nach dem angegebenen Gadget:


Konstruktion einer file-Typ-Struktur:
l199 l201 get_dword
/l487 exch def
l487 l201 get_dword
/l488 exch def
l488 36 my_add l201 get_dword
/l489 exch def
l489 l201 get_dword
/l490 exch def
l490 32 my_add l201 get_dword
/l491 exch def
l199 l491 l201 put_data_to_array
l199 12 my_sub 2304 l201 put_data_to_array

Schreiben von konstruierten Daten an die Adresse l492 (l491+0x32):
l492 0 l201 put_data_to_array %% 0x00 0
l492 4 my_add l375 l201 put_data_to_array %% 0x04 Adresse von <5E C3>
l492 8 my_add l373 l201 put_data_to_array %% 0x08 Adresse von <94 00 00 00 00 5E C3>
l492 12 my_add l377 l201 put_data_to_array %% 0x0C Adresse von <C2 0C 00>
l492 16 my_add l370 l201 put_data_to_array %% 0x10 Adresse von VirtualProtect()
l492 20 my_add 0 l201 put_data_to_array %% 0x14 0
l492 24 my_add 0 l201 put_data_to_array %% 0x18 0
l492 28 my_add 0 l201 put_data_to_array %% 0x1C 0
l492 32 my_add l368 l201 put_data_to_array %% 0x20 Adresse von Shellcode
l492 36 my_add l368 l201 put_data_to_array %% 0x24 Adresse von Shellcode——lpAddress
l492 40 my_add l349 l201 put_data_to_array %% 0x28 Größe von Shellcode——dwSize
l492 44 my_add 64 l201 put_data_to_array %% 0x2C PAGE_EXECUTE_READWRITE——flNewProtect
l492 48 my_add l493 l201 put_data_to_array %% 0x30 lpflOldProtect
Schließlich wird beim Ausführen der closefile-Anweisung zum Shellcode gesprungen:

Das EPS-Ausnutzungsskript befindet sich im Verzeichnis \word\media. Nach dem Entpacken ist es sichtbar. Die Ausnutzungsbeispiele dieser Schwachstelle sind abgesehen vom Shellcode-Teil im Wesentlichen identisch. Daher wird als Beispiel ein Sample einer Patchword-Organisation analysiert.
Dateiname: Cyber_Secure_Pakistan.docx
MD5: DD89BBB916A2C909630EC78CBB0E13E5
Springen zum Shellcode, Stack wiederherstellen:

Speicher anfordern:

Funktionsaufrufadressen abrufen:

Während des Debuggens konnte die Adresse der CreateToolhelp32Snapshot-Funktion aufgrund von Umgebungsproblemen möglicherweise nicht erfolgreich abgerufen werden:

Manuell die Adresse eintragen und Word öffnen, um die Analyse fortzusetzen. Prozesse aufzählen, nach WINWORD.exe suchen:

Im Verzeichnis C:\ProgramData\Microsoft\DeviceSync ein Programm namens MSBuild.exe erstellen:

Dateiinhalt schreiben. Der Inhalt ist in der payload_32-Variablen des EPS-Skripts gespeichert:


Erstellen der vmtools.dll-Datei:

Dateiinhalt schreiben. Der Inhalt ist in der payload_32_f2-Variablen des EPS-Skripts gespeichert:


Erstellen der VMwareCplLauncher.exe-Datei:

Ihr Inhalt ist in der payload_32_f1-Variablen des EPS-Skripts gespeichert:

Diese Datei ist eine mit dem Vmware-Signatur versehene weiße Datei:

Der folgende Inhalt wird in explorer.exe injiziert:

Die Funktion besteht darin, den VMwareCplLauncher.exe-Prozess zu erstellen:

Der weitere Ablauf wird in diesem Bericht von 360 erwähnt. Dieser Artikel behandelt den Analyseteil vorerst nicht:

Interessierte Leser können den Bericht weiterlesen.
Anmerkung: Die Ausnutzungsbeispiele dieser Schwachstelle sind weitgehend ähnlich. Der Unterschied liegt in der finalen MSBuild.exe-Nutzlast, die in der payload_32-Variablen des EPS-Skripts gespeichert ist. Sie kann direkt extrahiert werden. Nach dem Auffüllen des DOS-Dateikopfes kann sie in IDA geladen und analysiert werden.