
ChakraCore-Exploitationstechniken
CVE-2016-7190 [0] ist ein Heap-Overflow in der Array.map()-Funktion von ChakraCore, der es erlaubt, angrenzenden Speicher zu überschreiben. Die Hauptidee, um beliebigen Lese-/Schreibzugriff zu erlangen, besteht darin, zuerst eine Anzahl aufeinanderfolgender JavaScript-Integer-Arrays zu allozieren und den Overflow auszunutzen, um die Größe eines Arrays zu manipulieren. Dann nutzen wir dieses Array, um die Basisadresse eines Uint8Array auf die Adresse zu ändern, die wir lesen/schreiben möchten. Der Grund für diesen zusätzlichen Schritt ist, dass der Overflow auf das 2-fache der Größe des überlaufenen Integer-Arrays begrenzt ist.
Diese Schwachstelle eignet sich sehr gut zum Testen verschiedener Exploitation-Strategien, da sie leicht in aktuelle Versionen von ChakraCore wieder eingeführt werden kann (siehe undo-cve-2016-7190.patch).
In diesem Beispiel kapern wir den Kontrollfluss, indem wir den vtable-Zeiger eines Uint8Array-C++-Objekts auf eine kontrollierte Speicheradresse überschreiben und eine Funktion des Uint8Array-Objekts aufrufen, die zur Ausführung einer virtuellen Funktion führt.
Control-Flow Integrity (CFI) ist eine Verteidigungstechnik zur Abschwächung von Kontrollfluss-Hijacking-Angriffen. Die allgemeine Idee von CFI ist, ein Kontrollfluss-Diagramm (CFG) einer Anwendung während der Kompilierung zu berechnen und die Anwendung dann mit Laufzeitprüfungen zu instrumentieren, um sicherzustellen, dass der Kontrollfluss zur Laufzeit nicht von dem statisch berechneten CFG abweicht. Falls eine Anwendung, wie ein Webbrowser, jedoch dynamische Codegenerierung unterstützt, muss das CFG zur Laufzeit erweiterbar sein. Dies bringt eine Reihe von Herausforderungen mit sich:
Während unserer Forschung konzentrierten wir uns auf die letzte Herausforderung. Die Hauptidee ist, die Eingabe (Daten) des Just-in-Time (JIT)-Compilers zu manipulieren. Infolgedessen würde der JIT-Compiler bösartigen Code generieren, der dann in den aktuellen Kontext integriert würde – und sogar mit CFI gehärtet wäre. Parallel zu unserer Forschung zeigte theori [4], wie man CFI durch Manipulation der Ausgabe des JIT-Compilers umgehen kann. Dieser Angriff wird durch die Überprüfung der Integrität der Ausgabe mittels einer Prüfsumme entschärft. Dies kann unseren Angriff nicht stoppen, da wir die Eingabe des JIT-Compilers manipulieren. ALLERDINGS werden beide Angriffe durch die Auslagerung der JIT-Kompilation in einen anderen Prozess (auch bekannt als Arbitrary Code Guard [3]) entschärft.
Wir demonstrieren, dass ein Angreifer einen Data-only-Angriff gegen den JIT-Compiler ausnutzen kann, um beliebigen nativen Code zu erzeugen. Insbesondere modifizieren wir die Zwischendarstellung (IR) des JIT-Compilers, um vom Angreifer kontrollierte Anweisungen einzuschleusen. Wenn der JIT-Compiler dann den nativen Code basierend auf der modifizierten IR generiert, wird er vom Angreifer kontrollierten nativen Code erzeugen.
Zwischen dem Zeitpunkt der ursprünglichen Durchführung der Forschung und heute wurde die IR von ChakraCore geändert. Wir haben uns nicht die Mühe gemacht, den Angriff zu portieren. Um mit diesem Angriff herumzuspielen, verwenden Sie daher bitte die folgenden Binärdateien.
Während der Analyse von ACG [3] bemerkten wir unter anderem [1,2], dass globale schreibgeschützte Daten, die im .mrdata-Abschnitt gespeichert sind, angepasst werden müssen, um den dynamisch generierten Code zu integrieren. Dies geschieht durch die Funktion LdrProtectMrdata(), die einen Lock verwendet, um die Thread-Sicherheit zu gewährleisten. Allerdings wird der .mrdata-Abschnitt zuerst als beschreibbar neu abgebildet, bevor der Lock erworben wird. Interessanterweise enthält der .mrdata-Abschnitt die Basisadresse und Größe des .mrdata-Abschnitts, die später verwendet werden, um diesen Abschnitt wieder als schreibgeschützt abzubilden.
Ein Angreifer kann dies ausnutzen, um ein Angriffs-Primitiv zu erstellen, das das erneute Abbilden von schreibgeschütztem Speicher als beschreibbar ermöglicht. Daher führt der Angreifer die folgenden Schritte aus:
Infolgedessen blieb der .mrdata-Abschnitt beschreibbar. Um beliebigen Speicher als beschreibbar abzubilden, muss der Angreifer lediglich die Basisadresse des .mrdata-Abschnitts ändern und dann die obigen Schritte wiederholen.
Die Konsequenz dieses Angriffs ist, dass zuvor vertrauenswürdige Daten, d.h. Daten, die als schreibgeschützt abgebildet sind, unvertrauenswürdig werden. In unserem Proof-of-Concept nutzen wir dieses Primitiv aus, um den Control-flow Guard (CFGuard) zu umgehen, indem wir den Zeiger auf die Verifikationsfunktion von CFGuard ändern, die in _guard_dispatch_icall_fptr gespeichert ist. Der Effekt kann beobachtet werden, indem man den Debugger anhängt und die Variable bypass_cfguard ändert.
[0] https://bugs.chromium.org/p/project-zero/issues/detail?id=923
[1] http://alex-ionescu.com/publications/euskalhack/euskalhack2017-cfg.pdf
[2] https://sites.google.com/site/bingsunsec/dataonlyattack
[3] https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/
[4] http://theori.io/research/chakra-jit-cfg-bypass