MIT-Kurs "Computer Systems Security". Vorlesung 3: Pufferüberläufe: Exploits und Schutz, Teil 3

Massachusetts Institute of Technology. Vorlesung # 6.858. "Sicherheit von Computersystemen." Nikolai Zeldovich, James Mickens. 2014 Jahr


Computer Systems Security ist ein Kurs zur Entwicklung und Implementierung sicherer Computersysteme. Die Vorträge behandeln Bedrohungsmodelle, Angriffe, die die Sicherheit gefährden, und Sicherheitstechniken, die auf jüngsten wissenschaftlichen Arbeiten basieren. Zu den Themen gehören Betriebssystemsicherheit, Funktionen, Informationsflussmanagement, Sprachsicherheit, Netzwerkprotokolle, Hardwaresicherheit und Sicherheit von Webanwendungen.

Vorlesung 1: „Einführung: Bedrohungsmodelle“ Teil 1 / Teil 2 / Teil 3
Vorlesung 2: „Kontrolle von Hackerangriffen“ Teil 1 / Teil 2 / Teil 3
Vorlesung 3: „Pufferüberläufe: Exploits und Schutz“ Teil 1 / Teil 2 / Teil 3

Sie können die Technik des Erraten des "Kanarienvogels" für Ihre eigenen Zwecke verwenden, um das Vorhandensein von "schwachen" Bits in Bezug auf die Auswahl herauszufinden. Das heißt, wenn Sie richtig geraten haben, wird der Server neu gestartet, und dies dient als Signal für Sie, dass der eingestellte Wert recht einfach zu erraten ist. Somit ist es möglich, die zufälligen „Kanarienvögel“ zu besiegen, vorausgesetzt, dass sich ihr Wert nach dem Neustart des Servers nicht ändert. Sie können auch Gadgets verwenden, um eine verwandte Folge mehrerer Angriffe zu implementieren.

Als nächstes werden wir uns eine produktivere Methode ansehen, mit der Sie alle diese Methoden verwenden können, um die Verhinderung der Datenausführung, zufällige Adressräume und „Kanarienvögel“ zu umgehen.

Wenden wir uns 64-Bit-Architekturen anstelle von 32-Bit-Architekturen zu. Die ersteren eignen sich besser für die Randomisierung, sodass Sie viel mehr „Chance“ haben, sich gegen einen Hacker zu verteidigen. Und diese Systeme sehen im Hinblick auf die Angriffsbildung viel interessanter aus.

Diese Art der 64-Bit-Architektur wurde auch aus Sicht von BROP , der "blinden" rückwärtsorientierten Programmierung, betrachtet. Der Einfachheit halber nehmen wir an, dass der einzige Unterschied zwischen 64-Bit- und 32-Bit-Maschinen darin besteht, dass auf einer 64-Bit-Maschine Argumente an die Register und für eine 32-Bit-Maschine an den Stapel übergeben werden.



Wenn eine Funktion mit der Ausführung beginnt, wird nach bestimmten Registern gesucht, um herauszufinden, wo sich die Argumente befinden.

Kommen wir nun zum Kern des heutigen Vortrags - was ist Blind Return-orientiertes Programmieren oder BROP? Das erste, was wir tun werden, ist ein Stopp-Gadget zu finden. Denken Sie daran, dass wir mit "Gadget" im Wesentlichen Absenderadressen meinen. Das Gadget wird mit der Absenderadresse identifiziert, der Startadresse der Befehlsfolge, zu der wir gehen möchten. Was ist ein Stopp-Gadget?

Im Wesentlichen ist dies die Absenderadresse an einer Stelle im Code. Wenn Sie jedoch dorthin springen, halten Sie das Programm einfach an, ohne dass das Programm abstürzt. Aus diesem Grund wird dies als Stopp-Gadget bezeichnet.

Sie könnten an eine Stelle im Code springen, die dann einen Aufruf des Schlafsystems oder Pausen oder ähnliches auslöst. Es ist möglich, dass das Programm in einer Endlosschleife „hängen bleibt“, wenn Sie an diesen Ort springen. Es spielt keine Rolle, warum der Stopp auftritt, aber Sie können sich einige Szenarien vorstellen, die dazu führen würden.

Was nützt ein Stopp-Gadget?

Sobald es dem Angreifer gelungen ist, den „Kanarienvogel“ mithilfe der interaktiven Technik des Erraten von Bits zu besiegen, kann er beginnen, diese Absenderadresse und Ret-Adresse neu zu schreiben und das Stopp-Gadget zu „tappen“. Beachten Sie, dass die meisten zufälligen Adressen, die Sie auf dem Stapel platzieren können, wahrscheinlich einfach zum Absturz des Servers führen. Auch diese Nachricht ist für Sie, den Angreifer, ein Hinweis darauf, dass das, was Sie gefunden haben, kein Stopp-Gadget ist. Denn wenn der Server abstürzt, wird Ihr Socket geschlossen und Sie als Angreifer verstehen, dass Sie nicht auf das Stopp-Gadget gekommen sind. Aber wenn Sie etwas erraten haben und die Steckdose danach noch eine Weile offen bleibt, denken Sie: "Ja, ich habe dieses Stopp-Gadget gefunden!" Die Grundidee des ersten Schritts besteht also darin, dieses Stopp-Gadget zu finden.



Schritt zwei ist, dass Sie Gadgets suchen möchten, die Stapeleinträge mit dem Befehl pop löschen. Verwenden Sie daher diese Abfolge sorgfältig gestalteter Anweisungen, um herauszufinden, wann Sie eines dieser Stapel-Gadgets greifen. Diese Sequenz besteht aus der Sondenadresse der Sondenadresse , der Stoppadresse der Stoppadresse und der Systemfehleradresse der Absturzadresse .

Daher wird die Sondenadresse auf den Stapel gelegt. Dies ist die Adresse des potenziellen Gadgets zum Löschen des Stapels, Stoppadresse - dies haben wir im ersten Schritt berücksichtigt, dies ist die Adresse des Stopp-Gadgets. Die Absturzadresse ist dann einfach die Adresse des nicht ausführbaren Codes. Hier können Sie einfach die Nulladresse (0 x 0) eingeben. Wenn Sie die Ret- Funktion auf diese Adresse anwenden und versuchen, den Code dort auszuführen, führt dies zu einem Programmabsturz.



Wir können diese Arten von Adressen verwenden, um herauszufinden, wo diese Gadgets den Stack-Popping-Stack bereinigen.

Ich werde ein einfaches Beispiel geben. Angenommen, wir haben zwei verschiedene Sondenbeispiele , die Fallenfalle und die Stoppfalle . Angenommen, wir werden mit Hilfe der Sonde eine Adresse "prüfen". Angenommen, sie beginnt mit 4 und endet mit acht: 0x4 ... 8, und dahinter befindet sich die nächste Adresse der Form 0x4 ... C. Hypothetisch können wir annehmen, dass eine dieser beiden Adressen die Adresse des Stack-Popping- Gadgets ist.

Die Trap-Trap erhält die Nulladressen 0x0 und 0x0 und lässt das Stop- Gadget- Stop beliebige Adressen wie 0xS ... 0xS ... haben, das spielt keine Rolle. Dieses Stopp-Gadget zeigt auf den Code sleep (10), wodurch das Programm angehalten wird.

Beginnen wir mit der Sondenoperation , bei der ein Register gelöscht und in der folgenden Reihenfolge zurückgegeben wird: pop rax; ret . Was wird passieren? Wenn das System zu dieser Adresse springt, bewegt sich der Stapelzeiger in die Mitte unseres Gadgets. Was wird das Gadget hier tun? Richtig, führen Sie eine Pop-Rax- Operation durch.



Und dann folgt ret , wodurch die Funktion in die oberste Zeile des Gadgets verschoben wird, dh gestoppt wird , und die Funktion stoppt, ohne das gesamte Programm zum Absturz zu bringen. Mit diesem Gadget kann ein Angreifer also sagen, dass die Testadresse zu einer dieser Funktionen gehört, wodurch der Stapel gelöscht wird, da die Server-Client-Verbindung offen ist.

Nehmen wir nun an, dass die zweite Sondenadresse für ein Register auf etwas wie xor rax, rax, ret zeigte .



Was passiert also, wenn wir versuchen, zu diesem Gadget zu springen? Beachten Sie, dass nichts auf dem Stapel gelöscht wird, sondern nur der Inhalt der Register geändert wird. Wir werden also zu der oben angegebenen 0x0-Adresse zurückkehren. Dies führt zum Absturz des Systems. Die Client-Verbindung zum Server wird unterbrochen, und der Hacker wird verstehen, dass dies kein Gadget zum Löschen des Stapelstapels ist.

Auf diese Weise können Sie eine bizarrere Reihe von Fallen verwenden und Gadgets stoppen. Sie können beispielsweise zwei Elemente auf dem Stapel löschen. Dazu müssen Sie hier nur eine weitere Trap- Anweisung einfügen. Wenn dieses Gadget nicht zwei Elemente löscht, befinden Sie sich in einer dieser Traps, und der Code wird nicht mehr ausgeführt. Die Materialien für die Vorlesung beschreiben ein sogenanntes BROP-Gadget, das nützlich ist, wenn Sie nicht zur Programmierung zurückkehren möchten . Aber heute werde ich Ihnen auch sagen, wie Sie diese einfachen Pop- Gadgets verwenden können, um einen ähnlichen Angriff zu starten. Sobald Sie dies verstanden haben, wird der Umgang mit einem BROP-Gadget viel einfacher.

Aber verstehen Sie alle, wie wir die Sondenfunktion für diese Geräte verwenden können? Angenommen, Sie finden die Position der Codefragmente, mit denen Sie den Stapel mit der Pop- Funktion löschen und ein Element daraus entfernen können. Sie wissen jedoch nicht genau, in welchem ​​Register diese Pop- Funktion funktioniert. Sie wissen nur, dass sie bereits zur Hinrichtung bereit ist. Sie müssen jedoch wissen, in welchem ​​Register diese Pop- Gadgets funktionieren, da in einer 64-Bit-Architektur die Register steuern, wo sich die Argumente der Funktion befinden, die Sie aufrufen möchten.

Unser Ziel ist es daher, Gadgets zu erstellen, mit denen wir die Werte löschen können, die wir in bestimmte Stapelregister eingegeben haben, und schließlich einen Systemaufruf-Systemaufruf initiieren, mit dem wir etwas Schlechtes tun können.
Jetzt müssen wir bestimmen, welche Register von Pop- Gadgets verwendet werden.



Dazu können wir den Systemaufruf pausieren. Wenn der Systemaufruf angehalten wird, wird die Ausführung des Programms angehalten und es werden keine Argumente akzeptiert. Und das bedeutet, dass das System alles in den Registern ignoriert.

Um die Pause-Anweisung zu finden, die wir ausführen, können Sie alle diese Pop- Gadgets verknüpfen, damit wir sie alle auf den Stapel legen können, und zwischen jedem von ihnen fügen wir eine Syscall-Nummer ein, um anzuhalten. Dann werden wir sehen, ob wir das Programm auf diese Weise "aussetzen" können. Lassen Sie mich Ihnen ein konkretes Beispiel geben.

Hier fügen wir ein Gadget in die Rücksendeadressenleiste ein, das die RDI- Register löscht und die Ret- Funktion auf sie anwendet. Darüber setzen wir eine Syscall-Nummer, um anzuhalten.

Angenommen, wir haben ein weiteres Gadget darüber, das die Pop- Funktion in einem anderen Register implementiert, z. B. RSI , und dann ret . Und dann setzen wir die Syscall-Nummer erneut, um anzuhalten. Und wir tun dies für alle Gadgets, die wir gefunden haben, und landen dann oben auf dem Stapel mit der mutmaßlichen Adresse für syscall .



Erinnern Sie sich noch einmal daran, wie Sie diese Systemaufrufe verwenden. Sie müssen die Syscall-Nummer in das RAX- Register eintragen und dann die libc-Syscall- Funktion aufrufen, die den angeforderten Systemaufruf ausführen soll.

Was passiert also, wenn wir diesen Code ausführen? Wir werden hierher kommen, zur Ret-Adresszeile , wir werden zur Adresse dieses Gadgets springen. Es sollte beachtet werden, dass der Angreifer weiß, dass dieses Gadget auf der rechten Seite etwas vom Stapel entfernt, aber noch nicht weiß, in welchem ​​Register es sich befindet.

Was passiert also, wenn wir zur Ret-Adresse springen? Er wird die Pop- Funktion verwenden, um den Systemaufruf in einem Register anzuhalten , das dem Angreifer nicht bekannt ist, und dann werden wir diese Kette von Operationen weiter den Stapel hinaufklettern.

Wir hoffen, dass eines dieser Gadgets die Pop- Funktion für die Syscall-Nummer im entsprechenden RAX- Register ausführt. Bis wir hier oben auf dem Stapel ankommen und alle Register "durcheinander bringen", die eine Syscall-Nummer zum Anhalten haben, hoffen wir, dass wir noch ein Register haben, das korrekt sein sollte. Denn wenn eines unserer Geräte dies tut und wir dann nach einer Weile nach oben zurückkehren, werden wir eine Pause einlegen, wenn wir ret ausführen. Die Pause dient erneut als Signal an den Angreifer. Denn wenn diese angenommene Adresse für syscall falsch war, schlägt das Programm fehl.

Was erlaubt uns also, diese Angriffsphase durchzuführen? Wir wissen immer noch nicht, in welchen Registern sich welche Pop- Gadgets befinden, aber wir wissen, dass eines davon den RAX freigibt , den wir steuern möchten. Und wir kennen wahrscheinlich die Syscall- Adresse, weil wir es geschafft haben, das System anzuhalten .

Sobald wir dies getan haben, können wir diese Gadgets einzeln überprüfen und herausfinden, welches das System pausiert. Mit anderen Worten, wir schneiden alles zwischen der Ret-Adresszeile und der Oberseite des Stapels ab, um direkt zum Systemaufruf zu gelangen . Wir werden prüfen, ob eine Pause oder ein Systemfehler aufgetreten ist. Wenn ein Fehler auftritt, identifizieren wir das Gadget, das ihn verursacht hat. Beispiel: In der unteren Zeile rechts befindet sich pop rdi . Werde ihn los und versuche den nächsten. Geben Sie hier in die Zeile über der Ret-Adresse die tatsächliche Adresse für syscall ein . Könnten wir das Programm anhalten? Ja, das heißt, wir haben gelernt, dass dieses Pop- Gadget RAX freigeben sollte. Das ist klar?

Teilnehmerin: Eine Möglichkeit, die Adresse für einen Systemaufruf zu erraten, ist also nur eine blinde Übertragung von Gadgets?

Professor: Ja, und es gibt Möglichkeiten, diesen Prozess in den Vorlesungsmaterialien zu optimieren, wenn Sie die PLT- Dateierweiterung und ähnliche Dinge verwenden. Mit dem einfachen Angriff, den ich beschrieben habe, fügen Sie einfach die Adresse hier ein und stellen sicher, ob sie eine Pause verursacht hat oder nicht. Als Ergebnis des Tests ermitteln wir den Standort von syscall . Wir werden herausfinden, wo sich die Anweisung befindet, die Pop RAX ausführt. Wir brauchen auch Gadgets, die Pop auch in einigen anderen Registern ausführen. Sie können ähnliche Tests für sie durchführen. Verwenden Sie daher anstatt die Syscall-Nummer anzuhalten , push für einen anderen Befehl, der beispielsweise die Argumente RAX und RDI verwendet .

Sie können also die Tatsache nutzen, dass es für einen bestimmten Satz von Registern, die Sie steuern möchten, eine Art Systemaufruf gibt, der einem Angreifer ein Signal gibt, um herauszufinden, ob Sie ihn erfolgreich gebrochen haben oder nicht. Am Ende dieser Phase haben Sie also die Syscall- Adresse und die Gadget-Heap-Adresse, mit denen Sie beliebige Register einfügen können.

Und jetzt gehen wir zum vierten Schritt über, der als Schreibaufzeichnung bezeichnet wird . Der vierte Schritt ist das Aufzeichnen eines Systemaufrufs. Um write aufrufen zu können, benötigen wir die folgenden Gadgets:

pop rdi, ret;
pop rsi, ret;
pop rdx, ret;
Pop Rax, ret;
syscall



Wie werden diese Register von einem Systemaufruf verwendet? Der erste ist der Socket oder allgemeiner der Dateideskriptor, den Sie zum Schreiben übertragen möchten. Der zweite ist der Puffer, der dritte ist die Länge dieses Puffers, der vierte ist die Systemrufnummer und der fünfte ist der Systemaufruf selbst.

Wenn wir also alle diese Gadgets finden, können wir die Werte steuern, die in die Argumente eingebettet sind, die wiederum in diesen Registern abgelegt werden, weil wir sie einfach auf den Stapel „geschoben“ haben.

Was soll eine Steckdose sein? Hier müssen wir ein wenig raten. Sie können die Tatsache nutzen, dass Linux die Anzahl der gleichzeitig geöffneten Verbindungen für eine Datei begrenzt, die einen Wert von 2024 erreicht. Außerdem sollte dies das Minimum aller verfügbaren sein.

Ich frage mich, was wir in den Pufferzeiger einfügen werden. Das ist richtig, wir beabsichtigen, das Textfragment des Programms hier zu verwenden, wir werden es irgendwo im Programmcode in einen Zeiger setzen. Dies ermöglicht es uns, die Binärdatei mit dem richtigen Client-Aufruf vom Socket aus dem Speicher zu lesen. Dann kann der Angreifer diese Binärdatei nehmen, offline offline analysieren und mithilfe von GDB oder einem anderen Tool herausfinden, wo sich all dies befindet. Der Angreifer weiß, dass jetzt jedes Mal, wenn der Server "abstürzt", die gleichen zufälligen Dinge darin gespeichert werden. Nachdem ein Angreifer nun die Adressen und Offsets für den Inhalt des Stapels ermittelt hat, kann er diese Gadgets direkt angreifen. Es kann andere Schwachstellen direkt angreifen, herausfinden, wie die Shell geöffnet werden kann und dergleichen. Mit anderen Worten, an dem Ort, an dem Sie die Binärdatei mit dem Hacker versehen haben, wurden Sie besiegt.

So funktioniert der BROP- Angriff. Wie gesagt, in den Vorlesungsmaterialien gibt es viele Möglichkeiten, diese Prozesse zu optimieren, aber zuerst müssen Sie das Hauptmaterial verstehen, sonst verliert die Optimierung ihre Bedeutung. Daher können Sie mit mir einzeln oder nach dem Unterricht über Optimierung sprechen.

Im Moment genügt es zu sagen, dass dies die Grundlagen dafür sind, wie Sie einen BROP- Angriff starten können . Sie müssen das Stopp- Gadget finden, die Gadgets finden, die die Funktion von Pop- Stack-Einträgen ausführen, herausfinden, in welchen Registern sie sich befinden, wo sich syscall befindet , und dann basierend auf den erhaltenen Informationen den Schreibvorgang einleiten.



Gehen Sie also schnell das Thema durch, wie verteidigen Sie sich gegen BROP ? Das offensichtlichste, was Sie haben, ist die Re-Randomisierung. Da die Tatsache, dass "heruntergefallene" Server nicht wieder erscheinen und keine zufälligen Versionen von sich selbst erstellen, als Signal fungiert, das einem Angreifer die Möglichkeit gibt, verschiedene Hypothesen über die Funktionsweise von Programmen zu testen.

Eine einfache Möglichkeit, sich zu schützen, besteht darin, bei der Wiederbelebung Ihres Prozesses sicherzustellen, dass Sie Exec anstelle von Fork ausführen. Denn wenn Sie den Prozess ausführen, erstellen Sie einen völlig neuen, zufällig angeordneten Speicherplatz, zumindest geschieht dies unter Linux. Wenn Sie unter Linux mit PIE , Position Independent Execution , einer standortunabhängigen ausführbaren Datei, kompilieren, erhalten Sie bei Verwendung von exec nur einen neuen zufälligen Adressraum.

Der zweite Weg ist einfach die Verwendung von Windows, da dieses Betriebssystem im Grunde nicht das Äquivalent einer Gabelfunktion hat. Dies bedeutet, dass ein Server unter Windows unter Windows immer einen neuen zufälligen Adressraum hat.

Jemand hier fragte, was passieren würde, wenn er nach einer Serverstörung die Verbindung nicht trennen würde. Wenn wir also während eines Serverabsturzes diesen Fehler irgendwie „abfangen“ und die Verbindung für eine Weile offen halten, können wir den Angreifer verwirren, sodass er kein Signal über den Fehler erhält und denkt, dass er die richtige Adresse gefunden hat.

In diesem Fall wird Ihr BROP- Angriff zu einem DOS- Angriff. Weil du gerade alle möglichen Zombie-Prozesse hast, die es gibt. Sie sind nutzlos, aber Sie können sie nicht weiter gehen lassen, sonst müssen Sie diese Informationen löschen.

Eine andere Sache, an die Sie denken könnten, ist die Grenzkontrolle, über die wir zuvor gesprochen haben. Die Materialien der Vorlesung besagen, dass diese Methode unproduktiv ist, da sie doppelt so teuer ist, aber Sie sie trotzdem verwenden könnten.

So funktioniert BROP . Bei den Hausaufgaben wird dort eine heikle Frage gestellt: Was ist, wenn Sie einen Hash der aktuellen Zeit verwenden? Das heißt, die Zeitspanne, in der Sie das Programm neu starten. Reicht das aus, um diese Art von Angriff zu verhindern? Beachten Sie, dass Sie durch Hashing nicht auf magische Weise Entropiebits erhalten, wenn die Daten, die Sie in den Hash eingeben, leicht vorhersehbar sind. Wenn Ihr Hash Milliarden von Bits enthält, spielt das keine Rolle. Aber wenn Sie dort nur ein paar Bedeutungen haben, wird der Angreifer sie nur erraten. Natürlich ist es besser, einen zufälligen Zeit-Hash zu verwenden, als nichts zum Schutz vor einem Hacker zu verwenden, aber dies bietet Ihnen nicht die Sicherheit, auf die Sie zählen sollten.


Die Vollversion des Kurses finden Sie hier .

Vielen Dank für Ihren Aufenthalt bei uns. Gefällt dir unser Artikel? Möchten Sie weitere interessante Materialien sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder sie Ihren Freunden empfehlen. Habr-Benutzer erhalten 30% Rabatt auf ein einzigartiges Analogon von Einstiegsservern, das wir für Sie erfunden haben: Die ganze Wahrheit über VPS (KVM) E5-2650 v4 (6 Kerne) 10 GB DDR4 240 GB SSD 1 Gbit / s $ 20 oder wie teilt man den Server? (Optionen sind mit RAID1 und RAID10, bis zu 24 Kernen und bis zu 40 GB DDR4 verfügbar).

Dell R730xd 2 mal günstiger? Nur wir haben 2 x Intel Dodeca-Core Xeon E5-2650v4 128 GB DDR4 6 x 480 GB SSD 1 Gbit / s 100 TV von 249 US-Dollar in den Niederlanden und den USA! Lesen Sie mehr über den Aufbau eines Infrastrukturgebäudes. Klasse mit Dell R730xd E5-2650 v4 Servern für 9.000 Euro für einen Cent?

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


All Articles