SlideShare une entreprise Scribd logo
1  sur  60
Télécharger pour lire hors ligne
Studienarbeit



Stack- und Heap-Overflow-Schutz
                     bei
Windows XP und Windows Vista




        Fachbereich Informationstechnik

Studiengang Softwaretechnik und Medieninformatik




                Semester: SWT6

          Name: Johannes Hohenbichler



               Datum: 20.08.2008

                 Version: 1.03

           Betreuer: Dominik Schoop
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



     I      Inhaltsverzeichnis
I        Inhaltsverzeichnis ......................................................................................................................... 1-2
II       Abbildungsverzeichnis .................................................................................................................. 1-4
III      Abkürzungsverzeichnis ................................................................................................................. 1-5
IV       Toolsverzeichnis ........................................................................................................................... 1-6
V        Besondere Darstellungsformen.................................................................................................... 1-7
1        Überblick ...................................................................................................................................... 1-8
2        Grundlagen – Systemarchitektur................................................................................................ 2-10
      2.1       Speicherorganisation .......................................................................................................... 2-10
         2.1.1          Virtueller Speicher ...................................................................................................... 2-10
         2.1.2          Systemfunktionen....................................................................................................... 2-11
         2.1.3          Speichersegmente ...................................................................................................... 2-13
         2.1.4          Big- und Little-Endian ................................................................................................. 2-15
3        Grundlagen – Buffer Overflows .................................................................................................. 3-16
      3.1       Buffer Overflows ................................................................................................................ 3-16
         3.1.1          Off-By-One Buffer Overflows ..................................................................................... 3-17
         3.1.2          Mit Buffer Overflows den Kontrollfluss ändern ......................................................... 3-18
         3.1.3          Ausführen von beliebigem Code ................................................................................ 3-21
         3.1.4          Klassische Stack-basierende Angriffe ......................................................................... 3-22
         3.1.5          Exception Handler-basierende Angriffe ..................................................................... 3-25
         3.1.6          Heap-basierende Angriffe .......................................................................................... 3-27
      3.2       Shellcode und Payload ....................................................................................................... 3-29
         3.2.1          Anforderungen an Shellcode ...................................................................................... 3-29
         3.2.2          Besonderheiten von Windows-Shellcode .................................................................. 3-30
         3.2.3          Was möglich ist........................................................................................................... 3-30
         3.2.4          Abstraktes Beispiel: Portbind Shellcode für Windows ............................................... 3-31
4        Das Sicherheits-Ensemble von Windows und seine Schwachstellen ........................................ 4-32
      4.1       Das Windows-Zwei-Schichten-Modell................................................................................ 4-32
      4.2       Zugriffsrechte, Authentifizierung und Access Token.......................................................... 4-33
         4.2.1          Rechte von Prozessen unter Windows ....................................................................... 4-34
         4.2.2          Windows Service Hardening....................................................................................... 4-35
         4.2.3          Rechte von Prozessen unter Windows Vista: UAC ..................................................... 4-35


                                                                        Seite - 2 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



    4.3      PatchGuard ......................................................................................................................... 4-37
    4.4      Process Exection Block (PEB) Randomization..................................................................... 4-37
    4.5      Heap-Schutz........................................................................................................................ 4-38
    4.6      Visual Studio 2005 - Der "Windows-Compiler" .................................................................. 4-40
      4.6.1          Standard Annotation Language (SAL) ......................................................................... 4-40
      4.6.2          Optimierte Anordnung von Stack-Elementen (O2) .................................................... 4-42
      4.6.3          Puffer-Sicherheitsüberprüfung (GS) ........................................................................... 4-43
      4.6.4          Sichere Ausnahmebehandlung (SafeSEH) .................................................................. 4-47
      4.6.5          Data Execution Prevention (DEP) ............................................................................... 4-48
      4.6.6          Address Space Layout Randomization (ASLR) ............................................................ 4-51
    4.7      Abschließender Vergleich: XP gegenüber Vista ................................................................. 4-52
5     Fazit ............................................................................................................................................ 5-54
A     Literaturverzeichnis .................................................................................................................... 5-56
B     Quellcode ................................................................................................................................... 5-60




                                                                      Seite - 3 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



  II Abbildungsverzeichnis

Abbildung 2-1: 32-Bit-Windows Standard-Adresslayout nach [ 2 ] .................................................. 2-11
Abbildung 2-2: Prinzipielles Windows Prozess-Speicher-Layout nach [ 3 S. 182 ] und [ 4 S. 397 ] .. 2-13
Abbildung 2-3: Big-Endian im Speicher .............................................................................................. 2-15
Abbildung 2-4: Little-Endian im Speicher ........................................................................................... 2-15
Abbildung 3-1: Die Buffer buf0 und buf1 im Speicher ....................................................................... 3-18
Abbildung 3-2: Buffer Overflow von password_buffer in auth_flag .................................................. 3-21
Abbildung 3-3: Beispielhaftes Stackframe unter Windows allgemein ............................................... 3-22
Abbildung 3-4: Angriffsvektor ............................................................................................................ 3-23
Abbildung 3-5: Beispielhaftes Stackframe unter Verwendung von SEH [ 13 ] .................................. 3-27
Abbildung 4-1: Windows-Zwei-Schichten-Modell [ 4 ] ...................................................................... 4-33
Abbildung 4-2: Optimierte Anordnung von Stack-Elementen bei VS 2003 ....................................... 4-43
Abbildung 4-3: Beispielhaftes Stacklayout unter Verwendung von /GS bei VS 2005 [ 32 ] .............. 4-45




                                                                 Seite - 4 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



 III Abkürzungsverzeichnis



ASLR    Address Space Layout Randomization

BS      Betriebssystem

DACL    Discretionary Access Control List

DEP     Data Execution Protection, auch bekannt als NX

DoS     Denial of Service

Dll     Dynamic Link Library

EH      Exception Handler

GByte   Gigabyte

IE      Internet Explorer

LSA     Local Security Authority Subsystem Service

NX      No eXecute, auch bekannt als DEP

RET     Returnaddress

SAL     Standard Annotation Language

SEH     Structured Exception Handling

SFP     Saved Frame Pointer

SID     Security Identifier

VS      Visual Studio




                                            Seite - 5 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



 IV Toolsverzeichnis

Programm              Bezugsmöglichkeit

Windows power shell   http://www.microsoft.com/windowsserver2003/technologies/management/power
                      shell/default.mspx

Strawberry Perl       http://strawberryperl.com/

Process Explorer      http://technet.microsoft.com/de-de/sysinternals/default(en-us).aspx

Display Heap          Windows Server 2003 Resource Kit Tools
                      http://support.microsoft.com/kb/168609/de

Global Flags          Debugging Tools for Windows
                      http://www.microsoft.com/whdc/devtools/debugging/default.mspx




                                            Seite - 6 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



 V Besondere Darstellungsformen
In dieser Arbeit kommen im Wesentlichen vier spezielle Gestaltungselemente zur Anwendung



1. Begriffserklärungen und Hinweise sind in einer Box der folgenden Art untergebracht


  Begriffserklärungen und Hinweise
  Begriff: Beschreibung oder Definition des Begriffs bzw. Hinweistext




2. Kommandos

Kommandos die auf der Kommandozeile eingegeben werden (cmd.exe)
sind grau hinterlegt



3. Programmausgaben

Programmausgaben
sind orange hinterlegt



4. Quellcode / Ausschnitte aus Dateien

Auszüge aus Quellcode / Konfigurationsdatein
sind hellblau hinterlegt




                                             Seite - 7 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



1 Überblick
Ausgangssituation

In der Vergangenheit waren Buffer-Overflows eines der Hauptsicherheitsrisiken für Software. In den
letzten Jahren haben Softwarehersteller an diesem Problem intensiv gearbeitet und den Schutz stark
erhöhen können. Das beweisen nicht zuletzt CVE1-Statistiken, die Windows Vista für das Jahr 2008
bis zum Erscheinen dies Dokuments, in dem Monat Juni, nur eine einzige Schwachstelle
bescheinigen, die direkt auf Buffer-Overflows zurückzuführen ist. [ 1 ]



Ziel dieser Arbeit

Das Ziel dieser Arbeit ist es, einen Überblick zu schaffen, welche Mechanismen die Betriebssysteme
Windows XP und Windows Vista zur Vermeidung und zur Schadensbegrenzung von Buffer Overflows
bieten. Dabei wird auf die Schwachstellen der vorhandenen Mechanismen eingegangen und es
werden Wege aufgezeigt, wie sich diese Schwachstellen praktisch auszunutzen lassen. Außerdem hat
es diese Arbeit zum Ziel, die Neuerungen von Windows Vista, dem noch weiter verbreiteten
Windows XP gegenüber zu stellen und so den sicherheitstechnischen Mehrwert von Windows Vista
darzulegen.



An wen richtet sich diese Arbeit

Diese Arbeit richtet sich an alle Leser, die an der definierten Zielsetzung interessiert sind. Zum
vollständigen Verständnis werden jedoch Grundkenntnisse der Programmiersprachen c und x86
Assembler vorausgesetzt. Für Buffer Overflows besonders relevante Punkte sind jedoch grundliegend
beschrieben, so dass die Arbeit auch für Leser ohne Vorkenntnisse aus dem Themengebiet "Buffer-
Overflows" verständlich sein sollte.



Gliederung dieser Arbeit

Die einzelnen Kapitel dieser Arbeit können für sich selektiv gelesen werden. Die beiden
Grundlagenkapitel(Kapitel 2, 3) bauen jedoch aufeinander auf und sollten von Beginn bis Ende
gelesen werden. Zudem wird im Hauptkapitel(Kapitel 4) auf die Angriffsmöglichkeiten bezug
genommen, die im Grundlagenkapitel "Buffer Overflows" erklärt werden.

In dem Kapitel 2 werden einige, für Buffer Overflows wichtige, Grundlagen zur Systemarchitektur
vorgestellt. Der Fokus liegt hierbei speziell auf Eigenheiten des NT-Kerns der Betriebssystemfamilie
Microsoft Windows.



1
    National Vulnerability Database: http://nvd.nist.gov/
                                                     Seite - 8 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



In dem Kapitel 3 wird eine Einführung in das Thema Buffer Overflows gegeben und es werden
einzelne Codestücke präsentiert. Um die Übersichtlichkeit an dieser Stelle nicht unnötig zu
beeinträchtigen sind die vollständigen Programme in einem Zip-Archiv(Kapitel B) zu finden. Ebenfalls
im Kapitel B, finden sich Hinweise zum Übersetzen der jeweiligen Programme mit dem Compiler der
Entwicklungsumgebung Visual Studio 2003 / 2005. Ziel dieses Kapitels ist es, die grundliegenden
Konzepte von Buffer Overflows zu umreißen und einige windows-spezifische Angriffe zu erläutern.

Das Hauptkapitel dieser Arbeit beschäftigt sich im Detail mit den verschiedenen Schutzmaßnahmen
von Windows XP und Vista. Hierbei wird vor allem auf den effektiven Nutzen der einzelnen
Maßnahmen eingegangen. In diesem Zusammenhang werden Implementierungs- und
Designschwachstellen aufgezeigt und objektiv beurteilt. Dabei wird auf die im Kapitel 3 vorgestellten
Angriffe eingegangen und es wird geklärt, warum die Angriffe noch, oder nicht mehr, funktionieren.

Abschließend folg eine Zusammenfassung der wesentlichen Erkenntnisse der wesentlichen
Erkenntnisse und kritischen Blick auf die derzeitigen Entwicklungen.




                                             Seite - 9 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



2 Grundlagen – Systemarchitektur
In diesem Kapitel werden Grundlagen aus den Bereichen Betriebssystem- und Rechnerarchitektur
vorgestellt, die für ein fundiertes Verständnis von Buffer Overflows benötigt werden. Dabei wird
speziell auf Eigenheiten des NT-Kernels, der Betriebssystemfamilie Microsoft Windows, eingegangen.



2.1 Speicherorganisation
In diesem Abschnitt wird die Speicherorganisation von Windows XP und Windows Vista grundliegend
erläutert um ein eine Basis für das Verständnis, der in Kapitel 3 beschriebenen Angriffstechniken, zu
schaffen.



2.1.1 Virtueller Speicher
Bei 32-Bit-Windows-Systemen steht einer Anwendung theoretisch der maximal adressierbare
Speicher von 0x00000000 bis 0xFFFFFFFF zur Verfügung, was einer Größe von 2³² Byte = 4 GByte
entspricht. Dabei ist die kleinste adressierbare Einheit ein Byte groß.

Diese 4 GByte teilt Windows in zwei Hälften. Den höherwertigen Bereich verwendet es für
Betriebssystemfunktionen und den niederwertigen Bereich für das Programm selbst. Die Trennung
ist normalerweise, wie in der Abbildung 2-1 zu sehen ist, in der Mitte des Gesamtspeichers
vorgenommen. Dank dem Konzept des virtuellen Adressraums, ist es dabei nicht wichtig, welche
Menge an Arbeitsspeicher das System tatsächlich benötigt. Mit Hilfe der Hardware, zur
Adressvirtualisierung, kann das Betriebssystem die Umsetzung von physikalischen in virtuelle
Adressen erledigen, ohne dass sich ein laufendes Programm darum bemühen muss. Dabei kümmert
sich das Betriebssystem automatisch um das Ein- und Auslagern von Speicher.




                                             Seite - 10 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



                                                                 Hinweis
                      0000000h
                 (Kleine Adressen)
                                                                 Unter 32-Bit-Windows lässt sich
                                                                 die Aufteilung auch mittels einer
 00000000h
                Benutzermodus-                                   Bootoption in das Verhältnis 3GB
                   Prozesse
                    (2GB)                                        zu 1GB (Programmspeicher zu
 7FFFFFFFh
                                                                 Systemspeicher) ändern. Unter
 80000000h
                Systemspeicher                                   64-Bit-Windows-Varianten ist die
                     (2GB)
                                                                 Speicheraufteilung nahezu 50%
 FFFFFFFFh
                                                                 zu 50% [ 2 S. 14ff ] .
                     FFFFFFFFh
                 (Große Adressen)                                Außerdem gibt es die Address
                                                                 Windowing Extensions (AWE) mit
                                                                 deren Hilfe sich der virtuelle
Abbildung 2-1: 32-Bit-Windows Standard-Adresslayout
nach [ 2 ]                                                       Speicher vergrößern lässt [ 2 S.
                                                                 383ff ]




2.1.2 Systemfunktionen
Im Zusammenhang mit Buffer Overflows ist weniger die im letzen Kapitel vorgestellte
Speicheraufteilung interessant, sondern viel mehr die Art wie auf Systemfunktionen zugegriffen wird.

Im normalen Programmbetrieb werden alle benötigten Systemfunktionen, wie beispielsweise
sleep(), in den virtuellen Adressbereich der Anwendung eingeblendet. Beim Übersetzen des
Quellcodes erzeugt der Compiler automatisch die Befehle zum Laden der nötigen Systemfunktionen.
Die Adressen an denen die verschiedenen Systemfunktionen zu finden sind, sind innerhalb eines
Systems normalerweise immer gleich. Unterschiede bezüglich der Funktionsadressen gibt es lediglich
zwischen den einzelnen Windows-Versionen sowie zwischen den verschiedenen Service-Packs. Eine
wirkliche Ausnahme entsteht allerdings durch Address Space Layout Randomization (ASLR), auf das in
dem Kapitel 4.6.4 eingegangen wird.

Ein Beispiel:

Mit dem Programm dumpbin.exe, das Teil von Visual Studio 2005 ist, lässt sich die Basis-Adresse des
Windows-Kernels ermitteln:

PS C:WINDOWSsystem32> dumpbin /headers kernel32.dll | select-string -
pattern "image base"
         7C800000 image base (7C800000 to 7C906FFF)


Um nun beispielsweise die Absolute Adresse der Sleep-Funktion zu ermitteln muss noch die relative
Adresse, ausgehend von der Basis-Adresse, ermittelt werden:


                                                  Seite - 11 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



PS C:WINDOWSsystem32> dumpbin /all kernel32.dll | select-string -pattern
"sleep"
 7C8087E0: 73 6F 75 72 63 65 00 53 6C 65 65 70 00 53 6C 65       source.Sleep.Sle
       831 33E 00002442 Sleep
       832 33F 0000239C SleepEx


Aus der Basis-Adresse und der relativen Adresse ergibt sich die absolute Adresse der Funktion
sleep()

7c800000h + 00002442h = 7c802442h

Wie bereits erwähnt ist diese Adresse bei jedem Programmstart, unter Berücksichtigung der oben
genannten Ausnahmen, gleich.


  Tools-Hinweis
  Die obigen Kommandos wurden nicht in der normalen Eingabeaufforderung, sondern in der
  kostenlos über Microsoft erhältlichen Powershell, ausgeführt. Der Grund hierfür ist, dass in der
  Powershell die Funktion select-string() zur Verfügung steht. Bezugsmöglichkeiten: siehe
  Toolsverzeichnis




                                             Seite - 12 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



2.1.3 Speichersegmente
Der einem Prozess zur Verfügung stehende virtuelle Speicher, teilt sich wie in Abbildung 2-2 zu sehen
ist, in verschiedene Segmente auf. Wichtig sind hierbei vor allem die relative Position der Segmente
zueinander, sowie die Richtung des Schreibzugriffs. Der Schreibzugriff erfolgt immer von einer
Basisadresse in Richtung der höherwertigen Adressen.




                                        Prozess-Speicher                              Beispielhaftes
                                                                                       Stackframe


        0000000h
   (Kleine Adressen)
                                                                              Gespeicherte Register der
                                            Heap
                                                                                aufrufenden Funktion

                                                                                    Lokale Variablen

                                                                              Stack Frame Pointer (SFP)
                     Schreibrichtung




                                           Stack                                  Return Adresse (RET)

                                                                                       Parameter

                                            .TXT

                                           .DATA
                                                                          Heap        entspricht einer Dynamischen
                                            .BSS                                      Erweiterung des BSS-Segments
                                                                                      (langlebige Daten)
                                                                          Stack       Registerinhalte
        FFFFFFFFh                      Systemspeicher                                 lokale Variablen
    (Große Adressen)                                                                  Funktionsparameter
                                                                                      (kurzlebige Daten)

                                                                          .TXT        Programmkode
                                                                          .DATA       Initialisierte
                                                                                      globale und statische Variablen
                                                                          .BSS        Nicht initialisierte
                                                                                      globale und statische Variablen
                                                   Wachstumsrichtung




Abbildung 2-2: Prinzipielles Windows Prozess-Speicher-Layout nach [ 3 S. 182 ] und [ 4 S. 397 ]




                                                           Seite - 13 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Stack
Der Stack ist das Speichersegment, das zum Ablegen kurzlebiger Daten dient. So werden auf dem
Stack beispielsweise lokale Variablen und Funktionsparameter abgelegt.

In Abbildung 2-2 ist, auf der rechten Seite, ein typisches Stackframe abgebildet. Anhand der Pfeile,
die die Wachstumsrichtung angeben, lässt sich erkennen, dass der Stack von großen Adressen in
Richtung kleiner Adressen wächst.

Das Prinzip eines Stacks, oder auch Stapelspeicher genannt, ist folgendes:

    1. Sollen neue, kurzlebige Daten zwischengespeichert werden, so werden diese auf die bereits
       auf dem Stack liegenden Daten von oben aufgelegt. Die Operation zum Auflegen neuer
       Daten nennt sich "push".
    2. Werden die auf dem Stack gelegten Daten zur Verarbeitung benötigt oder muss der Stack
       aufgeräumt werden, so wird von oben, Stück für Stück, abgebaut. Die Operation zum
       Herunternehmen von vorhandenen Daten nennt sich "pop".

Wenn innerhalb eines Prozesses eine Funktion aufgerufen wird, so werden die benötigten Daten, wie
in der Abbildung 2-2 auf der rechten Seite zu sehen ist, auf dem Stack abgelegt (push). Ist die
Funktion beendet, so werden die Daten in umgekehrter Reihenfolge wieder vom Stack genommen
(pop).

Der Teil des Maschinencodes, der ein Stackframe aufbaut, nennt sich Prolog und der abbauende Teil
nennt sich Epilog.

Genauere Informationen zur Funktionsweise des Stacks können in [ 5 ] gefunden werden.



Heap
Der Heap ist der dynamische Speicherbereich eines Prozesses. Er kann dynamisch in seiner Größe
wachsen, somit kann der Prozess dynamisch Speicher anfordern und freigeben.

Der Heap ist in Abbildung 2-2 im Bereich der niedrigen Speicheradressen zu sehen, was für Windows
typisch ist. Außerdem ist in der Abbildung 2-2 dargestellt, dass der Heap in Richtung großer Adressen
wächst.

Inhaltlich gesehen werden auf dem Heap, verglichen mit dem Stack, langlebigere Daten abgelegt.

Ein Prozess hat typischer Weise mehrere Heaps. Ein neuer Heap kann z. B. mit der Windows-API-
Funktion HeapCreate() angefordert und mit HeapDestroy() wieder freigegeben werden. Das
Anfordern eines Speicherbereiches erfolgt anschließend mit HeapAlloc() und wird analog mit
HeapFree() wieder freigegeben. Bei Verwendung des c++-Operators new, wird automatisch
Speicher von dem Standard-Heap des Prozesses angefordert.




                                              Seite - 14 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Wie bereits angedeutet, hat der Programmierer die Wahl. Entweder er verwendet die von der
Windows-API bereitgestellten Funktionen für das Heap-Management, oder er verwendet seine
eigene Heap-Verwaltung. [ 6 ]

Die von Windows standardmäßig verwendete Heap-Verwaltung organisiert den Speicher in einer
Reihe unterschiedlicher Listen, die zusammen den gesamten Heap beschreiben. Dabei gibt es unter
anderem, eine Liste für noch freie, kleine Speicherblöcke mit Größe 4KByte und eine separat
geführte Liste, die alle belegten Blöcke enthält. Zudem werden temporären Listen erstellt, die der
Geschwindigkeitserhöhung dienen. [ 7 ] [ 8 ] [ 9 ]



2.1.4 Big- und Little-Endian
Als Big- und Little-Endian, werden zwei verschiedene Darstellungsformen von Werten, im Speicher
bezeichnet.

Beispiel: Darstellung der Speicher-Adresse AABBCCDD Hex = 2864434397 Dez im Speicher

Big-Endian: Das höchstwertige Paar steht ganz links, wie in Abbildung 2-3 zu sehen.

 Big-Endian


                 AA    BB   CC    DD
  00000000h                                FFFFFFFFh


Abbildung 2-3: Big-Endian im Speicher


Little-Endian: Das höchstwertige Paar steht ganz rechts, wie in Abbildung 2-4 zu sehen.

 Little-Endian


                 DD   CC     BB    AA
  00000000h                                FFFFFFFFh


Abbildung 2-4: Little-Endian im Speicher




                                                       Seite - 15 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



3 Grundlagen – Buffer Overflows

Ziel dieses Kapitel ist es, dem Leser die Grundkonzepte von Buffer Overflows zu vermitteln um so die
von Windows XP und Windows Vista verwendeten Schutzmechanismen besser beurteilen zu können.

Im Folgenden sind immer nur die unmittelbar relevanten Codeabschnitte gelistet. Die vollständigen
Programme mit Hinweisen zum Übersetzen und zugehöriger Programmausgaben sind im Anhang zu
finden.



   Wichtiger Hinweis für Selbstversuche
   Mit Visual Studio 2005 lassen sich die meisten Beispielprogramme nicht ohne weiteres mit den
   erwarteten Ergebnissen übersetzen. Der Grund hierfür sind Optimierungen und
   Schutzmechanismen, die der Visual Studio 2005 Compiler verwendet. Hinweise zum
   „richtigen“ Übersetzen, sowie die vollständigen Programme finden sich jeweils im Anhang




3.1 Buffer Overflows
Im Kontext der Programmiersprache c, ist ein Buffer ein zusammenhängender Speicherbereich, der
Daten desselben Typs beinhaltet. In c werden Buffer häufig auch als Arrays bezeichnet.

Ein primitiver Buffer kann in c beispielsweise folgende Form haben:

char buf0[4] = "ABC0";


In diesem Fall ist der Buffer 4 Byte groß und ist ein String-Buffer. Dabei dient das letzte Byte zur
Terminierung des Strings.

Ein Buffer Overflow, oder auch Puffer-Überlauf, passiert demnach, wenn die Grenzen des
vorgesehenen Speicherbereichs überschritten werden.

Die Folgen für das Gesamtsystem können dabei sehr unterschiedlich ausfallen.

So zeigt sich die gefährliche Seite von Buffer Overflows, wenn es dem Angreifer möglich ist
beliebigen Code einzuschleusen und seine Ausführung zu erreichen. In diesem Fall stehen dem
Angreifer nahezu unbegrenzte Möglichkeiten zur Verfügung. Von einem primitiven Beenden der
verwundbaren Software bis hin zum Erstellen von Benutzerkonten oder dem installieren eines Root-
Kits (Details zu Angriffen dieser Art in dem Kapitel 3.1.4).




                                             Seite - 16 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Bei weniger kritischen Programmierfehlern kann es zu inkonsistenten Daten kommen die nicht
zwangsläufige einen Programmabsturz nach sich ziehen müssen und somit lange unentdeckt bleiben
können. Damit beeinträchtigen diese Fehler zwar den Nutzen der Software, die
Angriffsmöglichkeiten können aber, wie das Beispiel im nächsten Abschnitt zeigt, recht beschränkt
sein.



3.1.1 Off-By-One Buffer Overflows
Ein klassischer Buffer Overflow im Zusammenhang mit c-Strings ist der "Off-By-One"-Fehler. Dabei
vergisst der Programmierer entweder den bereits erwähnten String-Terminator mitzuzählen, oder er
ist bei der indexzählweise der Arrays unaufmerksam.

Mit folgenden Programmausschnitten sollen die Folgen eines Off-By-One Overflows verdeutlicht
werden. Das vollständige Programm mit Compiler-Optionen und vollständiger Ausgabe sind im
Quellcode-Archiv zu finden (Kapitel B).

                                                  Quellcode: Kapitel-3-1-1_off-by-oneoff-by-one.c
//gekürzt...
char buf0[] = "1234";
char buf1[4] = "ABC0";
strcpy(buf1,buf0);


Führt man das vollständige Programm aus, so erhält man folgende Ausgabe

~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adresse von buf0: 0012FF68 sizeof(buf0): 5 Byte
Adresse von buf1: 0012FF64 sizeof(buf1): 4 Byte
buf0 1234
buf1 ABC
~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
buf0
buf1 1234


Betrachtet man diese Programmausgabe genauer, so fällt auf, dass der Buffer buf1 zwar den
gewünschten Inhalt erhalten hat, buf0 seinen Inhalt aber offenbar verloren hat.

Was hier passiert ist schnell erklärt: Es kommt zu einen Buffer Overflow, weil der Programmierer
vergessen hat, dass der Compiler an der Stelle char buf0[] = "1234"; automatisch ein 5-Byte
großes, null-terminiertes String-Array erstellt.

Deshalb überschreibt strcpy() mit dem '0' den ersten Teil des Buffers buf0, der nach buf1 im
Speicher liegt. Die Abbildung 3-1 soll diesen Vorgang verdeutlichen.

Hinweis zum Speicherlayout: Die Anordnung von Variablen im Speicher wird immer zum Zeitpunkt
des Übersetzens festgelegt und wird von dem Compiler entschieden. Der Compiler folgt beim
Erstellen des Speicherlayouts zwar festen Regeln, eine feste Beziehung zwischen der Reihenfolge der



                                            Seite - 17 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Definition/Deklaration im Quellcode und dem tatsächlichen Layout existieren jedoch nicht. Zur
Erinnerung: Der Heap unterliegt anderen Regeln, da dieser Speicher erst zur Laufzeit erstellt wird.



                          buf1                         buf0

                0x0012FF64                  0x0012FF68
 1. Vorher       A    B          C   '0'    1     2    3     4    '0'

 2. strcyp()

 3. Nachher      1    2          3   4      '0'   2    3     4    '0'


Abbildung 3-1: Die Buffer buf0 und buf1 im Speicher



Gibt man mittels printf("buf0 %sn", buf0); den String buf0 aus, so wird nichts ausgegeben,
weil der String-Terminator bereits an erster Stelle steht und printf() mit dem Formatstring %s, nur
Zeichen bis zu dem ersten vorkommenden '0' ausgibt.

Off-By-One Buffer-Overflows sind in den meisten Fällen nichts weiter als ärgerliche
Programmierfehler die relativ schwer zu finden sind. Der Nutzen ist für Angreifer bei
Programmierfehlern dieser Art meist sehr beschränkt. Hauptsächlich deshalb, weil der
Speicherbereich der durch den Benutzer beeinflusst werden kann, mit einer Speichereinheit (im
obigen Beispiel 1 Byte) nur sehr wenig Spielraum bietet. Die Folgen hängen aber davon ab, welche
Art von Informationen in den überschreibbaren Bereichen gespeichert sind.

Wie interessant selbst ein Off-By-One Buffer-Overflow werden kann zeigt der folgende Abschnitt.



3.1.2 Mit Buffer Overflows den Kontrollfluss ändern
Im dem folgenden Programm kann ein Buffer Overflow dazu verwendet werden eine Variable zu
überschreiben, die wesentlich für den Ablauf des Programms ist.

                                                       Quellcode: Kapitel-3-1-2_auth_overflowauth_overflow.c
#include       <stdio.h>
#include       <stdlib.h>
#include       <string.h>
#include       "hacking.h"

int check_authentication(char *password) {
      int auth_flag = 0;
      char password_buffer[8] = {'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F'};

          printf("~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n");
          printf("dump: password_buffern");


                                                         Seite - 18 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



       dump(password_buffer, 8);

       printf("dump: auth_flagn");
       dump(&auth_flag, 4);

       strcpy(password_buffer, password);

       if(strcmp(password_buffer, "password") == 0)
             auth_flag = 1;
       if(strcmp(password_buffer, "adminpw") == 0)
             auth_flag = 1;

       printf("~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n");
       printf("dump: password_buffern");
       dump(password_buffer, 8);

       printf("dump: auth_flagn");
       dump(&auth_flag, 4);

       return auth_flag;
}

int main(int argc, char *argv[]) {
      //... gekürtzt
      if(check_authentication(argv[1])) {
            printf("n-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
            printf("      Zugang freigegeben.n");
            printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
      } else {
            printf("nZugang verweigert.n");
      }
      getchar();
}



Hinweis: die Funktion dump() ist in der Include-Datei hacking.h definiert. Sie gibt von einer, per
ersten Parameter übergebenen Speicheradresse ausgehend, eine mit dem zweiten Parameter
definierte, Menge an Speicherinhalten, aus.

Das Programm übernimmt den ersten Parameter, den das Hauptprogramm übergeben bekommt und
kopiert ihn in der Funktion check_authentication() in einen Buffer, der als Zwischenspeicher
dient.

Stimmt das Passwort mit einem der gespeicherten Passwörter überein, so wird die Variable
auth_flag auf 1 gesetzt, was "wahr" entspricht.

Das Hauptprogramm nimmt den Rückgabewert der Funktion check_authentication() um
Zugang zu einem geschützten Bereich zu gewähren oder zu verweigern.



                                            Seite - 19 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Dabei gibt es in dem Programmfluss keinen Else-Zweig, der im Falle, keiner Übereinstimmung, die die
Variable auth_flag auf 0 setzen würde. Der Programmierer hat diesen Fall durch das Initaialisieren
mit 0 abgedeckt.

Gegen dieses Vorgehen ist prinzipiell nichts einzuwenden. Das Problem mit dem obigen Code ist
jedoch, dass die Funktion strcpy() unabhängig von der Größe des Zielbuffers password_buffer
alle Zeichen bis zum ersten '0' kopiert.

Diese Frage lässt sich mit Hilfe der Programmausgabe beantworten:

~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dump: password_buffer
0012FF54: 46 46 46 46 46 46 46 46                            | FFFFFFFF
dump: auth_flag
0012FF5C: 00 00 00 00                                        | ....
... gekürzt


Wie hier zu sehen ist liegt die Variable auth_flag unmittelbar nach dem Buffer password_buffer
im Speicher.

Das bedeutet, sollte es dem Benutzer möglich sein, dem Programm als ersten Parameter mehr als
acht Zeichen zu übergeben, so findet durch strcpy()ein Buffer Overflow in den Speicherbereich
der Variable auth_flag statt.

Startet man das Programm beispielsweise folgendermaßen:

auth_overflow.exe 12345678A


So erhält man folgende Ausgabe:

~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dump: password_buffer
0012FF54: 46 46 46 46 46 46 46 46                            | FFFFFFFF
dump: auth_flag);
0012FF5C: 00 00 00 00                                        | ....
~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dump: password_buffer
0012FF54: 31 32 33 34 35 36 37 38                            | 12345678
dump: auth_flag);
0012FF5C: 41 00 00 00                                        | A...

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-


Wie zu sehen ist, hat der Benutzer mittels "12345678" acht Zeichen übergeben und noch ein
zusätzliches 'A' angehängt.

Wie in der Abbildung 3-2 zu sehen ist, wird das 'A' an die Speicheradresse 0x0012FF5C geschrieben.
Der Rückgabewert der Funktion check_authentication() ist als int deklariert und wird durch
das Programm als wahr oder falsch interpretiert. Da in der Programmiersprache c alle Werte ungleich
0 dem Wert "Wahr" entsprechen, wird im Falle eines Overflows der Zugang zum geschützten Bereich
gewährt.


                                            Seite - 20 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




                                        Overflowrichtung



                                password_buffer                       auth_flag

               0x0012FF54                                       0x0012FF5C
 1. Vorher       F    F     F       F     F       F   F     F     0   0     0     0

 2. strcyp()

 3. Nachher      1    2     3       4     5       6   7     8     A   0     0     0


Abbildung 3-2: Buffer Overflow von password_buffer in auth_flag

Dieses Beispiel zeigt eindrucksvoll, wie bereits ein einziges Zeichen an der richtigen Stelle genügen
kann, um den Ablauf eines Programms wesentlich zu beeinflussen.

Hinweis: Werden in diesem Programm mehr wie zwölf Zeichen übergeben, so stürzt das Programm
ab, da Informationen auf dem Stack überschrieben werden, die für den weiteren Programmablauf
benötigt werden. Abgesehen von Programmabstürzen, bietet diese Möglichkeit der Manipulation
von Werten, einen interessanten Ansatzpunkt für weitere Angriffsmöglichkeiten, auf die in den
folgenden Abschnitten eingegangen wird.

Bei dem zuletzt vorgestellten Overflow, ist außerdem zu bedenken, dass nicht nur Zahlenwerte und
Strings auf diese Weise manipuliert werden können. Es ist ebenso möglich Funktionspointer zu
überschreiben. Auf diese Weise kann wahlfrei, vorhandener, oder auch eingeschleuster
Programmcode ausgeführt werden.

Damit kommt dieses Grundagekapitel zu den "nützlicheren" Angriffsmöglichkeiten mittels Buffer
Overflows: Dem Ausführen von beliebigem Code.



3.1.3 Ausführen von beliebigem Code
Um auf einem Zielsystem beliebigen Code auszuführen sind prinzipiell immer zwei Schritte nötig

    1. Den auszuführenden Code in den Arbeitsspeicher des Zielsystems einschleusen
    2. Eine Möglichkeit finden den eingeschleusten Code ausführen zu lassen

Der erste Punkt lässt sich, ähnlich wie im Beispiel des letzten Abschnitts, mittels Programmparameter
erreichen.

Der zweite Punkt wird meistens durch das Manipulieren eins Funktionspointers erreicht. Dazu
können vom Programmierer selbst angelegte Funktionspointer verwendet werden, oder aber auch
auf dem Stack liegende Rücksprungadressen. [ 5 ]



                                                       Seite - 21 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Da die wirkliche Herausforderung mehr beim Ausführen, als beim Einschleusen liegt, werden in den
restlichen Abschnitten dieses Kapitels einige Möglichkeiten vorgestellt, um den Code zur Ausführung
zu bringen.



3.1.4 Klassische Stack-basierende Angriffe
Stack-basierende Angriffe verfahren normaler weise streng nach dem bereits vorgestellten Schema:

    1. Den auszuführenden Code in Arbeitsspeicher des Zielsystems einschleusen
    2. Eine Möglichkeit finden den gespeicherten Code ausführen zu lassen

Eine Möglichkeit den Code einzuschleusen haben wir bereits kennen gelernt: Die strcpy()-Funktion.
Kommen wir also zu Punkt zwei: Dem Starten des eingeschleusten Codes.

Bei klassischen Stack-basierenden Angriffen erfolgt das Starten des Codes durch das Manipulieren,
der auf dem Stack gespeicherten Rücksprung-Adresse(RET). Betrachtet man die in Abbildung 3-3
dargestellte Speicherbelegung, so stellt man fest, dass RET an einer höheren Adresse im Speicher
liegt als die lokalen Variablen.


                                                          Beispielhaftes
                                                           Stackframe



       0000000h
   (Kleine Adressen)                               Gespeicherte Register der
                                                    aufgerufenen Funktion
                                                                                   Overflowrichtung
                               Wachstumsrichtung




                                                       Lokale Variablen

                                                   Stack Frame Pointer (SFP)

                                                     Return Adresse (RET)


        FFFFFFFFh                                         Parameter
    (Große Adressen)



Abbildung 3-3: Beispielhaftes Stackframe unter Windows allgemein




Da lokal angelegte String-Arrays (Buffer) nichts weiter sind als die in Abbildung 3-3 dargestellten
lokalen Variablen, ist es möglich, mit einer ausreichend großen Eingabe die Return-Adresse zu
überschreiben.

Damit der nächste Schritt, das Bestimmen der gewünschten Rücksprungadresse, einfacher ist, wird
als Füllmenge (um RET zu erreichen) ein spezieller Assembler-Befehl verwendet: ein NOP. NOP steht
für No OPeration und hat als einzige Aufgabe einen Maschinenzyklus zu verbrauchen. Danach führt



                                                                    Seite - 22 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



die CPU die Befehlsverarbeitung mit dem darauf folgenden Befehl fort. Typischer Weise werden an
dieser Stelle einige NOPs aufeinander folgen und bilden so einen NOP-Sled2.

Um nun den eingeschleusten Code ausführen zu lassen, muss RET auf den ersten eingeschleusten
Befehl zeigen. Wenn ein NOP-Sled verwendet wird, reicht es an dieser Stelle aus, einen beliebigen
Punkt auf dem NOP-Sled zu treffen. Das ist besonders hilfreich, weil der Compiler mit dem das
verwundbare Programm übersetzt wird möglicher Weise einige Optimierungen durchführt, die die
exakte Positionsbestimmung des ersten Befehls erschweren können. Aus demselben Grund ist es
üblich die gewünschte Return-Adresse mehrfach zu wiederholen.

Besondere Aufmerksamkeit muss man der Ausrichtung der eingeschleusten Return-Adresse widmen.
Das bedeutet man muss darauf achten, dass das erste Byte der selbst geschriebenen Return-Adresse
auch wirklich über das erste Byte der alten Return-Adresse geschrieben wird. Ohne Verwendung
eines Debuggers, lässt sich die korrekte Ausrichtung am einfachsten durch Probieren finden. Dabei
sollten maximal vier Versuche nötig sein, da eine Adresse bei 32-Bit-Systemen nur 4 Byte groß ist und
es somit nur vier Möglichkeiten gibt.

Die absolute Adresse des Puffers wird meist mittels eines Debuggers ermittelt und auf x86-Systemen
in Little-Endian-Anordnung eingebgeben. Interessant hierbei ist auch, dass der Stack eines Prozesses
immer an derselben Adresse beginnt, was bedeutet, dass auch hier Probieren zum Ziel führen kann.

Alternativ können zur Positionsbestimmung von Buffern auch Formatstring-Verwundbarkeit
verwendet werden [ 5 ] . Auf Formatstrings wird in dieser Arbeit jedoch nicht eingegangen, weil sie
nicht Teil des Themas "Buffer-Overflows" sind.

Insgesamt wird dem auszunutzenden Programm die in Abbildung 3-4 dargestellte Kombination
übergeben. Diese Kombination aus NOP-Sled, Payload3 und widerholter Return-Adresse, wird häufig
als Angriffsvektor bezeichnet.

                                                        RET
       NOP-sled                 Payload
                                                (mehrfach wiederholt)


Abbildung 3-4: Angriffsvektor

Bleibt noch zu erwähnen, dass die Speichermenge die für den Payload zur Verfügung steht, durch die
Position der Return-Adresse begrenzt ist. Dieses Problem lässt sich aber durch einen relativen
Sprung, nach den Speicherbereich mit der Return-Adresse umgehen. [ 10 ]




2
 NOP-Sled/NOP-Sledge: Übersetzt: NOP-Schlitten. Das bedeutet, eine Folge von NOPs führen direkt, ohne jede weitere
Funktion, zu dem nächsten „richtigen“ Maschinenbefehl. Beispielsweise dem ersten Befehl des Sehellcodes.

3
 Der Payload/Shellcode besteht aus Maschinencode. Er ist der Teil des einzuschleusenden Codes, der die eigentliche
Funktionalität beinhaltet. Häufig werden die Begriffe Payload und Shellcode als Synonym verwendet. Details zu Shellcode
werden in dem Kapitel 3.2 behandelt

                                                      Seite - 23 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




    Tools-Hinweis
    Die felxibelste Möglichkeit einen Angriffsvektor zu erstellen ist mit der Programmiersprach Perl.
    Perl gibt es auch für Windows: Toolsverzeichnis IV




Beispiel: stack-overflow.c im Quellcode-Ordner "Kapitel-3-1-4_stack-overflow"

Eine Variante des Programms auth_overflow.c ist unter dem Namen stack-overflow.c Quellcode-
Archiv zu finden. Dieses Programm bekommt seine Eingabe dabei nicht als Parameter, sondern liest
von einer Datei. Außerdem wird nicht die Funktion strcpy() verwendet, sondern die Funktion
memcpy(). Für diese Modifikationen gibt es zwei Gründe:

     1. Mit der Windows-Eingabeaufforderung ist es nicht ohne weiteres möglich einem Programm,
        per Parameter Zeichen zu übergeben, die nicht "druckbar" sind. Da das auf die meisten
        Maschinenbefehle zutrifft, ist ein einfaches Einschleusen von Code über die Kommandozeile
        nicht möglich. Abhilfe würde an dieser Stelle das Programm cat4 schaffen mit dem man die
        Eingabe an das Programm umleiten kann. Cat ist aber nicht Bestandteil von Windows. Zudem
        besteht das Problem der Parameterübergabe mit nichtdruckbaren Zeichen auch in den
        meisten Debuggern. Aus diesem Grund liest das Demo-Programm aus einer Datei.
     2. Wie in dem Abschnitt 2.1 beschrieben, liegt der für das Programm selbst zur Verfügung
        stehende Speicher im niederen Adressbereich. Deshalb ist die Wahrscheinlichkeit sehr hoch,
        dass der Buffer in dem der eingeschleuste Code liegt in einem Adressbereich liegt, der mit
        zwei führenden Nullen (0x00AABBCC) beginnt. Das bedeutet, dass strcpy() die
        Speicheradresse nicht mehr kopiert. Es gibt eine Reihe von Möglichkeiten den
        eingeschleusten Code trotz dieses strcpy()-Problems zur Ausführung zu bringen
        (beispielsweise "return to libc"). Um das Beispiel einfach zu halten wird dieses Problem an
        dieser Stelle durch die Verwendung von memcpy() umgangen.

Trotz dieser zwei Vereinfachungen, bleibt das demonstrierte Prinzip in allen Fällen gleich: Den
Angriffsvektor einschleusen und den Maschinencode zur Ausführung bringen.

Ein funktionierender Angriffsvektor zu dem Programm stack-overflow.c ist ebenfalls im Quellcode-
Archiv zu finden. Dabei wird der Angriffsvektor mittels eines Perl-Skripts generiert. Der hierbei
verwendete Payload startet hierbei nichts weiter als den Windows-Taschenrechner clac.exe. Details
zur Erstellung von Payload folgt in dem Abschnitt 3.2.




4
  cat ist ein Kommandozeilen-Tool aus der UNIX-Welt, mit dem sich unter anderem sehr einfach Eingaben aus einer Datei
lesen lassen. Das Tool gibt es auch für Windows

                                                      Seite - 24 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Gekürzte Ausgabe des Programms stack-overflow.exe

~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dump(password_buffer...);
0012FA48: 90 90 90 90 90 90 90 90 90 90 90 90 90     90    90   90   |   ................
0012FA58: 90 90 90 90 90 90 90 90 90 90 90 90 90     90    90   90   |   ................      NOP-sled
0012FA68: 90 90 90 90 90 90 90 90 90 90 90 90 90     90    90   90   |   ................
0012FA78: 29 c9 83 e9 dd d9 ee d9 74 24 f4 5b 81     73    13   b3   |   ).......t$.[.s..
0012FA88: 87 5d 85 83 eb fc e2 f4 4f 6f 19 85 b3     87    d6   c0   |   .]......Oo......
0012FA98: 8f 0c 21 80 cb 86 b2 0e fc 9f d6 da 93     86    b6   cc   |   ..!.............
0012FAA8: 38 b3 d6 84 5d b6 9d 1c 1f 03 9d f1 b4     46    97   88   |   8...]........F..
0012FAB8: b2 45 b6 71 88 d3 79 81 c6 62 d6 da 97     86    b6   e3   |   .E.q..y..b......
0012FAC8: 38 8b 16 0e ec 9b 5c 6e 38 9b d6 84 58     0e    01   a1   |   8.....n8...X...       Payload
0012FAD8: b7 44 6c 45 d7 0c 1d b5 36 47 25 89 38     c7    51   0e   |   .DlE....6G%.8.Q.
0012FAE8: c3 9b f0 0e db 8f b6 8c 38 07 ed 85 b3     87    d6   ed   |   ........8.......
0012FAF8: 8f d8 6c 73 d3 d1 d4 7d 30 47 26 d5 db     f9    85   67   |   ..ls...}0G&....g
0012FB08: c0 ef c5 7b 39 89 0a 7a 54 e4 3c e9 d0     a9    38   fd   |   ...{9..zT.<...8.
0012FB18: d6 87 5d 85 50 fa 12 00 50 fa 12 00 50     fa    12   00   |   ..].P...P...P...
0012FB28: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50     fa    12   00   |   P...P...P...P...         RET
0012FB38: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50     fa    12   00   |   P...P...P...P... (mehrfach wiederholt)
0012FB48: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50     fa    12   00   |   P...P...P...P...
0012FB58: ff                                                         |   .
dump(&auth_flag, 4);
0012FA44: 00 00 00 00                                            | ....



Erklärung:

1.   Der Wert 90 Hex entspricht einem NOP
2.   Der rot markierte Bereich ist der Maschinencode zum Starten von calc.exe
3.   Am Schluss folgt die mehrfach wiederholte Return-Adresse



3.1.5 Exception Handler-basierende Angriffe
Die Programmiersprache c bietet von sich aus keinerlei Möglichkeiten, Ausnahmen im
Programmablauf abzufangen. Eine solche Ausnahme ist beispielsweise eine Division durch Null. Mit
C++ wurde dieser Missstand behoben und es ist somit, durch Elemente der Programmiersprache,
möglich eine Ausnahmebehandlung durchzuführen. Das bedeutet, dass ein Programm, das eine
Division durch null durchführt, nicht mehr zwangsläufig abstürzt, sondern durch eine geeignete
Ausnahmebehandlung weiterlaufen kann. [ 11 ]

Microsoft hat für die Programmiersprache c eigene Elemente eingebracht, mit denen eine
Ausnahmebehandlung möglich wird: __try und __except

Diese Art der Ausnahmebehandlung nennt sich Structured Exception Handling (SEH). Im Unterschied
zu c++ wird die Art der Ausnahme mittels eines unsigned int ermittelt, während in c++ anhand des
Klassen-Typs unterschieden wird. [ 11 ] [ 12 ]




                                            Seite - 25 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Beispiel: Strukturierte Ausnahmebehandlung (SEH) - Division durch Null

                                                    Quellcode: Kapitel-3-1-5_exceptionexception.c
#include <stdio.h>

int MyExceptionHandler(void);
int main(int argc,char *argv[]){
      int a = 1000, b = 0, c = 0;

       if(argc != 2){
             printf("Divisor als Parameter uebergebenn");
             exit(0);
       }
       b = atoi(argv[1]);

       /* Falls eine 0 als erster Programmparameter übergeben wird, wird
        * eine EXCEPTION_INT_DIVIDE_BY_ZERO ausgelößt */
       __try {
             c = a / b;
             printf("Ergebnis der Division %i / %i = %in", a, b, c);
       } __except ( MyExceptionHandler() ){}

       printf("Programm beendet sichn");
       return 0;
}

/* return 1, damit die Exception nicht and das
  * Betriebsystem weitergereicht wird */
int MyExceptionHandler(void){
      printf("Exception.. Ausnahme abgefangen und als behandelt markiert");
      return 1;
}



SEH lässt sich im Zusammenhang mit Buffer Overflows nutzen, um eingeschleusten Code zur
Ausführung zu bringen. Dabei macht sich der Angreifer den Umstand zu nutze, dass die
Funktionsadresse der Funktion zur Ausnahmebehandlung, wie in Abbildung 3-5 zu sehen ist, auf dem
Stack gespeichert wird.




                                            Seite - 26 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




                                                          Beispielhaftes
                                                           Stackframe



       0000000h
   (Kleine Adressen)
                                                   Gespeicherte Register der
                                                    aufgerufenen Funktion




                                                                                   Overflowrichtung
                               Wachstumsrichtung
                                                       Lokale Variablen

                                                   Stack Frame Pointer (SFP)

                                                     Return Adresse (RET)

        FFFFFFFFh
    (Große Adressen)                                      Parameter

                                                              ...

                                                     Pointer zur nächsten
                                                        SEH-Funktion

                                                   Pointer zur SEH-Funktion




Abbildung 3-5: Beispielhaftes Stackframe unter Verwendung von SEH [ 13 ]

Wie bei den klassischen Stack-basierenden Buffer Overflows, wird von einem verwundbaren Buffer
ausgehend, diese Funktionsadresse im Speicher überschrieben. Der Unterschied besteht lediglich
darin, dass die Behandlung der Ausnahme veranlasst werden muss, z. B. mit einer Division durch
Null. [ 14 ]

Interessant ist hierbei, dass jedes Programm mindestens einen, automatisch erstellten SEH besitzt,
den der main()-Routine.

Im Quellcode-Archiv findet sich im Ordner "Kapitel-3-1-5_exception_exploit" ein vollständiges
Beispiel, wie sich SEH unter Windows praktisch ausnutzen lässt.




3.1.6 Heap-basierende Angriffe
Heap-basierende Angriffe funktionieren prinzipiell genauso, wie alle anderen Buffer-Overflows auch.
Es werden Daten überschrieben, die nach einem verwundbaren Puffer im Speicher liegen. Der
einzige Unterschied besteht darin, dass, verglichen mit den anderen Datensegmenten, im Heap
andere Daten gespeichert sind.

Folgende Daten finden sich vermehrt bzw. nur auf dem Heap:

    1. Nutzdaten. Beispielsweise die Variable auth_flag aus dem Abschnitt 3.1.2 (gibt es auch auf
       beim Stack)

                                                                    Seite - 27 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



    2. Daten die den Programmablauf steuern und vom Programmierer bewusst angelegt wurden
       (auch auf dem Stack vorhanden, aber selten)
    3. Daten die den Programmablauf steuern und vom Compiler automatisch angelegt werden
    4. Daten die der Speicherverwaltung dienen. Beispielsweise das Windows-Heap-Management

Den Punkt "Nutzdaten", wurde bereits in dem Abschnitt 3.1.2 ausführlich behandelt und da an dieser
Stelle keine wesentlichen Unterschiede existieren, wird an dieser Stelle nicht näher darauf
eingegangen.

Der zweite Punkt ist vom prinzipiellen Vorgehen bereits bekannt.

Der Punkt Nummer Drei bietet interessante Möglichkeiten, ist in der Praxis jedoch kaum anzutreffen.

Punkt Nummer Vier hat in den letzten Jahren deutlich an Bedeutung gewonnen und ist vor allem im
Zusammenhang mit dem Windows-Heap-Management interessant. Deshalb wird im Folgenden das
prinzipielle Vorgehen bei Angriffen auf das Windows-Heap-Management erläutert.


Angriffe auf das Windows-Heap-Management
Wie bereits in dem Abschnitt 2.1.3 beschrieben, ist ein Heap, der mit HeapCreate()erzeugt wird,
durch eine Reihe von Listen organisiert. Wird mit der Funktion HeapAlloc() ein Speicherbereich
angefordert, so wird von der Heapverwaltung ein passendes Stück Speicher gesucht. Dieses Stück
Speicher wird, unmittelbar vor dem nutzbaren Bereich, mit einem Header versehen, der
Informationen für die Heapverwaltung enthält. [ 15 ] [ 8 ]

Bei Angriffen auf die Heapverwaltung ist es das Ziel, die Headerinformationen derart zu
manipulieren, dass eine begrenzte Menge an Daten an eine beliebige Stelle im Speicher geschrieben
werden kann. Hierbei macht sich der Angreifer zunutze, dass die Headerinformationen Pointer
enthalten, die zur Verkettung der Listen und zur Referenzierung des Nutzspeichers dienen. Beim
Anfordern und Freigeben von Speicher werden die Header teilweise kopiert und unter bestimmten
Bedingungen in einer doppelt verketteten Liste gespeichert. Zur Bestimmung der jeweiligen
Nachbarelemente werden die Headerinformationen verwendet. Somit ist es möglich, durch gezieltes
Überschreiben der entsprechenden Pointer, einen wahlfreien Schreibzugriff mit einer Datenmenge
von 4 Bytes zu erreichen. Dieser Vorgang lässt sich prinzipiell beliebig oft wiederholen, meist ist
jedoch eine Menge von 4 Bytes vollkommen ausreichend, da dies die Größe eines Pointers ist. Somit
lässt sich beispielsweise im Process Execution Block (PEB) ein Pointer überschreiben. Hier bietet sich
der Pointer auf die Funktion ExitProcess() an, die vor dem endgültigen Beenden des Prozesses
ausgeführt wird. Durch das Überschreiben des ExitProcess()-Pointers kann eingeschleuster Code
durch das Beenden des Programms zur Ausführung gebracht werden. [ 15 ] [ 8 ] [ 16 ] [ 17 ] [ 18 ] [
16 ] [ 19 ]

In der Praxis gibt es eine Vielzahl von Variationen der eben beschriebenen Methode. Hierbei haben
jedoch alle Varianten die Gemeinsamkeit, dass sie durch gezieltes Manipulieren der Header, Code zur
Ausführung bringen.



                                             Seite - 28 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Einen besonderen Reiz haben vor allem die Varianten, die es dem angegriffenen Programm
ermöglichen weiter zu laufen, indem sie die beschädigten Heapstrukturen wieder mit gültigen
Werten reparieren. Ein weiterer Grund für die gestiegene Popularität von Angriffen auf das
Heapmanagement sind vor allem Mechanismen, die zum Schutz des Stacks eingeführt wurden und
den Heap so zum schwächsten Glied der Kette werden ließen. Details zu aktuellen
Schutzmechanismen des Stacks werden im Kapitel 4.6 behandelt.



3.2 Shellcode und Payload
Die beiden Begriffe Shellcode und Payload werden heutzutage häufig als Synonym verwendet. Streng
genommen ist der Shellcode jedoch eine spezielle Form des Payloads:

    1.   Der Payload an sich ist der Teil des Angriffsvektors, der die eigentliche Funktionalität
         beinhaltet, also beispielsweise das Starten des Windows-Taschenrechners.
    2.   Der Shellcode ist streng genommen ein spezieller Payload, der eine Shell
         (Eingabeaufforderung) startet, mit der der Angreifer interagieren kann.

In dieser Arbeit wird häufig ebenfalls der Begriff "Shellcode", anstelle des historisch korrekten
Begriffes verwendet. Die Begründung ist die Tatsache, dass der Begriff Shellcode einen höheren
Wiedererkennungswert bietet und selbst in Printmedien gebräuchlicher ist.

Da das Thema Shellcode, bei sorgfältiger Behandlung, den Umfang dieser Arbeit bei weitem
sprengen würde, wird im Folgenden nur stichpunktartig auf Anforderungen an Shellcode
eingegangen.



3.2.1 Anforderungen an Shellcode
Anforderungen an die Darstellung des Shellcodes :

   1. Zeichensatz: Shellcode unterliegt einigen Einschränkungen bezüglich der verwendbaren
      Zeichen. Beispielsweise darf in Shellcode der durch strcpy() eingeschleust wird, nicht das
      Zeichen 0x00 vorkommen, weil strcpy() nur bis zu diesem Zeichen kopiert. Außerdem werden
      die eingegebenen Zeichen häufig von dem angegriffenen Programm geprüft und z. B. nur
      druckbare Zeichen verarbeitet.
   2. Kompatibilität: Die Kompatibilität von Shellcode ist unumgänglich auf eine Art der
      Speicheranordnung beschränkt: entweder Big- oder Little-Endian.
   3. Kodierung: Soll eine Funktion angegriffen werden die eine UTF-8 kodierte Zeichenfolge
      erwartet, so muss außer der Big- und Little-Endian-Anordnung außerdem eine bestimmte
      Reihenfolge von Zwei-Byte-Paaren eingehalten werden. [ 20 ]
   4. Kompaktheit: Häufig ist die Menge an einschleusbaren Zeichen beschränkt.
   5. Unentdeckt bleiben: Unter Umständen passiert der Shellcode auf dem Weg zum Ziel
      Intrusion Detection Systeme (IDS), die die übertragenen Daten auf bekannte Schad-
      Signaturen hin überprüfen.

                                            Seite - 29 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Anforderungen an die Logik des Shellcodes:

       1. Betriebssystemabhängig: Da die meisten Betriebssysteme einen grundliegend
          unterschiedlichen Zugriff auf Systemfunktionen haben, muss der Shellcode meistens
          Betriebssystemspezifisch sein.
       2. Behandlung der Anwendung: Unter bestimmten Umständen hat man die Möglichkeit, das
          angegriffene Programm weiter laufen zu lassen und so die Wahrscheinlichkeit, das der
          Angriff entdeckt wird stark zu reduzieren.



3.2.2 Besonderheiten von Windows-Shellcode
Verglichen mit Shellcode der unter Linux lauffähig ist, gibt es für Windows-Shellcode eine Reihe von
Besonderheiten

1.      Windows-Shellcode ist größer als Linux-Shellcode [ 21 ]
2.      Keine 1-zu-1-Abbildung der Bibliotheksfunktionen des Betriebssystems zu den Systemcalls auf
        Kernelebene [ 21 ]
3.      Die verwundbaren Buffer liegen meist in einem Speicherbereich, der mit 0x00… beginnt und
        somit ein nicht verwendbares Zeichen darstellt.



3.2.3 Was möglich ist
Je nachdem welchen Anforderungen (siehe 3.2.1) der eingeschleuste Code unterliegt, ergeben sich
unterschiedliche Möglichkeiten:

1. Erweitern der Rechte durch angreifen von lokalen Prozessen die mit umfangreicheren Privilegien
   ausgestattet sind.
2. Nachladen von Systemfunktionen, falls der Prozess eine für den Angriff wichtige Dll-Funktion5
   nicht geladen hat, kann man diese Dll selbst nachladen.
3. Nachladen von Code, falls der eingeschleuste Code in seiner Größe beschränkt war, hat man die
   Möglichkeit z. B. aus dem Internet Code nachzuladen. [ 21 ]

Wenn man davon ausgeht, dass man die Möglichkeit hat, eine unbegrenzte Menge an
eingeschleusten Code, mit Administrator-Rechten auszuführen, dann hat man praktisch keine
Einschränkungen mehr. Man kann beispielsweise:

1. Ein lokales Benutzer-Konto erstellen (Local Access)
2. Einen Prozess starten, der beliebige Befehle über das Netzwerk ausführt (Remote Access)
3. Einen Keylogger installieren, der sämtliche Aktivitäten des Benutzers aufzeichnet, inklusive
   Tastatur- und Mausinteraktion (Spionage)




5
    Dynamic Link Library (Dll)
                                               Seite - 30 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



3.2.4 Abstraktes Beispiel: Portbind Shellcode für Windows
An dieser Stelle folgt ein eine allgemeine Beschreibung, der Vorgehensweise, zum Starten einer
Remote-Shell: [ 21 ]

   1. Suchen der Datei kernel32.dll
   2. Auflösen der Symbole GetProcAddressA(), LoadLibraryA() sowie ExitProcess()
   3. Laden der Winsock Bibliothek ws2_32.dll
   4. eine Shell an einen TCP-Port binden
      4.1. Socket erzeugen
      4.2. Socket an Port binden
      4.3. Auf dem Port auf eingehende Verbindung warten
      4.4. Verbindung akzeptieren
   5. Kommandointerpreter starten
   6. Den Elternprozess sauber beenden oder die beschädigten Strukturen reparieren

Um nicht zu sehr vom eigentlichen Thema, den Buffer Overflows, abzukommen, wird an dieser Stelle
nicht weiter auf das Themengebiet Shellcode eingegangen. Interessierten sei [ 21 ] für weitere
Informationen empfohlen.

Es folgt nun eine Vorstellung der Verschiedenen Mechanismen in Windows, die dazu dienen den
Schaden durch Buffer-Overflows so gering wie möglich zu halten.




                                           Seite - 31 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



4 Das Sicherheits-Ensemble von Windows und seine Schwachstellen

Dieses Kapitels hat es zum Ziel, die Frage nach dem betitelten Stack- und Heap-Overflow-Schutz bei
Windows XP und Windows Vista zu klären. Nicht eingegangen wird auf weitere nützliche Funktionen
zur Erhöhung der Sicherheit, die nichts direkt mit Buffer-Overflows zu tun haben.

Die vorhergegangenen beiden Grundlagenkapitel, haben sich im Wesentlichen damit beschäftigt,
welche Bedingungen, unter früheren Windowsversionen, gegeben sein mussten, um einem Buffer-
Overflow erfolgreich auszunutzen.

Dieses Kapitel beschäftigt sich zu Beginn damit, wie sich der Schaden, eines unter fremder Kontrolle
stehenden Programms, einschränken lässt und welche neuen Ansätze mit Windows Vista verfolgt
werden (bis einschließlich Kapitel 4.2).

Anschließend werden Methoden von Windows XP und Vista analysiert und bewertet, die es zum Ziel
haben, Angriffe mittels Buffer-Overflows so früh wie möglich zu entschärfen.



4.1 Das Windows-Zwei-Schichten-Modell
Den grundlegendsten       Schutzmechanismus      der       Windows-Architektur   bildet   das   Zwei-
Schichtenmodell.

Unter Windows ist die gesamte Funktionalität in zwei unterschiedlich privilegierte Bereiche
eingeteilt: [ 4 ]

      Kernel-Mode (Ring 0): Der Modus mit den höchstmöglichen Privilegien – Keine
       Einschränkungen beim Zugriff auf die Ressourcen des Kernels
      User-Mode (Ring 1): Eingeschränkter Modus – Beim Zugriff auf Systemressourcen muss eine
       ganze Abfolge von Funktionen abgearbeitet werden, die vom Ring0 als Services zur
       Verfügung gestellt werden.

Häufig werden User- und Kernel-Mode wie in Abbildung 4-1 grafisch dargestellt. Dabei soll
symbolisiert werden, dass auf die innen liegenden Funktionen und Ressourcen nur über die
verbindende Schicht zugegriffen werden kann.




                                            Seite - 32 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




                         Ring1
                      (User-Mode)


                          Ring0
                      (Kernel-Mode)




                         Kernel




Abbildung 4-1: Windows-Zwei-Schichten-Modell [ 4 ]




Im Ring0 finden sich vorrangig Gerätetreiber und Systemfunktionen. Im Ring1 hingegen findet sich
alles Übrige, z. B. ein Browser oder ein FTP-Server.

Schafft es ein Angreifer direkt im Ring0 einen Programmierfehler für sich zu nutzen, so stehen im
praktisch keine Hindernisse mehr im Weg. Allerdings erfordern Angriffe auf dieser Ebene sehr
spezifisches Detailwissen, das äußerst selten offen zugänglich ist. Dies ist auch der Grund dafür, dass
Angriffe auf den Ring0 verhältnismäßig selten vorkommen. Ein Beispiel für einen erfolgreichen
Angriff auf den Gerätetreiber diverser W-LAN-Chips ist der Angriff von Jon Ellch (Pseudonym: Jonny
Cache)[ 22 ] . [ 4 ] [ 23 ]

Meist ist es aus dem oben genannten Grund einfacher, eine verwundbare Anwendung im Ring1
anzugreifen. Jedoch haben Prozesse im Ring1 einen Prozess-Kontext, der einen
rechtebeschreibenden Token beinhaltet. Näheres dazu im nächsten Abschnitt.



4.2 Zugriffsrechte, Authentifizierung und Access Token
Meldet sich ein Benutzer an einem Windows-System an, so bekommt er vom Local Security Authority
Subsystem Service (LSA) ein Access Token zugeteilt. Dieser Token besteht aus einem Security
Identifier (SID), der zu seinem Benutzerkonto gehört, und einigen weiteren SIDs, die zu seinen
Gruppen gehören in denen er Mitglied ist. Eine solche SID ist eine 48 Bit lange Nummer, die
domainweit einzigartig ist. Außerdem enthält der Token, falls definiert, eine Liste von nicht erlaubten
SIDs und eine Liste mit Privilegien, die zu den jeweiligen SIDs gehören. Privilegien sind z. B. das Recht
einen Treiber zu laden (SeLoadDriverPrivilege) oder das Recht den Computer herunterzufahren
(SeShutdownPrivilege).[ 4 ] [ 24 ]



                                                     Seite - 33 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Alle Ressourcen, auf die ein Benutzer theoretisch zugreifen kann, haben eine so genannte
Discretionary Access Control List (DACL)6. Praktisch wird der Zugang auf die Ressource durch LSA
reglementiert. Das bedeutet, der Zugriff ist nur erlaubt, wenn eine der SIDs aus dem Token des
Benutzers in der DACL-Liste unter dem entsprechenden Privileg (Lesen, Schreiben usw.) zu finden ist.
[4]

Nach [ 4 ] lässt sich zur besseren Übersicht eine Einteilung in Subjekte und Objekte vornehmen

    1. Subjekte: Einheiten, die Aktionen durchführt – unter Windows sind das Prozesse

    2. Objekte: Sind die Empfänger einer Aktion – unter Windows zugriffsgeschützte Objekte mit
       zugehörigen DACL wie z. B. Dateien und Registry-Einträge



4.2.1 Rechte von Prozessen unter Windows
Startet ein am System angemeldeter Benutzer einen Prozess, so wird in dem Prozess-Kontext der
Token des Benutzers gespeichert. Fortan darf der Prozess (Subjekt) auf alle Ressourcen (Objekte)
zugreifen, auf die auch der Benutzer durch seinen Token Zugriff hat.

Ist nun unter Windows XP ein Administrator am System angemeldet und startet den Internet
Explorer, so hat der Browser denselben Token wie der Administrator. Surft der Administrator nun
beispielsweise über eine Seite, die den populären Animated-Cursor Exploit7 eingebaut hat, so stehen
dem Angreifer keinerlei Hindernisse im Weg. Er hat dank des hoch-privilegierten Token, mit der SID
des angemeldeten Administrators, vollständigen Zugriff auf das gesamte System.

Mit Windows 2000 wurde die Möglichkeit eingeführt, einen Kindprozess/Thread mit einem
eingeschränkten Token zu starten. Der eingeschränkte Token kann, ausgehend von dem Original-
Token, wie folgt definiert werden: Privilegien entfernen, SIDs entfernen, nicht erlaubte SIDs
hinzufügen. Wurde ein Thread mit einem eingeschränkten Token gestartet, so ist er an die neuen
Einschränkungen gebunden. Allerdings ist es möglich dem Thread mit dem eingeschränkten Token,
wieder den uneingeschränkten Token zuzuweisen. Weil diese Möglichkeit besteht, empfiehlt es sich
die API-Funktion CreateProcessAsUser( ) zu verwenden, so dass diese Möglichkeit nicht mehr
besteht. [ 24 ]

Mit Windows Server 2003 wurde es möglich die Privilegien eines Prozess-Token dauerhaft
einzuschränken. Diese Möglichkeit ist aber ungleich der des eingeschränkten Token, da hier der
Prozess dauerhaft seine eigenen Privilegien beschneidet. Alle von ihm erzeugten Kindprozesse erben,
nach demselben Prinzip wie bei Windows 2000, den Token des Elternprozesses, z. B. einen, in den
Privilegien eingeschränkten. Die Möglichkeit die SIDs des Token zu modifizieren besteht hier nicht.[
24 ]



6
  DACL wird in der Literatur häufig auch ohne das führende D, nur als Access Control List (ACL), verwendet.
Außerdem wird häufig der Ausdruck Security Descriptor als Synonym verwendet.
7
  Animated-Cursor Exploit: http://www.securityfocus.com/advisories/7814
                                                  Seite - 34 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




4.2.2 Windows Service Hardening
Da auch Windows-Services nichts weiter wie Prozesse sind, haben auch sie einen Token über den sie
im Betrieb ihre Berechtigungen erhalten. Für einen Angreifer sind damit die Standard-Services einer
Windows-Installation besonders interessant, weil sie aufgrund ihrer Funktionalität ständig laufen und
typischer Weise Systemfunktionen ausführen, die hohe Privilegien benötigen.

Mit Windows Server 2003 wurden zwei neue Service-Konten eingerichtet die den bisher
übermächtigen LocalSystem entlasten sollten: LocalService und NetworkService. Dabei sind die
beiden neuen Konten in den Rechten derart beschnitten, dass viele Services nicht mit ihnen arbeiten
können und deshalb den bisherigen LocalSystem weiterhin verwenden.[ 4 ]

Zur verbesserten Schadensbegrenzung wurde bei Windows Vista eine Reihe von Maßnahmen
eingeführt, die ein erheblich granulareres Rechtemanagement wie bisher ermöglichen:

    1. Service-spezifische SIDs: Jeder Service hat eine eigene SID. Sinnvoller Weise bekommen nur
       diejenigen Objekte in ihrer DACL die SIDs des speziellen Services eingetragen, die sie auch
       wirklich benötigen.[ 4 ]
    2. Eingeschränkte SIDs: Die RESTRICTED-SID kennzeichnet einen Prozess, der ein
       eingeschränktes Token enthält. [ 25 ] (nicht auf Services beschränkt)
    3. Reduzierte Privilegien für Services: Überflüssige Privilegien, wie das Debugging Privileg
       wurden den Diensten generell entzogen [ 4 ]
    4. Session 0 Isolation: Unter Windows Server 2003 und früheren Versionen, war es unter
       Umständen möglich seine Privilegien durch Angriffe auf Services mit höheren Privilegien
       innerhalb derselben Session zu erweitern. Diese Problem wird dadurch entschärft, dass nur
       noch Services in der Session 0 laufen und alle vom User gestarteten Anwendungen in
       eigenen Session (1 und höher) gestartet werden. [ 26 ] [ 27 ]
    5. UAC: siehe nächster Abschnitt (nicht auf Services beschränkt)



4.2.3 Rechte von Prozessen unter Windows Vista: UAC
Es ist zwar bekannt, dass es sich nicht empfiehlt mit Administratorrechten im Internet zu surfen, die
Konsequenz als normaler Benutzer an einem System zu arbeiten, haben jedoch nur wenige
Anwender gezogen. Der Hauptgrund hierfür ist die mangelnde Unterstützung seitens Windows. So ist
es bis zu der Version "Vista" nötig gewesen, eine Druckerinstallation oder das Stellen der Uhrzeit mit
Administratorrechten zu erledigen.

UAC steht für User Account Control und soll das Arbeiten als eingeschränkter Benutzer erleichtern.
Dazu fragt Windows Vista den Benutzer, falls nötig, automatisch nach dem Passwort, für das
Benutzerkonto, dass zum ausführen der Aktion nötig ist. Dabei ist die Interaktion mit den BS solange
gesperrt, bis der Benutzer auf die Anfrage reagiert hat.



                                             Seite - 35 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Im Detail verwendet UAC eine neue Technik namens Mandatory Integrity Control (MIC). MIC ist eine
Erweiterung für die DACLs um sogenannte Integrity Level (IL). Gespeichert werden diese neuen
Rechte in den Mandatory ACLs (MACL).

Als IL wurden folgende vier Möglichkeiten eingeführt:

       Low

       Medium (als Standard)

       High

       System

Jeder Prozess hat in seinem Token zusätzlich ein IL gespeichert. Will ein Prozess mit einem niedrigen
IL auf eine Ressource mit einem höheren IL zugreifen, so ist die oben beschriebene Interaktion mit
dem Benutzer vonnöten.

Damit UAC funktioniert wurde der LSA modifiziert, so dass es jetzt zwei Arten von Token gibt:

       einen gefilterten: ohne erweiterte Rechte

       einen gelinkten: mit allen Originalrechten

Außerdem muss der Programmierer im Manifest seiner Anwendung das benötigte IL angeben.

Standardmäßig werden alle Prozesse mit dem gefilterten Token ausgeführt. Der gelinkte Token wird
nur verwendet, wenn eine Applikation, aufgrund des Eintrags im Manifest, nach erweiterten Rechten
fragt. Dieses Verhalten führt auch dazu, dass selbst ein angemeldeter Administrator zuerst vom
System um Erlaubnis gefragt wird, wenn die Aufgabe ein höheres IL erfordert.

Als Beispiel für eine sinnvolle Anwendung des UAC kann der Internet Explorer betrachtet werden:

        Der Internet Explorer läuft per Voreinstellung mit Low-IL, was bedeutet, er kann nur auf
        Objekte     mit    LOW-IL     SIDs    zugreifen.     Standardmäßig       sind    das      nur
        %USERPROFILE%AppDataLocalLow und der Registry-Key HKCUSoftwareAppDataLow. Auf
        alle anderen Systemressourcen hat der Prozess des IE keinen Zugriff, was die Sicherheit stark
        erhöht.

Wird der IE mit aktivierten UAC betrieben, so wird diese Konstellation häufig als Protected Mode IE
(PMIE) oder auch Low Rights Internet Explorer (LoRIE) bezeichnet.

UAC ist die für den Benutzer offensichtlichste und auch eine der nützlichsten Sicherheitsmaßnahmen
zur Schadensbegrenzung. Allerdings empfinden viele Benutzer die ständige Nachfrage nach
erweiterten Rechten als störend. Außerdem funktionieren nicht alle Programme einwandfrei mit
UAC. Diese zwei Kritikpunkte führen dazu, dass viele Benutzer UAC Systemweit deaktivieren, was den
gesamten Nutzen hinfällig macht.



                                              Seite - 36 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



4.3 PatchGuard
PatchGuard wurde von Microsoft entwickelt und hat es zum Ziel Software von Drittherstellern daran
zu hindern Änderungen am Kernel vorzunehmen. Dadurch soll potentiell gefährlicher Software
erschwert werden sich einzunisten.

Eingeführt wurde PatchGuard mit den Windows-Versionen Server 2003 mit SP1 (64-Bit) und XP
Professional x64. Eine Portierung auf die x86-Versionen ist derzeit nicht geplant. [ 28 ]

Da PatchGuard eine Reihe von Unzulänglichkeiten besitzt, die das Umgehen des Schutzes relativ
einfach machen, ist der tatsächliche Nutzen in Bezug auf Schadsoftware fraglich. Häufig wird sogar
behauptet, PatchGuard sei lediglich ein Instrument von Microsoft, um die Dritthersteller dazu zu
zwingen, saubereren und stabileren Code zu schreiben. [ 29 ]

Viel Kritik an dem System gab es auch von den Herstellern von Sicherheits-Software, wie Anti-Viren-
Software und Rootkit-Detection-Tools, da sich diese Produkte häufig an den, durch dieses System
nicht mehr erreichbaren Stellen, eingenistet haben, um so von einem möglichst uneingeschränkten
Punkt aus operieren zu können. [ 29 ]

Eine umfassende Analyse der neusten Version (v3) von PatchGuard ist im Internet unter [ 28 ] zu
finden.



4.4 Process Exection Block (PEB) Randomization

Überblick
Mit Windows XP SP28 wurde eine Funktionalität namens PEB-Randomization eingeführt. Ihr Ziel ist
es, Angriffe zu erschweren, die zur Codeausführung PEB-Einträge manipulieren. Im Process Exection
Block (PEB) sind vor allem für Angriffe interessante Funktions-Pointer gespeichert, wie ein Pointer auf
die Funktion ExitProcess().



Funktionsweise
Beim Programmstart wird die Basisadresse des PEB dynamisch gewählt. Dadurch ist ein Remote-
Exploit9, dass den PEB als Einstiegspunkt verwendet, nicht mehr zuverlässig, da die zu
überschreibende Adresse des Funktions-Pointers in dem, jetzt an einem zufälligen Ort im Speicher
liegenden, PEB gespeichert ist.




8
 Funktionalität ist auch in Server 2003 SP1 / Vista vorhanden
9
 Ein Remote-Exploit ist dadurch charakterisiert, dass der Angreifer entfernt über ein Netzwerk zugreift und
nicht an der Maschine selbst sitzt.
                                                  Seite - 37 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Schwachstellen
Wie sich ein einer Untersuchung von Symantec herausstellte [ 30 ] , beschränkt sich die Anzahl der
Möglichkeiten, für die Basisadresse des PEB, auf 16. Hinzu kommt noch, dass die Adresse in 20% aller
Fälle gleich ist.



Effektiver Schutz
Die Möglichkeit verlässliche Remote-Exploits zu schreiben wurde reduziert. Die Option der Brute-
Force-Angriffe bleibt jedoch, mit einem recht hohen Erfolgsgrad, bestehen.



4.5 Heap-Schutz

Überblick
Wie in Abschnitt 3.1.6 beschrieben, kann es auch auf dem Heap zu Buffer-Overflows kommen. Um
den Schaden begrenzen zu können, wurden mit dem SP2 von Windows XP im Wesentlichen zwei
neue Features, für die von Windows bereitgestellte Heap-Verwaltung, eingeführt: [ 8 ]

       Cookies auf dem Heap (vergleichbar mit denen der GS-Funktionalität des Stacks)
       Plausibilitätsprüfung (sanity checking) der Headerinformationen der einzelnen Heap-
        Segmente



Funktionsweise Cookies
Bei jedem Aufruf von HeapAlloc() wird vor und nach jedem Puffer auf dem Heap ein 1-Byte
großes, mit Pseudo-Zufallszahlen initialisiertes Cookie, abgelegt. [ 8 ]

Im Betrieb wird vor jedem Entnehmen eines Blocks aus der "Frei-Liste" geprüft, ob der Wert des
Cookies noch der Selbe ist, wie zum Zeitpunkt der Initialisierung. Dazu wird der Wert mit dem des
separat gespeicherten Original-Cookies verglichen. Hat sich der Wert geändert, so wird zuerst ein
HeapDestory() ausgeführt und anschließend wird eine Exception geworfen. Falls diese nicht
behandelt wird, wird das Programm beendet. [ 8 ]

Außerdem wurde ein sogenannter Low Fragmentation Heap (LFH) entwickelt, der Primär der
Performancesteigerung dient. Dieser LFH ist ebenfalls durch ein Cookie geschützt. Allerdings durch
eins der Größe 4-Byte. Durch das größere Cookie, wird das erraten des Werts und die
Wahrscheinlichkeit für erfolgreiche Brute-Force-Angriffe, stark reduziert. [ 17 ]

Funktionsweise Plausibilitätsprüfung
Die "Frei-Listen" sind als doppelt verkette Listen aufgebaut. Die Pointer, die zur Verkettung der Liste
in beide Richtungen dienen, nennen sich Blink, für das vorhergegangene Element und Flink, für das
nächste Element. [ 8 ]

                                              Seite - 38 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Vor jedem Entnehmen eines Blocks aus der "Free-List" werden Blink und Flink auf ihre Plausibilität
hin geprüft. Das bedeutet, es wird geprüft, ob der der Blink aus dem Header des Folgeelements auch
tatsächlich auf das aktuelle Element zeigt. Dieselbe Prüfung wird in die andere Richtung vollzogen.
[8]



Schwachstellen
Innerhalb der Heap-Verwaltung gibt es nicht nur die oben genannten "Frei-Listen", sondern auch
Lookaside-Liste, die primär der Geschwindigkeitserhöhung dienen. Diese Listen sind nur einfach
verkette Listen und aus diesem Grund könnten die Pointer nicht auf Plausibilität hin geprüft werden..
Da aber unter XP mit SP2 der PEB und der Heap dynamische Startadressen haben, benötigt ein Brute-
Force-Angriff nach mehr wie 2000 Versuche, vorausgesetzt die Applikation läuft nach einem
fehlgeschlagenen Versuch weiter. [ 17 ]

Eine weitere Schwachstelle ist das nur 1-Byte große Cookie und die in Abschnitt 4.4 beschriebene,
schwache Zufallswahl des PEB-Speicherortes. Hinzu kommt noch die Tatsache, dass der LFH unter
Windows XP SP2 standardmäßig von keiner Applikation verwendet wird. [ 17 ]

Einen weiteren Angriff auf das Heap-Management beschreibt Alexander Anisimov. Dabei nutzt er die
Tatsache, dass die Cookie-Prüfung und die Plausibilitätsprüfung der Pointer nur unter bestimmten
Umständen stattfinden. So ist es Alexander Anisimov, unter einer Reihe von Voraussetzungen
möglich, die Blink- und Flink-Pointer derart zu manipulieren, dass ein wahlfreier Schreibzugriff von
1016 Bytes möglich ist. Die Voraussetzungen für diese Art von Angriff sind jedoch derart exotisch,
dass Microsoft an dieser Stelle keinen unmittelbaren Handlungsbedarf sieht. [ 8 ]

Eine etwas robustere Methode, als die von Alexander Anisimov, beschreibt Nicolas Falliere in [ 15 ] .
Dabei ist das Prinzip das gleiche: Es werden Blink- und Flink-Pointer überschrieben, dadurch kommt
es zu einem wahlfreien Schreibzugriff der Größe von 4-Byte. Dabei unterliegt dieser Angriff aber den
gleichen Problemen wie der Angriff, auf die einfach verketteten Lookaside-Listen. Es ist also auch hier
nur ein Brute-Force-Angriff möglich. [ 15 ]

Nach [ 31 ] werden die Lookaside-Listen in den aktuellen Windows-Versionen nicht mehr verwendet,
wodurch auf ihnen basierende Angriffe nicht mehr möglich sind.



Effektiver Schutz
Unter Windows XP mit SP1 war es aufgrund der fixen Speicherorte des PEB und des Heaps, sowie des
Fehlens, der oben beschrieben Neuerungen, möglich zuverlässige Remote-Exploits zu schreiben [ 17 ]

Mit Windows XP SP2 wurden Maßnahmen eingeführt, die das Entwickeln zuverlässiger Exploits
deutlich erschwert haben. Dennoch sind Brute-Force-Angriffe weiterhin möglich.

Hinweis: Die in diesem Abschnitt beschriebenen Schwachstellen und Verbesserungen beziehen sich
ausschließlich auf das Heap-Management von Windows. Programme die ihr eigenes Heap-

                                              Seite - 39 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Management implementieren, profitieren von keiner der hier vorgestellten Neuerungen. Der Heap-
Schutz wurde von Microsoft dennoch durch andere, in diesem Dokument vorgestellte
Sicherheitsfunktionen, stark verbessert. Zu nennen sind in diesem Zusammenhang vor allem DEP,
SafeSEH und ASLR (Vista).

Insgesamt hat sich die Gefahr durch Heap-basierende Angriffe stark verringert. So dass sich Angriffe
zukünftig verstärkt auf Anwendungsdaten konzentrieren werden, die auf dem Heap liegen (frei
übersetzt nach [ 9 ] ).




4.6 Visual Studio 2005 - Der "Windows-Compiler"
Wie jedes andere Programm auch, müssen die Quelldateien eins Betriebssystems mit einem
Compiler in ausführbare Dateien übersetzt werden. Im Falle von Windows kommt bei Microsoft
Visual Studio (VS) zum Einsatz [ 32 ] .

Im Folgenden werden die, für Buffer-Overflows relevanten, Funktionen von VS 2005 vorgestellt und
mit den Verbesserungen gegenüber früher Schutzfunktionen verglichen. Der Focus wird hierbei auf
VS 200510 liegen, da dies der Compiler ist, mit dem das Betriebssystem Windows Vista übersetzt
wird. Außerdem wird der effektive Nutzen der Maßnahmen diskutiert.




4.6.1 Standard Annotation Language (SAL)


Option            Beschreibung [ 33 ]                             Standard           Windows Binaries

/analyze          Codeanalyse in der Enterprise-Version            -                 Windows Vista
(Compiler)




Überblick
SAL dient der Compiler gestützten Qualitätssicherung des Codes und ist in den Versionen "Team
System" und "Team Edition" von VS 2005 mittels der Option /analyze verfügbar. Ihr Ziel ist es dem
Entwickler beim Übersetzen seiner Programme Hinweise zu geben, an welcher Stelle der
Programmcode möglicher weise nicht so arbeitet wie erwartet. Die Hauptaufgabe von SAL ist es
dabei, auf potentielle Buffer-Overflows aufmerksam zu machen. [ 31 ] [ 34 ]


10
  In den verwendeten Quellen ist häufig die Rede von dem „Whidbey-Compiler“. Whidbey ist der Codename
für Visual Studio 2005
                                               Seite - 40 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista




Funktionsweise
Beim Erstellen des Quellcodes muss der Programmierer sogenannte Annotations in seinen Code
einbringen. Zum Beispiel die Annotation __out_ecount, wie in folgendem Beispiel zu sehen ist:

void FillString(
      __out_ecount(cchBuf) TCHAR* buf,
      size_t cchBuf,
      char ch) {

    for (size_t i = 0; i < cchBuf; i++)             {
      buf[i] = ch;
    }
}

Quelle: [ 34 ]


Beim Kompilieren mit der /analyse Option wird VS in diesem Beispiel folgende Ausgabe erstellen:

c:codesaltestsaltest.cpp(54) : warning C6203: Buffer overrun for non-stack buffer 'b' in
call to 'FillString': length '420' exceeds buffer size '400'

c:codesaltestsaltest.cpp(54) : warning C6386: Buffer overrun: accessing 'argument 1', the
writable size is '200*2' bytes, but '420' bytes might be written: Lines: 53, 54

c:codesaltestsaltest.cpp(54) : warning C6387: 'argument 1' might be '0': this does not
adhere to the specification for the function 'FillString': Lines: 53, 54

Quelle: (43)



Dabei gibt es eine Reihe weiterer Annotations, die der Programmierer in seinen Code verwenden
kann.

Da die ursprüngliche Hauptaufgabe von SAL darin bestand Buffer zu beschreiben, auf die lesend und
schreibend zugegriffen wird, versehen die Entwickler von Windows Vista alle entsprechenden
Funktionen mit Annotations, bevor das Produkt an den Kunden ausgeliefert wird. [ 34 ]



Schwachstellen
Da SAL eine Meta-Sprache ist, die zur Unterstützung von statischen Analysetools entwickelt wurde,
können dem entsprechend nur Fehler gefunden werden, die beim Kompilieren erkennbar sind.
Buffer-Overflows, die zur Laufzeit auftreten, weil die Puffergröße beispielsweise von einer
Benutzereingabe abhängt, können nicht erkannt werden. [ 34 ]




                                           Seite - 41 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Effektiver Nutzen
Durch die Verwendung von SAL ist der Programmierer gezwungen sich mehr Gedanken über den
Ablauf seiner Funktionen zu machen. Zusammen mit einer konsequenten Anwendung und der
Berücksichtigung der Compilerwarnungen, hätten mit SAL bereits in der Vergangenheit einige Buffer-
Overflows vermieden werden können.

Da SAL eine automatisierte Neuerung zur Verbesserung der Code-Qualität ist, ist SAL eine effektive
Maßnahme gegen die Entstehung von Sicherheitslücken.




4.6.2 Optimierte Anordnung von Stack-Elementen (O2)

Option               Beschreibung [ 33 ]                                 Standard                Windows
                                                                                                 Binaries

/O2                  Erstellt schnellen Code.                            bei Release-Build       Theoretisch alle
(Compiler)




Überblick
Seit VS 2003 wird, beim Erstellen eins Release-Builds11, die Anordnung von Variablen (Puffern) auf
dem Stack optimiert. Dabei ist es das Ziel potentiell verwundbare Puffer an einer ungefährlicheren
Position anzulegen [ 35 ] .



Funktionsweise
Wie auf der Abbildung 4-2 zu erkennen ist, wird die lokale Variable auth_flag, vor dem potentiell
verwundbaren Puffer im Speicher angelegt. Da der Schreibzugriff immer nur in Richtung größer
werdender Adressen stattfindet, ist die Variable auth_flag in Sicherheit. Dasselbe Verfahren wird
auch bei dem Anlegen von Pointer angewendet.




11
     Standardmäßig ist bei dem Profil „Release“, in Visual Studio, die Compiler-Option O2 aktiviert
                                                     Seite - 42 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



                                                                   Stackframe               Stackframe
               Quellcode                                              Ohne                      Mit
                                                                   Optimierung              Optimierung              0000000h
 int check_authentication(char *password) {                                                                      (Kleine Adressen)
           int auth_flag = 0;
           char password_buffer[8];
                                                              password_buffer: char[32]       auth_flag: int




                                                                                                                                        Wachstumsrichtung
            strcpy(password_buffer, password);




                                                                                                                     Overflowrichtung
                                                               auth_flag: int     password_buffer: char[32]
            if(strcmp(password_buffer, "password") == 0)
                      auth_flag = 1;
            if(strcmp(password_buffer, "adminpw") == 0) Stack Frame Pointer (SFP) Stack Frame Pointer (SFP)
                      auth_flag = 1;
                                                               Return Adresse (RET)       Return Adresse (RET)
            return auth_flag;
 }
                                                                     Parameter                 Parameter


                                                                                                                    FFFFFFFFh
                                                                                                                 (Große Adressen)




Abbildung 4-2: Optimierte Anordnung von Stack-Elementen bei VS 2003




Effektiver Schutz
Durch dieses Verfahren wird zwar der Schutz, von lokalen Variablen und Pointern, vor Buffer-
Overflows gewährt. Nicht geschützt sind diese Ziele jedoch vor Buffer-Underruns12.

Die größte Gefahr bleibt nach wie vor bestehen. Wie in der Abbildung 4-2 zu sehen ist, ist es immer
noch möglich, ausgehend von dem verwundbaren Puffer, die Returnadresse (RET) zu überschreiben
und so beliebigen Code zur Ausführung zu bringen. Kombiniert mit den im Folgenden vorgestellten
Mechanismen ergibt sich dennoch ein echter Mehrwert durch diese Optimierungen.




4.6.3 Puffer-Sicherheitsüberprüfung (GS)

Option           Beschreibung [ 33 ]                                                  Standard [ 35 ]               Windows
                                                                                                                    Binaries [ 32 ]

/GS              Puffer-Sicherheitsüberprüfung                                        aktiviert                     teilweise bei
(Compiler)                                                                            seit VS 2005                  XP SP2, Vista

/GS-             Puffer-Sicherheitsüberprüfung deaktivieren                           deaktiviert                   teilweise bei
(Compiler)                                                                            seit Visual C++ 2002          XP SP2, Vista




12
     Zu einem Buffer-Underrun kann es beispielsweise durch fehlerhafte Pointerarithmetik kommen
                                                           Seite - 43 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista



Überblick
Hinter dem Compilerswitch /GS verbirgt sich eine Funktion namens Buffer Security Check. Das
Haupt-Ziel dieser Funktion ist es, ein Überschreiben der Return-Adresse zu erkennen und das
Programm gegebenenfalls abzubrechen. Unter Linux gibt es mit StackGuard und ProPolice Lösungen,
die diesem Prinzip ebenfalls folgen.

Verfügbar ist diese Option seit Visual C++ 2002 und standardmäßig aktiviert ist die Option seit VS
2005[ 35 ] . Dabei gilt es allerdings zu beachten, dass sich die Implementierungsdetails und damit der
effektive Schutz, seit der ersten Version stark verändert haben. Im Folgenden wird nur auf die
Implementierung von VS 2005 eingegangen.

Seit Windows XP SP2 bzw. Windows Server 2003 ist die Option auch in den meisten Windows-
Binaries Standard. [ 32 ]



Funktionsweise
Programme, die mit der /GS Option kompiliert wurden, initialisieren nach dem Programmstart 4Byte
großes Master-Cookie, mit nicht vorhersagbarem Inhalt. Wird anschließend im Programmverlauf eine
geschützte Funktion aufgerufen, so wird innerhalb des Funktionsprologs eine Kopie des Master-
Cookies, wie auf Abbildung 4-3 zu sehen, auf dem Stack platziert. Wichtig ist hierbei vor allem die
Position des Cookies auf dem Stack: es liegt nach den potentiell verbundbaren lokalen Variablen und
vor der Return-Adresse. Hat die Funktion ihre Arbeit erledigt, so wird innerhalb des Funktionsepilogs
der momentane Wert, des auf dem Stack liegenden Cookies, mit dem Master-Cookie verglichen.
Sollte innerhalb der Funktion ein Buffer-Overflow vorgekommen sein, der bis zu dem Cookie reicht,
so wird das an dieser Stelle erkannt. Gegebenenfalls wird anschließend eine Ausnahme geworfen
und falls diese nicht entsprechend behandelt wird, wird das Programm wird mit einer Fehlermeldung
abgebrochen. [ 32 ]




                                             Seite - 44 -
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista
Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista

Contenu connexe

Tendances

Agorum core-entwickler-dokumentation-6 4-0
Agorum core-entwickler-dokumentation-6 4-0Agorum core-entwickler-dokumentation-6 4-0
Agorum core-entwickler-dokumentation-6 4-0agorum Software GmbH
 
Agorum core-administrations-handbuch-6 4-0a
Agorum core-administrations-handbuch-6 4-0aAgorum core-administrations-handbuch-6 4-0a
Agorum core-administrations-handbuch-6 4-0aagorum Software GmbH
 
Privacy handbuch win
Privacy handbuch winPrivacy handbuch win
Privacy handbuch winbigkos2
 
Das "Privacy Handbuch"
Das "Privacy Handbuch"Das "Privacy Handbuch"
Das "Privacy Handbuch"Dominik Wind
 
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008Thomas Schoessow
 
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...teena77
 
Einsteiger zertifizierung des LPI
Einsteiger zertifizierung des LPIEinsteiger zertifizierung des LPI
Einsteiger zertifizierung des LPIMichael M. Bosbach
 
Der Linux Schulserver
Der Linux Schulserver Der Linux Schulserver
Der Linux Schulserver heiko.vogl
 
Handbuch de
Handbuch deHandbuch de
Handbuch degordem
 
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.TechDivision GmbH
 
Final Opentrans 2.0 Rfq
Final Opentrans 2.0   RfqFinal Opentrans 2.0   Rfq
Final Opentrans 2.0 Rfqguest6f1fb4
 
411793375 terex-tc35-pdf
411793375 terex-tc35-pdf411793375 terex-tc35-pdf
411793375 terex-tc35-pdfIoanBurbulea
 
Sappres Netweaver Identity Management
Sappres Netweaver Identity ManagementSappres Netweaver Identity Management
Sappres Netweaver Identity Managementgueste2a899
 
Best Practice Guide
Best Practice GuideBest Practice Guide
Best Practice Guideguestc141a6
 

Tendances (19)

Agorum core-entwickler-dokumentation-6 4-0
Agorum core-entwickler-dokumentation-6 4-0Agorum core-entwickler-dokumentation-6 4-0
Agorum core-entwickler-dokumentation-6 4-0
 
BSI Audit
BSI AuditBSI Audit
BSI Audit
 
Agorum core-administrations-handbuch-6 4-0a
Agorum core-administrations-handbuch-6 4-0aAgorum core-administrations-handbuch-6 4-0a
Agorum core-administrations-handbuch-6 4-0a
 
Privacy handbuch win
Privacy handbuch winPrivacy handbuch win
Privacy handbuch win
 
Das "Privacy Handbuch"
Das "Privacy Handbuch"Das "Privacy Handbuch"
Das "Privacy Handbuch"
 
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008
Bedienungsanleitung Schlüsselverwaltung TSObjektkey 2008
 
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...
Besser php programmieren - Von der Klasse über Unittests, Cruisecontrol, Seli...
 
B8 Handbuch
B8 HandbuchB8 Handbuch
B8 Handbuch
 
Einsteiger zertifizierung des LPI
Einsteiger zertifizierung des LPIEinsteiger zertifizierung des LPI
Einsteiger zertifizierung des LPI
 
German Original
German OriginalGerman Original
German Original
 
Der Linux Schulserver
Der Linux Schulserver Der Linux Schulserver
Der Linux Schulserver
 
Handbuch de
Handbuch deHandbuch de
Handbuch de
 
JAX B
JAX BJAX B
JAX B
 
Pr O St Doku
Pr O St DokuPr O St Doku
Pr O St Doku
 
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.
Agiles Projektmanagement – Projektentwicklung mit Scrum, Kanb an & Co.
 
Final Opentrans 2.0 Rfq
Final Opentrans 2.0   RfqFinal Opentrans 2.0   Rfq
Final Opentrans 2.0 Rfq
 
411793375 terex-tc35-pdf
411793375 terex-tc35-pdf411793375 terex-tc35-pdf
411793375 terex-tc35-pdf
 
Sappres Netweaver Identity Management
Sappres Netweaver Identity ManagementSappres Netweaver Identity Management
Sappres Netweaver Identity Management
 
Best Practice Guide
Best Practice GuideBest Practice Guide
Best Practice Guide
 

En vedette

Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
Creating "Secure" PHP Applications, Part 1, Explicit Code & QACreating "Secure" PHP Applications, Part 1, Explicit Code & QA
Creating "Secure" PHP Applications, Part 1, Explicit Code & QAarchwisp
 
Creating "Secure" PHP applications, Part 2, Server Hardening
Creating "Secure" PHP applications, Part 2, Server HardeningCreating "Secure" PHP applications, Part 2, Server Hardening
Creating "Secure" PHP applications, Part 2, Server Hardeningarchwisp
 
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)archwisp
 
WebDAV - The good, the bad and the evil
WebDAV - The good, the bad and the evilWebDAV - The good, the bad and the evil
WebDAV - The good, the bad and the evilTobias Schlitt
 
Connect your Javascript web app to ownCloud over the WebDAV interface
Connect your Javascript web app to ownCloud over the WebDAV interface Connect your Javascript web app to ownCloud over the WebDAV interface
Connect your Javascript web app to ownCloud over the WebDAV interface Ilian Sapundshiev
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerLuminary Labs
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsLinkedIn
 

En vedette (8)

Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
Creating "Secure" PHP Applications, Part 1, Explicit Code & QACreating "Secure" PHP Applications, Part 1, Explicit Code & QA
Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
 
Creating "Secure" PHP applications, Part 2, Server Hardening
Creating "Secure" PHP applications, Part 2, Server HardeningCreating "Secure" PHP applications, Part 2, Server Hardening
Creating "Secure" PHP applications, Part 2, Server Hardening
 
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)
Breaking RF Unlock Codes - Presented at TriKC 0x01 (November 2014)
 
WebDAV - The good, the bad and the evil
WebDAV - The good, the bad and the evilWebDAV - The good, the bad and the evil
WebDAV - The good, the bad and the evil
 
Connect your Javascript web app to ownCloud over the WebDAV interface
Connect your Javascript web app to ownCloud over the WebDAV interface Connect your Javascript web app to ownCloud over the WebDAV interface
Connect your Javascript web app to ownCloud over the WebDAV interface
 
WebDAV as Web API
WebDAV as Web APIWebDAV as Web API
WebDAV as Web API
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI Explainer
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving Cars
 

Similaire à Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista

Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...ArtemEger
 
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...Melanie Eibl
 
Large Scale Multilayer Perceptron
Large Scale Multilayer PerceptronLarge Scale Multilayer Perceptron
Large Scale Multilayer PerceptronSascha Jonas
 
Bachelorarbeit paul gerber.pdf
Bachelorarbeit paul gerber.pdfBachelorarbeit paul gerber.pdf
Bachelorarbeit paul gerber.pdfwissem hammouda
 
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE ProgrammiermodellVergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodelladesso AG
 
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...Sascha Jonas
 
Algorithmen und Applikationen zur interaktiven Visualisierung und Analyse ch...
Algorithmen und Applikationen zur interaktiven  Visualisierung und Analyse ch...Algorithmen und Applikationen zur interaktiven  Visualisierung und Analyse ch...
Algorithmen und Applikationen zur interaktiven Visualisierung und Analyse ch...Frank Oellien
 
Maven Definitive Guide De
Maven Definitive Guide DeMaven Definitive Guide De
Maven Definitive Guide Debouchri
 
C++ Standard Template Library
C++ Standard Template LibraryC++ Standard Template Library
C++ Standard Template Libraryguestfc11c0c
 
Dreamweaver cs5 hilfe
Dreamweaver cs5 hilfeDreamweaver cs5 hilfe
Dreamweaver cs5 hilfefersel2015
 

Similaire à Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista (20)

Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...
 
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...
Masterarbeit / Fakultät für Mathematik und Informatik / Lehrgebiet Datenverar...
 
Large Scale Multilayer Perceptron
Large Scale Multilayer PerceptronLarge Scale Multilayer Perceptron
Large Scale Multilayer Perceptron
 
Bachelorarbeit paul gerber.pdf
Bachelorarbeit paul gerber.pdfBachelorarbeit paul gerber.pdf
Bachelorarbeit paul gerber.pdf
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE ProgrammiermodellVergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
 
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...
Entwicklung eines Frameworks zum automatisierten Handel eines Multi-Broker-PA...
 
Algorithmen und Applikationen zur interaktiven Visualisierung und Analyse ch...
Algorithmen und Applikationen zur interaktiven  Visualisierung und Analyse ch...Algorithmen und Applikationen zur interaktiven  Visualisierung und Analyse ch...
Algorithmen und Applikationen zur interaktiven Visualisierung und Analyse ch...
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Dsvdoc
DsvdocDsvdoc
Dsvdoc
 
Maven Definitive Guide De
Maven Definitive Guide DeMaven Definitive Guide De
Maven Definitive Guide De
 
C++ Standard Template Library
C++ Standard Template LibraryC++ Standard Template Library
C++ Standard Template Library
 
Dreamweaver cs5 hilfe
Dreamweaver cs5 hilfeDreamweaver cs5 hilfe
Dreamweaver cs5 hilfe
 
Solution Guide
Solution GuideSolution Guide
Solution Guide
 
Laz Infos Svn0082
Laz Infos Svn0082Laz Infos Svn0082
Laz Infos Svn0082
 

Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista

  • 1. Studienarbeit Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Fachbereich Informationstechnik Studiengang Softwaretechnik und Medieninformatik Semester: SWT6 Name: Johannes Hohenbichler Datum: 20.08.2008  Version: 1.03 Betreuer: Dominik Schoop
  • 2. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista I Inhaltsverzeichnis I Inhaltsverzeichnis ......................................................................................................................... 1-2 II Abbildungsverzeichnis .................................................................................................................. 1-4 III Abkürzungsverzeichnis ................................................................................................................. 1-5 IV Toolsverzeichnis ........................................................................................................................... 1-6 V Besondere Darstellungsformen.................................................................................................... 1-7 1 Überblick ...................................................................................................................................... 1-8 2 Grundlagen – Systemarchitektur................................................................................................ 2-10 2.1 Speicherorganisation .......................................................................................................... 2-10 2.1.1 Virtueller Speicher ...................................................................................................... 2-10 2.1.2 Systemfunktionen....................................................................................................... 2-11 2.1.3 Speichersegmente ...................................................................................................... 2-13 2.1.4 Big- und Little-Endian ................................................................................................. 2-15 3 Grundlagen – Buffer Overflows .................................................................................................. 3-16 3.1 Buffer Overflows ................................................................................................................ 3-16 3.1.1 Off-By-One Buffer Overflows ..................................................................................... 3-17 3.1.2 Mit Buffer Overflows den Kontrollfluss ändern ......................................................... 3-18 3.1.3 Ausführen von beliebigem Code ................................................................................ 3-21 3.1.4 Klassische Stack-basierende Angriffe ......................................................................... 3-22 3.1.5 Exception Handler-basierende Angriffe ..................................................................... 3-25 3.1.6 Heap-basierende Angriffe .......................................................................................... 3-27 3.2 Shellcode und Payload ....................................................................................................... 3-29 3.2.1 Anforderungen an Shellcode ...................................................................................... 3-29 3.2.2 Besonderheiten von Windows-Shellcode .................................................................. 3-30 3.2.3 Was möglich ist........................................................................................................... 3-30 3.2.4 Abstraktes Beispiel: Portbind Shellcode für Windows ............................................... 3-31 4 Das Sicherheits-Ensemble von Windows und seine Schwachstellen ........................................ 4-32 4.1 Das Windows-Zwei-Schichten-Modell................................................................................ 4-32 4.2 Zugriffsrechte, Authentifizierung und Access Token.......................................................... 4-33 4.2.1 Rechte von Prozessen unter Windows ....................................................................... 4-34 4.2.2 Windows Service Hardening....................................................................................... 4-35 4.2.3 Rechte von Prozessen unter Windows Vista: UAC ..................................................... 4-35 Seite - 2 -
  • 3. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 4.3 PatchGuard ......................................................................................................................... 4-37 4.4 Process Exection Block (PEB) Randomization..................................................................... 4-37 4.5 Heap-Schutz........................................................................................................................ 4-38 4.6 Visual Studio 2005 - Der "Windows-Compiler" .................................................................. 4-40 4.6.1 Standard Annotation Language (SAL) ......................................................................... 4-40 4.6.2 Optimierte Anordnung von Stack-Elementen (O2) .................................................... 4-42 4.6.3 Puffer-Sicherheitsüberprüfung (GS) ........................................................................... 4-43 4.6.4 Sichere Ausnahmebehandlung (SafeSEH) .................................................................. 4-47 4.6.5 Data Execution Prevention (DEP) ............................................................................... 4-48 4.6.6 Address Space Layout Randomization (ASLR) ............................................................ 4-51 4.7 Abschließender Vergleich: XP gegenüber Vista ................................................................. 4-52 5 Fazit ............................................................................................................................................ 5-54 A Literaturverzeichnis .................................................................................................................... 5-56 B Quellcode ................................................................................................................................... 5-60 Seite - 3 -
  • 4. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista II Abbildungsverzeichnis Abbildung 2-1: 32-Bit-Windows Standard-Adresslayout nach [ 2 ] .................................................. 2-11 Abbildung 2-2: Prinzipielles Windows Prozess-Speicher-Layout nach [ 3 S. 182 ] und [ 4 S. 397 ] .. 2-13 Abbildung 2-3: Big-Endian im Speicher .............................................................................................. 2-15 Abbildung 2-4: Little-Endian im Speicher ........................................................................................... 2-15 Abbildung 3-1: Die Buffer buf0 und buf1 im Speicher ....................................................................... 3-18 Abbildung 3-2: Buffer Overflow von password_buffer in auth_flag .................................................. 3-21 Abbildung 3-3: Beispielhaftes Stackframe unter Windows allgemein ............................................... 3-22 Abbildung 3-4: Angriffsvektor ............................................................................................................ 3-23 Abbildung 3-5: Beispielhaftes Stackframe unter Verwendung von SEH [ 13 ] .................................. 3-27 Abbildung 4-1: Windows-Zwei-Schichten-Modell [ 4 ] ...................................................................... 4-33 Abbildung 4-2: Optimierte Anordnung von Stack-Elementen bei VS 2003 ....................................... 4-43 Abbildung 4-3: Beispielhaftes Stacklayout unter Verwendung von /GS bei VS 2005 [ 32 ] .............. 4-45 Seite - 4 -
  • 5. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista III Abkürzungsverzeichnis ASLR Address Space Layout Randomization BS Betriebssystem DACL Discretionary Access Control List DEP Data Execution Protection, auch bekannt als NX DoS Denial of Service Dll Dynamic Link Library EH Exception Handler GByte Gigabyte IE Internet Explorer LSA Local Security Authority Subsystem Service NX No eXecute, auch bekannt als DEP RET Returnaddress SAL Standard Annotation Language SEH Structured Exception Handling SFP Saved Frame Pointer SID Security Identifier VS Visual Studio Seite - 5 -
  • 6. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista IV Toolsverzeichnis Programm Bezugsmöglichkeit Windows power shell http://www.microsoft.com/windowsserver2003/technologies/management/power shell/default.mspx Strawberry Perl http://strawberryperl.com/ Process Explorer http://technet.microsoft.com/de-de/sysinternals/default(en-us).aspx Display Heap Windows Server 2003 Resource Kit Tools http://support.microsoft.com/kb/168609/de Global Flags Debugging Tools for Windows http://www.microsoft.com/whdc/devtools/debugging/default.mspx Seite - 6 -
  • 7. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista V Besondere Darstellungsformen In dieser Arbeit kommen im Wesentlichen vier spezielle Gestaltungselemente zur Anwendung 1. Begriffserklärungen und Hinweise sind in einer Box der folgenden Art untergebracht Begriffserklärungen und Hinweise Begriff: Beschreibung oder Definition des Begriffs bzw. Hinweistext 2. Kommandos Kommandos die auf der Kommandozeile eingegeben werden (cmd.exe) sind grau hinterlegt 3. Programmausgaben Programmausgaben sind orange hinterlegt 4. Quellcode / Ausschnitte aus Dateien Auszüge aus Quellcode / Konfigurationsdatein sind hellblau hinterlegt Seite - 7 -
  • 8. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 1 Überblick Ausgangssituation In der Vergangenheit waren Buffer-Overflows eines der Hauptsicherheitsrisiken für Software. In den letzten Jahren haben Softwarehersteller an diesem Problem intensiv gearbeitet und den Schutz stark erhöhen können. Das beweisen nicht zuletzt CVE1-Statistiken, die Windows Vista für das Jahr 2008 bis zum Erscheinen dies Dokuments, in dem Monat Juni, nur eine einzige Schwachstelle bescheinigen, die direkt auf Buffer-Overflows zurückzuführen ist. [ 1 ] Ziel dieser Arbeit Das Ziel dieser Arbeit ist es, einen Überblick zu schaffen, welche Mechanismen die Betriebssysteme Windows XP und Windows Vista zur Vermeidung und zur Schadensbegrenzung von Buffer Overflows bieten. Dabei wird auf die Schwachstellen der vorhandenen Mechanismen eingegangen und es werden Wege aufgezeigt, wie sich diese Schwachstellen praktisch auszunutzen lassen. Außerdem hat es diese Arbeit zum Ziel, die Neuerungen von Windows Vista, dem noch weiter verbreiteten Windows XP gegenüber zu stellen und so den sicherheitstechnischen Mehrwert von Windows Vista darzulegen. An wen richtet sich diese Arbeit Diese Arbeit richtet sich an alle Leser, die an der definierten Zielsetzung interessiert sind. Zum vollständigen Verständnis werden jedoch Grundkenntnisse der Programmiersprachen c und x86 Assembler vorausgesetzt. Für Buffer Overflows besonders relevante Punkte sind jedoch grundliegend beschrieben, so dass die Arbeit auch für Leser ohne Vorkenntnisse aus dem Themengebiet "Buffer- Overflows" verständlich sein sollte. Gliederung dieser Arbeit Die einzelnen Kapitel dieser Arbeit können für sich selektiv gelesen werden. Die beiden Grundlagenkapitel(Kapitel 2, 3) bauen jedoch aufeinander auf und sollten von Beginn bis Ende gelesen werden. Zudem wird im Hauptkapitel(Kapitel 4) auf die Angriffsmöglichkeiten bezug genommen, die im Grundlagenkapitel "Buffer Overflows" erklärt werden. In dem Kapitel 2 werden einige, für Buffer Overflows wichtige, Grundlagen zur Systemarchitektur vorgestellt. Der Fokus liegt hierbei speziell auf Eigenheiten des NT-Kerns der Betriebssystemfamilie Microsoft Windows. 1 National Vulnerability Database: http://nvd.nist.gov/ Seite - 8 -
  • 9. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista In dem Kapitel 3 wird eine Einführung in das Thema Buffer Overflows gegeben und es werden einzelne Codestücke präsentiert. Um die Übersichtlichkeit an dieser Stelle nicht unnötig zu beeinträchtigen sind die vollständigen Programme in einem Zip-Archiv(Kapitel B) zu finden. Ebenfalls im Kapitel B, finden sich Hinweise zum Übersetzen der jeweiligen Programme mit dem Compiler der Entwicklungsumgebung Visual Studio 2003 / 2005. Ziel dieses Kapitels ist es, die grundliegenden Konzepte von Buffer Overflows zu umreißen und einige windows-spezifische Angriffe zu erläutern. Das Hauptkapitel dieser Arbeit beschäftigt sich im Detail mit den verschiedenen Schutzmaßnahmen von Windows XP und Vista. Hierbei wird vor allem auf den effektiven Nutzen der einzelnen Maßnahmen eingegangen. In diesem Zusammenhang werden Implementierungs- und Designschwachstellen aufgezeigt und objektiv beurteilt. Dabei wird auf die im Kapitel 3 vorgestellten Angriffe eingegangen und es wird geklärt, warum die Angriffe noch, oder nicht mehr, funktionieren. Abschließend folg eine Zusammenfassung der wesentlichen Erkenntnisse der wesentlichen Erkenntnisse und kritischen Blick auf die derzeitigen Entwicklungen. Seite - 9 -
  • 10. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 2 Grundlagen – Systemarchitektur In diesem Kapitel werden Grundlagen aus den Bereichen Betriebssystem- und Rechnerarchitektur vorgestellt, die für ein fundiertes Verständnis von Buffer Overflows benötigt werden. Dabei wird speziell auf Eigenheiten des NT-Kernels, der Betriebssystemfamilie Microsoft Windows, eingegangen. 2.1 Speicherorganisation In diesem Abschnitt wird die Speicherorganisation von Windows XP und Windows Vista grundliegend erläutert um ein eine Basis für das Verständnis, der in Kapitel 3 beschriebenen Angriffstechniken, zu schaffen. 2.1.1 Virtueller Speicher Bei 32-Bit-Windows-Systemen steht einer Anwendung theoretisch der maximal adressierbare Speicher von 0x00000000 bis 0xFFFFFFFF zur Verfügung, was einer Größe von 2³² Byte = 4 GByte entspricht. Dabei ist die kleinste adressierbare Einheit ein Byte groß. Diese 4 GByte teilt Windows in zwei Hälften. Den höherwertigen Bereich verwendet es für Betriebssystemfunktionen und den niederwertigen Bereich für das Programm selbst. Die Trennung ist normalerweise, wie in der Abbildung 2-1 zu sehen ist, in der Mitte des Gesamtspeichers vorgenommen. Dank dem Konzept des virtuellen Adressraums, ist es dabei nicht wichtig, welche Menge an Arbeitsspeicher das System tatsächlich benötigt. Mit Hilfe der Hardware, zur Adressvirtualisierung, kann das Betriebssystem die Umsetzung von physikalischen in virtuelle Adressen erledigen, ohne dass sich ein laufendes Programm darum bemühen muss. Dabei kümmert sich das Betriebssystem automatisch um das Ein- und Auslagern von Speicher. Seite - 10 -
  • 11. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Hinweis 0000000h (Kleine Adressen) Unter 32-Bit-Windows lässt sich die Aufteilung auch mittels einer 00000000h Benutzermodus- Bootoption in das Verhältnis 3GB Prozesse (2GB) zu 1GB (Programmspeicher zu 7FFFFFFFh Systemspeicher) ändern. Unter 80000000h Systemspeicher 64-Bit-Windows-Varianten ist die (2GB) Speicheraufteilung nahezu 50% FFFFFFFFh zu 50% [ 2 S. 14ff ] . FFFFFFFFh (Große Adressen) Außerdem gibt es die Address Windowing Extensions (AWE) mit deren Hilfe sich der virtuelle Abbildung 2-1: 32-Bit-Windows Standard-Adresslayout nach [ 2 ] Speicher vergrößern lässt [ 2 S. 383ff ] 2.1.2 Systemfunktionen Im Zusammenhang mit Buffer Overflows ist weniger die im letzen Kapitel vorgestellte Speicheraufteilung interessant, sondern viel mehr die Art wie auf Systemfunktionen zugegriffen wird. Im normalen Programmbetrieb werden alle benötigten Systemfunktionen, wie beispielsweise sleep(), in den virtuellen Adressbereich der Anwendung eingeblendet. Beim Übersetzen des Quellcodes erzeugt der Compiler automatisch die Befehle zum Laden der nötigen Systemfunktionen. Die Adressen an denen die verschiedenen Systemfunktionen zu finden sind, sind innerhalb eines Systems normalerweise immer gleich. Unterschiede bezüglich der Funktionsadressen gibt es lediglich zwischen den einzelnen Windows-Versionen sowie zwischen den verschiedenen Service-Packs. Eine wirkliche Ausnahme entsteht allerdings durch Address Space Layout Randomization (ASLR), auf das in dem Kapitel 4.6.4 eingegangen wird. Ein Beispiel: Mit dem Programm dumpbin.exe, das Teil von Visual Studio 2005 ist, lässt sich die Basis-Adresse des Windows-Kernels ermitteln: PS C:WINDOWSsystem32> dumpbin /headers kernel32.dll | select-string - pattern "image base" 7C800000 image base (7C800000 to 7C906FFF) Um nun beispielsweise die Absolute Adresse der Sleep-Funktion zu ermitteln muss noch die relative Adresse, ausgehend von der Basis-Adresse, ermittelt werden: Seite - 11 -
  • 12. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista PS C:WINDOWSsystem32> dumpbin /all kernel32.dll | select-string -pattern "sleep" 7C8087E0: 73 6F 75 72 63 65 00 53 6C 65 65 70 00 53 6C 65 source.Sleep.Sle 831 33E 00002442 Sleep 832 33F 0000239C SleepEx Aus der Basis-Adresse und der relativen Adresse ergibt sich die absolute Adresse der Funktion sleep() 7c800000h + 00002442h = 7c802442h Wie bereits erwähnt ist diese Adresse bei jedem Programmstart, unter Berücksichtigung der oben genannten Ausnahmen, gleich. Tools-Hinweis Die obigen Kommandos wurden nicht in der normalen Eingabeaufforderung, sondern in der kostenlos über Microsoft erhältlichen Powershell, ausgeführt. Der Grund hierfür ist, dass in der Powershell die Funktion select-string() zur Verfügung steht. Bezugsmöglichkeiten: siehe Toolsverzeichnis Seite - 12 -
  • 13. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 2.1.3 Speichersegmente Der einem Prozess zur Verfügung stehende virtuelle Speicher, teilt sich wie in Abbildung 2-2 zu sehen ist, in verschiedene Segmente auf. Wichtig sind hierbei vor allem die relative Position der Segmente zueinander, sowie die Richtung des Schreibzugriffs. Der Schreibzugriff erfolgt immer von einer Basisadresse in Richtung der höherwertigen Adressen. Prozess-Speicher Beispielhaftes Stackframe 0000000h (Kleine Adressen) Gespeicherte Register der Heap aufrufenden Funktion Lokale Variablen Stack Frame Pointer (SFP) Schreibrichtung Stack Return Adresse (RET) Parameter .TXT .DATA Heap entspricht einer Dynamischen .BSS Erweiterung des BSS-Segments (langlebige Daten) Stack Registerinhalte FFFFFFFFh Systemspeicher lokale Variablen (Große Adressen) Funktionsparameter (kurzlebige Daten) .TXT Programmkode .DATA Initialisierte globale und statische Variablen .BSS Nicht initialisierte globale und statische Variablen Wachstumsrichtung Abbildung 2-2: Prinzipielles Windows Prozess-Speicher-Layout nach [ 3 S. 182 ] und [ 4 S. 397 ] Seite - 13 -
  • 14. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Stack Der Stack ist das Speichersegment, das zum Ablegen kurzlebiger Daten dient. So werden auf dem Stack beispielsweise lokale Variablen und Funktionsparameter abgelegt. In Abbildung 2-2 ist, auf der rechten Seite, ein typisches Stackframe abgebildet. Anhand der Pfeile, die die Wachstumsrichtung angeben, lässt sich erkennen, dass der Stack von großen Adressen in Richtung kleiner Adressen wächst. Das Prinzip eines Stacks, oder auch Stapelspeicher genannt, ist folgendes: 1. Sollen neue, kurzlebige Daten zwischengespeichert werden, so werden diese auf die bereits auf dem Stack liegenden Daten von oben aufgelegt. Die Operation zum Auflegen neuer Daten nennt sich "push". 2. Werden die auf dem Stack gelegten Daten zur Verarbeitung benötigt oder muss der Stack aufgeräumt werden, so wird von oben, Stück für Stück, abgebaut. Die Operation zum Herunternehmen von vorhandenen Daten nennt sich "pop". Wenn innerhalb eines Prozesses eine Funktion aufgerufen wird, so werden die benötigten Daten, wie in der Abbildung 2-2 auf der rechten Seite zu sehen ist, auf dem Stack abgelegt (push). Ist die Funktion beendet, so werden die Daten in umgekehrter Reihenfolge wieder vom Stack genommen (pop). Der Teil des Maschinencodes, der ein Stackframe aufbaut, nennt sich Prolog und der abbauende Teil nennt sich Epilog. Genauere Informationen zur Funktionsweise des Stacks können in [ 5 ] gefunden werden. Heap Der Heap ist der dynamische Speicherbereich eines Prozesses. Er kann dynamisch in seiner Größe wachsen, somit kann der Prozess dynamisch Speicher anfordern und freigeben. Der Heap ist in Abbildung 2-2 im Bereich der niedrigen Speicheradressen zu sehen, was für Windows typisch ist. Außerdem ist in der Abbildung 2-2 dargestellt, dass der Heap in Richtung großer Adressen wächst. Inhaltlich gesehen werden auf dem Heap, verglichen mit dem Stack, langlebigere Daten abgelegt. Ein Prozess hat typischer Weise mehrere Heaps. Ein neuer Heap kann z. B. mit der Windows-API- Funktion HeapCreate() angefordert und mit HeapDestroy() wieder freigegeben werden. Das Anfordern eines Speicherbereiches erfolgt anschließend mit HeapAlloc() und wird analog mit HeapFree() wieder freigegeben. Bei Verwendung des c++-Operators new, wird automatisch Speicher von dem Standard-Heap des Prozesses angefordert. Seite - 14 -
  • 15. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Wie bereits angedeutet, hat der Programmierer die Wahl. Entweder er verwendet die von der Windows-API bereitgestellten Funktionen für das Heap-Management, oder er verwendet seine eigene Heap-Verwaltung. [ 6 ] Die von Windows standardmäßig verwendete Heap-Verwaltung organisiert den Speicher in einer Reihe unterschiedlicher Listen, die zusammen den gesamten Heap beschreiben. Dabei gibt es unter anderem, eine Liste für noch freie, kleine Speicherblöcke mit Größe 4KByte und eine separat geführte Liste, die alle belegten Blöcke enthält. Zudem werden temporären Listen erstellt, die der Geschwindigkeitserhöhung dienen. [ 7 ] [ 8 ] [ 9 ] 2.1.4 Big- und Little-Endian Als Big- und Little-Endian, werden zwei verschiedene Darstellungsformen von Werten, im Speicher bezeichnet. Beispiel: Darstellung der Speicher-Adresse AABBCCDD Hex = 2864434397 Dez im Speicher Big-Endian: Das höchstwertige Paar steht ganz links, wie in Abbildung 2-3 zu sehen. Big-Endian AA BB CC DD 00000000h FFFFFFFFh Abbildung 2-3: Big-Endian im Speicher Little-Endian: Das höchstwertige Paar steht ganz rechts, wie in Abbildung 2-4 zu sehen. Little-Endian DD CC BB AA 00000000h FFFFFFFFh Abbildung 2-4: Little-Endian im Speicher Seite - 15 -
  • 16. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 3 Grundlagen – Buffer Overflows Ziel dieses Kapitel ist es, dem Leser die Grundkonzepte von Buffer Overflows zu vermitteln um so die von Windows XP und Windows Vista verwendeten Schutzmechanismen besser beurteilen zu können. Im Folgenden sind immer nur die unmittelbar relevanten Codeabschnitte gelistet. Die vollständigen Programme mit Hinweisen zum Übersetzen und zugehöriger Programmausgaben sind im Anhang zu finden. Wichtiger Hinweis für Selbstversuche Mit Visual Studio 2005 lassen sich die meisten Beispielprogramme nicht ohne weiteres mit den erwarteten Ergebnissen übersetzen. Der Grund hierfür sind Optimierungen und Schutzmechanismen, die der Visual Studio 2005 Compiler verwendet. Hinweise zum „richtigen“ Übersetzen, sowie die vollständigen Programme finden sich jeweils im Anhang 3.1 Buffer Overflows Im Kontext der Programmiersprache c, ist ein Buffer ein zusammenhängender Speicherbereich, der Daten desselben Typs beinhaltet. In c werden Buffer häufig auch als Arrays bezeichnet. Ein primitiver Buffer kann in c beispielsweise folgende Form haben: char buf0[4] = "ABC0"; In diesem Fall ist der Buffer 4 Byte groß und ist ein String-Buffer. Dabei dient das letzte Byte zur Terminierung des Strings. Ein Buffer Overflow, oder auch Puffer-Überlauf, passiert demnach, wenn die Grenzen des vorgesehenen Speicherbereichs überschritten werden. Die Folgen für das Gesamtsystem können dabei sehr unterschiedlich ausfallen. So zeigt sich die gefährliche Seite von Buffer Overflows, wenn es dem Angreifer möglich ist beliebigen Code einzuschleusen und seine Ausführung zu erreichen. In diesem Fall stehen dem Angreifer nahezu unbegrenzte Möglichkeiten zur Verfügung. Von einem primitiven Beenden der verwundbaren Software bis hin zum Erstellen von Benutzerkonten oder dem installieren eines Root- Kits (Details zu Angriffen dieser Art in dem Kapitel 3.1.4). Seite - 16 -
  • 17. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Bei weniger kritischen Programmierfehlern kann es zu inkonsistenten Daten kommen die nicht zwangsläufige einen Programmabsturz nach sich ziehen müssen und somit lange unentdeckt bleiben können. Damit beeinträchtigen diese Fehler zwar den Nutzen der Software, die Angriffsmöglichkeiten können aber, wie das Beispiel im nächsten Abschnitt zeigt, recht beschränkt sein. 3.1.1 Off-By-One Buffer Overflows Ein klassischer Buffer Overflow im Zusammenhang mit c-Strings ist der "Off-By-One"-Fehler. Dabei vergisst der Programmierer entweder den bereits erwähnten String-Terminator mitzuzählen, oder er ist bei der indexzählweise der Arrays unaufmerksam. Mit folgenden Programmausschnitten sollen die Folgen eines Off-By-One Overflows verdeutlicht werden. Das vollständige Programm mit Compiler-Optionen und vollständiger Ausgabe sind im Quellcode-Archiv zu finden (Kapitel B). Quellcode: Kapitel-3-1-1_off-by-oneoff-by-one.c //gekürzt... char buf0[] = "1234"; char buf1[4] = "ABC0"; strcpy(buf1,buf0); Führt man das vollständige Programm aus, so erhält man folgende Ausgabe ~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Adresse von buf0: 0012FF68 sizeof(buf0): 5 Byte Adresse von buf1: 0012FF64 sizeof(buf1): 4 Byte buf0 1234 buf1 ABC ~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ buf0 buf1 1234 Betrachtet man diese Programmausgabe genauer, so fällt auf, dass der Buffer buf1 zwar den gewünschten Inhalt erhalten hat, buf0 seinen Inhalt aber offenbar verloren hat. Was hier passiert ist schnell erklärt: Es kommt zu einen Buffer Overflow, weil der Programmierer vergessen hat, dass der Compiler an der Stelle char buf0[] = "1234"; automatisch ein 5-Byte großes, null-terminiertes String-Array erstellt. Deshalb überschreibt strcpy() mit dem '0' den ersten Teil des Buffers buf0, der nach buf1 im Speicher liegt. Die Abbildung 3-1 soll diesen Vorgang verdeutlichen. Hinweis zum Speicherlayout: Die Anordnung von Variablen im Speicher wird immer zum Zeitpunkt des Übersetzens festgelegt und wird von dem Compiler entschieden. Der Compiler folgt beim Erstellen des Speicherlayouts zwar festen Regeln, eine feste Beziehung zwischen der Reihenfolge der Seite - 17 -
  • 18. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Definition/Deklaration im Quellcode und dem tatsächlichen Layout existieren jedoch nicht. Zur Erinnerung: Der Heap unterliegt anderen Regeln, da dieser Speicher erst zur Laufzeit erstellt wird. buf1 buf0 0x0012FF64 0x0012FF68 1. Vorher A B C '0' 1 2 3 4 '0' 2. strcyp() 3. Nachher 1 2 3 4 '0' 2 3 4 '0' Abbildung 3-1: Die Buffer buf0 und buf1 im Speicher Gibt man mittels printf("buf0 %sn", buf0); den String buf0 aus, so wird nichts ausgegeben, weil der String-Terminator bereits an erster Stelle steht und printf() mit dem Formatstring %s, nur Zeichen bis zu dem ersten vorkommenden '0' ausgibt. Off-By-One Buffer-Overflows sind in den meisten Fällen nichts weiter als ärgerliche Programmierfehler die relativ schwer zu finden sind. Der Nutzen ist für Angreifer bei Programmierfehlern dieser Art meist sehr beschränkt. Hauptsächlich deshalb, weil der Speicherbereich der durch den Benutzer beeinflusst werden kann, mit einer Speichereinheit (im obigen Beispiel 1 Byte) nur sehr wenig Spielraum bietet. Die Folgen hängen aber davon ab, welche Art von Informationen in den überschreibbaren Bereichen gespeichert sind. Wie interessant selbst ein Off-By-One Buffer-Overflow werden kann zeigt der folgende Abschnitt. 3.1.2 Mit Buffer Overflows den Kontrollfluss ändern Im dem folgenden Programm kann ein Buffer Overflow dazu verwendet werden eine Variable zu überschreiben, die wesentlich für den Ablauf des Programms ist. Quellcode: Kapitel-3-1-2_auth_overflowauth_overflow.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include "hacking.h" int check_authentication(char *password) { int auth_flag = 0; char password_buffer[8] = {'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F'}; printf("~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n"); printf("dump: password_buffern"); Seite - 18 -
  • 19. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista dump(password_buffer, 8); printf("dump: auth_flagn"); dump(&auth_flag, 4); strcpy(password_buffer, password); if(strcmp(password_buffer, "password") == 0) auth_flag = 1; if(strcmp(password_buffer, "adminpw") == 0) auth_flag = 1; printf("~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n"); printf("dump: password_buffern"); dump(password_buffer, 8); printf("dump: auth_flagn"); dump(&auth_flag, 4); return auth_flag; } int main(int argc, char *argv[]) { //... gekürtzt if(check_authentication(argv[1])) { printf("n-=-=-=-=-=-=-=-=-=-=-=-=-=-n"); printf(" Zugang freigegeben.n"); printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-n"); } else { printf("nZugang verweigert.n"); } getchar(); } Hinweis: die Funktion dump() ist in der Include-Datei hacking.h definiert. Sie gibt von einer, per ersten Parameter übergebenen Speicheradresse ausgehend, eine mit dem zweiten Parameter definierte, Menge an Speicherinhalten, aus. Das Programm übernimmt den ersten Parameter, den das Hauptprogramm übergeben bekommt und kopiert ihn in der Funktion check_authentication() in einen Buffer, der als Zwischenspeicher dient. Stimmt das Passwort mit einem der gespeicherten Passwörter überein, so wird die Variable auth_flag auf 1 gesetzt, was "wahr" entspricht. Das Hauptprogramm nimmt den Rückgabewert der Funktion check_authentication() um Zugang zu einem geschützten Bereich zu gewähren oder zu verweigern. Seite - 19 -
  • 20. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Dabei gibt es in dem Programmfluss keinen Else-Zweig, der im Falle, keiner Übereinstimmung, die die Variable auth_flag auf 0 setzen würde. Der Programmierer hat diesen Fall durch das Initaialisieren mit 0 abgedeckt. Gegen dieses Vorgehen ist prinzipiell nichts einzuwenden. Das Problem mit dem obigen Code ist jedoch, dass die Funktion strcpy() unabhängig von der Größe des Zielbuffers password_buffer alle Zeichen bis zum ersten '0' kopiert. Diese Frage lässt sich mit Hilfe der Programmausgabe beantworten: ~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dump: password_buffer 0012FF54: 46 46 46 46 46 46 46 46 | FFFFFFFF dump: auth_flag 0012FF5C: 00 00 00 00 | .... ... gekürzt Wie hier zu sehen ist liegt die Variable auth_flag unmittelbar nach dem Buffer password_buffer im Speicher. Das bedeutet, sollte es dem Benutzer möglich sein, dem Programm als ersten Parameter mehr als acht Zeichen zu übergeben, so findet durch strcpy()ein Buffer Overflow in den Speicherbereich der Variable auth_flag statt. Startet man das Programm beispielsweise folgendermaßen: auth_overflow.exe 12345678A So erhält man folgende Ausgabe: ~~~ vorher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dump: password_buffer 0012FF54: 46 46 46 46 46 46 46 46 | FFFFFFFF dump: auth_flag); 0012FF5C: 00 00 00 00 | .... ~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dump: password_buffer 0012FF54: 31 32 33 34 35 36 37 38 | 12345678 dump: auth_flag); 0012FF5C: 41 00 00 00 | A... -=-=-=-=-=-=-=-=-=-=-=-=-=- Access Granted. -=-=-=-=-=-=-=-=-=-=-=-=-=- Wie zu sehen ist, hat der Benutzer mittels "12345678" acht Zeichen übergeben und noch ein zusätzliches 'A' angehängt. Wie in der Abbildung 3-2 zu sehen ist, wird das 'A' an die Speicheradresse 0x0012FF5C geschrieben. Der Rückgabewert der Funktion check_authentication() ist als int deklariert und wird durch das Programm als wahr oder falsch interpretiert. Da in der Programmiersprache c alle Werte ungleich 0 dem Wert "Wahr" entsprechen, wird im Falle eines Overflows der Zugang zum geschützten Bereich gewährt. Seite - 20 -
  • 21. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Overflowrichtung password_buffer auth_flag 0x0012FF54 0x0012FF5C 1. Vorher F F F F F F F F 0 0 0 0 2. strcyp() 3. Nachher 1 2 3 4 5 6 7 8 A 0 0 0 Abbildung 3-2: Buffer Overflow von password_buffer in auth_flag Dieses Beispiel zeigt eindrucksvoll, wie bereits ein einziges Zeichen an der richtigen Stelle genügen kann, um den Ablauf eines Programms wesentlich zu beeinflussen. Hinweis: Werden in diesem Programm mehr wie zwölf Zeichen übergeben, so stürzt das Programm ab, da Informationen auf dem Stack überschrieben werden, die für den weiteren Programmablauf benötigt werden. Abgesehen von Programmabstürzen, bietet diese Möglichkeit der Manipulation von Werten, einen interessanten Ansatzpunkt für weitere Angriffsmöglichkeiten, auf die in den folgenden Abschnitten eingegangen wird. Bei dem zuletzt vorgestellten Overflow, ist außerdem zu bedenken, dass nicht nur Zahlenwerte und Strings auf diese Weise manipuliert werden können. Es ist ebenso möglich Funktionspointer zu überschreiben. Auf diese Weise kann wahlfrei, vorhandener, oder auch eingeschleuster Programmcode ausgeführt werden. Damit kommt dieses Grundagekapitel zu den "nützlicheren" Angriffsmöglichkeiten mittels Buffer Overflows: Dem Ausführen von beliebigem Code. 3.1.3 Ausführen von beliebigem Code Um auf einem Zielsystem beliebigen Code auszuführen sind prinzipiell immer zwei Schritte nötig 1. Den auszuführenden Code in den Arbeitsspeicher des Zielsystems einschleusen 2. Eine Möglichkeit finden den eingeschleusten Code ausführen zu lassen Der erste Punkt lässt sich, ähnlich wie im Beispiel des letzten Abschnitts, mittels Programmparameter erreichen. Der zweite Punkt wird meistens durch das Manipulieren eins Funktionspointers erreicht. Dazu können vom Programmierer selbst angelegte Funktionspointer verwendet werden, oder aber auch auf dem Stack liegende Rücksprungadressen. [ 5 ] Seite - 21 -
  • 22. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Da die wirkliche Herausforderung mehr beim Ausführen, als beim Einschleusen liegt, werden in den restlichen Abschnitten dieses Kapitels einige Möglichkeiten vorgestellt, um den Code zur Ausführung zu bringen. 3.1.4 Klassische Stack-basierende Angriffe Stack-basierende Angriffe verfahren normaler weise streng nach dem bereits vorgestellten Schema: 1. Den auszuführenden Code in Arbeitsspeicher des Zielsystems einschleusen 2. Eine Möglichkeit finden den gespeicherten Code ausführen zu lassen Eine Möglichkeit den Code einzuschleusen haben wir bereits kennen gelernt: Die strcpy()-Funktion. Kommen wir also zu Punkt zwei: Dem Starten des eingeschleusten Codes. Bei klassischen Stack-basierenden Angriffen erfolgt das Starten des Codes durch das Manipulieren, der auf dem Stack gespeicherten Rücksprung-Adresse(RET). Betrachtet man die in Abbildung 3-3 dargestellte Speicherbelegung, so stellt man fest, dass RET an einer höheren Adresse im Speicher liegt als die lokalen Variablen. Beispielhaftes Stackframe 0000000h (Kleine Adressen) Gespeicherte Register der aufgerufenen Funktion Overflowrichtung Wachstumsrichtung Lokale Variablen Stack Frame Pointer (SFP) Return Adresse (RET) FFFFFFFFh Parameter (Große Adressen) Abbildung 3-3: Beispielhaftes Stackframe unter Windows allgemein Da lokal angelegte String-Arrays (Buffer) nichts weiter sind als die in Abbildung 3-3 dargestellten lokalen Variablen, ist es möglich, mit einer ausreichend großen Eingabe die Return-Adresse zu überschreiben. Damit der nächste Schritt, das Bestimmen der gewünschten Rücksprungadresse, einfacher ist, wird als Füllmenge (um RET zu erreichen) ein spezieller Assembler-Befehl verwendet: ein NOP. NOP steht für No OPeration und hat als einzige Aufgabe einen Maschinenzyklus zu verbrauchen. Danach führt Seite - 22 -
  • 23. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista die CPU die Befehlsverarbeitung mit dem darauf folgenden Befehl fort. Typischer Weise werden an dieser Stelle einige NOPs aufeinander folgen und bilden so einen NOP-Sled2. Um nun den eingeschleusten Code ausführen zu lassen, muss RET auf den ersten eingeschleusten Befehl zeigen. Wenn ein NOP-Sled verwendet wird, reicht es an dieser Stelle aus, einen beliebigen Punkt auf dem NOP-Sled zu treffen. Das ist besonders hilfreich, weil der Compiler mit dem das verwundbare Programm übersetzt wird möglicher Weise einige Optimierungen durchführt, die die exakte Positionsbestimmung des ersten Befehls erschweren können. Aus demselben Grund ist es üblich die gewünschte Return-Adresse mehrfach zu wiederholen. Besondere Aufmerksamkeit muss man der Ausrichtung der eingeschleusten Return-Adresse widmen. Das bedeutet man muss darauf achten, dass das erste Byte der selbst geschriebenen Return-Adresse auch wirklich über das erste Byte der alten Return-Adresse geschrieben wird. Ohne Verwendung eines Debuggers, lässt sich die korrekte Ausrichtung am einfachsten durch Probieren finden. Dabei sollten maximal vier Versuche nötig sein, da eine Adresse bei 32-Bit-Systemen nur 4 Byte groß ist und es somit nur vier Möglichkeiten gibt. Die absolute Adresse des Puffers wird meist mittels eines Debuggers ermittelt und auf x86-Systemen in Little-Endian-Anordnung eingebgeben. Interessant hierbei ist auch, dass der Stack eines Prozesses immer an derselben Adresse beginnt, was bedeutet, dass auch hier Probieren zum Ziel führen kann. Alternativ können zur Positionsbestimmung von Buffern auch Formatstring-Verwundbarkeit verwendet werden [ 5 ] . Auf Formatstrings wird in dieser Arbeit jedoch nicht eingegangen, weil sie nicht Teil des Themas "Buffer-Overflows" sind. Insgesamt wird dem auszunutzenden Programm die in Abbildung 3-4 dargestellte Kombination übergeben. Diese Kombination aus NOP-Sled, Payload3 und widerholter Return-Adresse, wird häufig als Angriffsvektor bezeichnet. RET NOP-sled Payload (mehrfach wiederholt) Abbildung 3-4: Angriffsvektor Bleibt noch zu erwähnen, dass die Speichermenge die für den Payload zur Verfügung steht, durch die Position der Return-Adresse begrenzt ist. Dieses Problem lässt sich aber durch einen relativen Sprung, nach den Speicherbereich mit der Return-Adresse umgehen. [ 10 ] 2 NOP-Sled/NOP-Sledge: Übersetzt: NOP-Schlitten. Das bedeutet, eine Folge von NOPs führen direkt, ohne jede weitere Funktion, zu dem nächsten „richtigen“ Maschinenbefehl. Beispielsweise dem ersten Befehl des Sehellcodes. 3 Der Payload/Shellcode besteht aus Maschinencode. Er ist der Teil des einzuschleusenden Codes, der die eigentliche Funktionalität beinhaltet. Häufig werden die Begriffe Payload und Shellcode als Synonym verwendet. Details zu Shellcode werden in dem Kapitel 3.2 behandelt Seite - 23 -
  • 24. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Tools-Hinweis Die felxibelste Möglichkeit einen Angriffsvektor zu erstellen ist mit der Programmiersprach Perl. Perl gibt es auch für Windows: Toolsverzeichnis IV Beispiel: stack-overflow.c im Quellcode-Ordner "Kapitel-3-1-4_stack-overflow" Eine Variante des Programms auth_overflow.c ist unter dem Namen stack-overflow.c Quellcode- Archiv zu finden. Dieses Programm bekommt seine Eingabe dabei nicht als Parameter, sondern liest von einer Datei. Außerdem wird nicht die Funktion strcpy() verwendet, sondern die Funktion memcpy(). Für diese Modifikationen gibt es zwei Gründe: 1. Mit der Windows-Eingabeaufforderung ist es nicht ohne weiteres möglich einem Programm, per Parameter Zeichen zu übergeben, die nicht "druckbar" sind. Da das auf die meisten Maschinenbefehle zutrifft, ist ein einfaches Einschleusen von Code über die Kommandozeile nicht möglich. Abhilfe würde an dieser Stelle das Programm cat4 schaffen mit dem man die Eingabe an das Programm umleiten kann. Cat ist aber nicht Bestandteil von Windows. Zudem besteht das Problem der Parameterübergabe mit nichtdruckbaren Zeichen auch in den meisten Debuggern. Aus diesem Grund liest das Demo-Programm aus einer Datei. 2. Wie in dem Abschnitt 2.1 beschrieben, liegt der für das Programm selbst zur Verfügung stehende Speicher im niederen Adressbereich. Deshalb ist die Wahrscheinlichkeit sehr hoch, dass der Buffer in dem der eingeschleuste Code liegt in einem Adressbereich liegt, der mit zwei führenden Nullen (0x00AABBCC) beginnt. Das bedeutet, dass strcpy() die Speicheradresse nicht mehr kopiert. Es gibt eine Reihe von Möglichkeiten den eingeschleusten Code trotz dieses strcpy()-Problems zur Ausführung zu bringen (beispielsweise "return to libc"). Um das Beispiel einfach zu halten wird dieses Problem an dieser Stelle durch die Verwendung von memcpy() umgangen. Trotz dieser zwei Vereinfachungen, bleibt das demonstrierte Prinzip in allen Fällen gleich: Den Angriffsvektor einschleusen und den Maschinencode zur Ausführung bringen. Ein funktionierender Angriffsvektor zu dem Programm stack-overflow.c ist ebenfalls im Quellcode- Archiv zu finden. Dabei wird der Angriffsvektor mittels eines Perl-Skripts generiert. Der hierbei verwendete Payload startet hierbei nichts weiter als den Windows-Taschenrechner clac.exe. Details zur Erstellung von Payload folgt in dem Abschnitt 3.2. 4 cat ist ein Kommandozeilen-Tool aus der UNIX-Welt, mit dem sich unter anderem sehr einfach Eingaben aus einer Datei lesen lassen. Das Tool gibt es auch für Windows Seite - 24 -
  • 25. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Gekürzte Ausgabe des Programms stack-overflow.exe ~~~ nachher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dump(password_buffer...); 0012FA48: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................ 0012FA58: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................ NOP-sled 0012FA68: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 | ................ 0012FA78: 29 c9 83 e9 dd d9 ee d9 74 24 f4 5b 81 73 13 b3 | ).......t$.[.s.. 0012FA88: 87 5d 85 83 eb fc e2 f4 4f 6f 19 85 b3 87 d6 c0 | .]......Oo...... 0012FA98: 8f 0c 21 80 cb 86 b2 0e fc 9f d6 da 93 86 b6 cc | ..!............. 0012FAA8: 38 b3 d6 84 5d b6 9d 1c 1f 03 9d f1 b4 46 97 88 | 8...]........F.. 0012FAB8: b2 45 b6 71 88 d3 79 81 c6 62 d6 da 97 86 b6 e3 | .E.q..y..b...... 0012FAC8: 38 8b 16 0e ec 9b 5c 6e 38 9b d6 84 58 0e 01 a1 | 8.....n8...X... Payload 0012FAD8: b7 44 6c 45 d7 0c 1d b5 36 47 25 89 38 c7 51 0e | .DlE....6G%.8.Q. 0012FAE8: c3 9b f0 0e db 8f b6 8c 38 07 ed 85 b3 87 d6 ed | ........8....... 0012FAF8: 8f d8 6c 73 d3 d1 d4 7d 30 47 26 d5 db f9 85 67 | ..ls...}0G&....g 0012FB08: c0 ef c5 7b 39 89 0a 7a 54 e4 3c e9 d0 a9 38 fd | ...{9..zT.<...8. 0012FB18: d6 87 5d 85 50 fa 12 00 50 fa 12 00 50 fa 12 00 | ..].P...P...P... 0012FB28: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50 fa 12 00 | P...P...P...P... RET 0012FB38: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50 fa 12 00 | P...P...P...P... (mehrfach wiederholt) 0012FB48: 50 fa 12 00 50 fa 12 00 50 fa 12 00 50 fa 12 00 | P...P...P...P... 0012FB58: ff | . dump(&auth_flag, 4); 0012FA44: 00 00 00 00 | .... Erklärung: 1. Der Wert 90 Hex entspricht einem NOP 2. Der rot markierte Bereich ist der Maschinencode zum Starten von calc.exe 3. Am Schluss folgt die mehrfach wiederholte Return-Adresse 3.1.5 Exception Handler-basierende Angriffe Die Programmiersprache c bietet von sich aus keinerlei Möglichkeiten, Ausnahmen im Programmablauf abzufangen. Eine solche Ausnahme ist beispielsweise eine Division durch Null. Mit C++ wurde dieser Missstand behoben und es ist somit, durch Elemente der Programmiersprache, möglich eine Ausnahmebehandlung durchzuführen. Das bedeutet, dass ein Programm, das eine Division durch null durchführt, nicht mehr zwangsläufig abstürzt, sondern durch eine geeignete Ausnahmebehandlung weiterlaufen kann. [ 11 ] Microsoft hat für die Programmiersprache c eigene Elemente eingebracht, mit denen eine Ausnahmebehandlung möglich wird: __try und __except Diese Art der Ausnahmebehandlung nennt sich Structured Exception Handling (SEH). Im Unterschied zu c++ wird die Art der Ausnahme mittels eines unsigned int ermittelt, während in c++ anhand des Klassen-Typs unterschieden wird. [ 11 ] [ 12 ] Seite - 25 -
  • 26. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Beispiel: Strukturierte Ausnahmebehandlung (SEH) - Division durch Null Quellcode: Kapitel-3-1-5_exceptionexception.c #include <stdio.h> int MyExceptionHandler(void); int main(int argc,char *argv[]){ int a = 1000, b = 0, c = 0; if(argc != 2){ printf("Divisor als Parameter uebergebenn"); exit(0); } b = atoi(argv[1]); /* Falls eine 0 als erster Programmparameter übergeben wird, wird * eine EXCEPTION_INT_DIVIDE_BY_ZERO ausgelößt */ __try { c = a / b; printf("Ergebnis der Division %i / %i = %in", a, b, c); } __except ( MyExceptionHandler() ){} printf("Programm beendet sichn"); return 0; } /* return 1, damit die Exception nicht and das * Betriebsystem weitergereicht wird */ int MyExceptionHandler(void){ printf("Exception.. Ausnahme abgefangen und als behandelt markiert"); return 1; } SEH lässt sich im Zusammenhang mit Buffer Overflows nutzen, um eingeschleusten Code zur Ausführung zu bringen. Dabei macht sich der Angreifer den Umstand zu nutze, dass die Funktionsadresse der Funktion zur Ausnahmebehandlung, wie in Abbildung 3-5 zu sehen ist, auf dem Stack gespeichert wird. Seite - 26 -
  • 27. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Beispielhaftes Stackframe 0000000h (Kleine Adressen) Gespeicherte Register der aufgerufenen Funktion Overflowrichtung Wachstumsrichtung Lokale Variablen Stack Frame Pointer (SFP) Return Adresse (RET) FFFFFFFFh (Große Adressen) Parameter ... Pointer zur nächsten SEH-Funktion Pointer zur SEH-Funktion Abbildung 3-5: Beispielhaftes Stackframe unter Verwendung von SEH [ 13 ] Wie bei den klassischen Stack-basierenden Buffer Overflows, wird von einem verwundbaren Buffer ausgehend, diese Funktionsadresse im Speicher überschrieben. Der Unterschied besteht lediglich darin, dass die Behandlung der Ausnahme veranlasst werden muss, z. B. mit einer Division durch Null. [ 14 ] Interessant ist hierbei, dass jedes Programm mindestens einen, automatisch erstellten SEH besitzt, den der main()-Routine. Im Quellcode-Archiv findet sich im Ordner "Kapitel-3-1-5_exception_exploit" ein vollständiges Beispiel, wie sich SEH unter Windows praktisch ausnutzen lässt. 3.1.6 Heap-basierende Angriffe Heap-basierende Angriffe funktionieren prinzipiell genauso, wie alle anderen Buffer-Overflows auch. Es werden Daten überschrieben, die nach einem verwundbaren Puffer im Speicher liegen. Der einzige Unterschied besteht darin, dass, verglichen mit den anderen Datensegmenten, im Heap andere Daten gespeichert sind. Folgende Daten finden sich vermehrt bzw. nur auf dem Heap: 1. Nutzdaten. Beispielsweise die Variable auth_flag aus dem Abschnitt 3.1.2 (gibt es auch auf beim Stack) Seite - 27 -
  • 28. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 2. Daten die den Programmablauf steuern und vom Programmierer bewusst angelegt wurden (auch auf dem Stack vorhanden, aber selten) 3. Daten die den Programmablauf steuern und vom Compiler automatisch angelegt werden 4. Daten die der Speicherverwaltung dienen. Beispielsweise das Windows-Heap-Management Den Punkt "Nutzdaten", wurde bereits in dem Abschnitt 3.1.2 ausführlich behandelt und da an dieser Stelle keine wesentlichen Unterschiede existieren, wird an dieser Stelle nicht näher darauf eingegangen. Der zweite Punkt ist vom prinzipiellen Vorgehen bereits bekannt. Der Punkt Nummer Drei bietet interessante Möglichkeiten, ist in der Praxis jedoch kaum anzutreffen. Punkt Nummer Vier hat in den letzten Jahren deutlich an Bedeutung gewonnen und ist vor allem im Zusammenhang mit dem Windows-Heap-Management interessant. Deshalb wird im Folgenden das prinzipielle Vorgehen bei Angriffen auf das Windows-Heap-Management erläutert. Angriffe auf das Windows-Heap-Management Wie bereits in dem Abschnitt 2.1.3 beschrieben, ist ein Heap, der mit HeapCreate()erzeugt wird, durch eine Reihe von Listen organisiert. Wird mit der Funktion HeapAlloc() ein Speicherbereich angefordert, so wird von der Heapverwaltung ein passendes Stück Speicher gesucht. Dieses Stück Speicher wird, unmittelbar vor dem nutzbaren Bereich, mit einem Header versehen, der Informationen für die Heapverwaltung enthält. [ 15 ] [ 8 ] Bei Angriffen auf die Heapverwaltung ist es das Ziel, die Headerinformationen derart zu manipulieren, dass eine begrenzte Menge an Daten an eine beliebige Stelle im Speicher geschrieben werden kann. Hierbei macht sich der Angreifer zunutze, dass die Headerinformationen Pointer enthalten, die zur Verkettung der Listen und zur Referenzierung des Nutzspeichers dienen. Beim Anfordern und Freigeben von Speicher werden die Header teilweise kopiert und unter bestimmten Bedingungen in einer doppelt verketteten Liste gespeichert. Zur Bestimmung der jeweiligen Nachbarelemente werden die Headerinformationen verwendet. Somit ist es möglich, durch gezieltes Überschreiben der entsprechenden Pointer, einen wahlfreien Schreibzugriff mit einer Datenmenge von 4 Bytes zu erreichen. Dieser Vorgang lässt sich prinzipiell beliebig oft wiederholen, meist ist jedoch eine Menge von 4 Bytes vollkommen ausreichend, da dies die Größe eines Pointers ist. Somit lässt sich beispielsweise im Process Execution Block (PEB) ein Pointer überschreiben. Hier bietet sich der Pointer auf die Funktion ExitProcess() an, die vor dem endgültigen Beenden des Prozesses ausgeführt wird. Durch das Überschreiben des ExitProcess()-Pointers kann eingeschleuster Code durch das Beenden des Programms zur Ausführung gebracht werden. [ 15 ] [ 8 ] [ 16 ] [ 17 ] [ 18 ] [ 16 ] [ 19 ] In der Praxis gibt es eine Vielzahl von Variationen der eben beschriebenen Methode. Hierbei haben jedoch alle Varianten die Gemeinsamkeit, dass sie durch gezieltes Manipulieren der Header, Code zur Ausführung bringen. Seite - 28 -
  • 29. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Einen besonderen Reiz haben vor allem die Varianten, die es dem angegriffenen Programm ermöglichen weiter zu laufen, indem sie die beschädigten Heapstrukturen wieder mit gültigen Werten reparieren. Ein weiterer Grund für die gestiegene Popularität von Angriffen auf das Heapmanagement sind vor allem Mechanismen, die zum Schutz des Stacks eingeführt wurden und den Heap so zum schwächsten Glied der Kette werden ließen. Details zu aktuellen Schutzmechanismen des Stacks werden im Kapitel 4.6 behandelt. 3.2 Shellcode und Payload Die beiden Begriffe Shellcode und Payload werden heutzutage häufig als Synonym verwendet. Streng genommen ist der Shellcode jedoch eine spezielle Form des Payloads: 1. Der Payload an sich ist der Teil des Angriffsvektors, der die eigentliche Funktionalität beinhaltet, also beispielsweise das Starten des Windows-Taschenrechners. 2. Der Shellcode ist streng genommen ein spezieller Payload, der eine Shell (Eingabeaufforderung) startet, mit der der Angreifer interagieren kann. In dieser Arbeit wird häufig ebenfalls der Begriff "Shellcode", anstelle des historisch korrekten Begriffes verwendet. Die Begründung ist die Tatsache, dass der Begriff Shellcode einen höheren Wiedererkennungswert bietet und selbst in Printmedien gebräuchlicher ist. Da das Thema Shellcode, bei sorgfältiger Behandlung, den Umfang dieser Arbeit bei weitem sprengen würde, wird im Folgenden nur stichpunktartig auf Anforderungen an Shellcode eingegangen. 3.2.1 Anforderungen an Shellcode Anforderungen an die Darstellung des Shellcodes : 1. Zeichensatz: Shellcode unterliegt einigen Einschränkungen bezüglich der verwendbaren Zeichen. Beispielsweise darf in Shellcode der durch strcpy() eingeschleust wird, nicht das Zeichen 0x00 vorkommen, weil strcpy() nur bis zu diesem Zeichen kopiert. Außerdem werden die eingegebenen Zeichen häufig von dem angegriffenen Programm geprüft und z. B. nur druckbare Zeichen verarbeitet. 2. Kompatibilität: Die Kompatibilität von Shellcode ist unumgänglich auf eine Art der Speicheranordnung beschränkt: entweder Big- oder Little-Endian. 3. Kodierung: Soll eine Funktion angegriffen werden die eine UTF-8 kodierte Zeichenfolge erwartet, so muss außer der Big- und Little-Endian-Anordnung außerdem eine bestimmte Reihenfolge von Zwei-Byte-Paaren eingehalten werden. [ 20 ] 4. Kompaktheit: Häufig ist die Menge an einschleusbaren Zeichen beschränkt. 5. Unentdeckt bleiben: Unter Umständen passiert der Shellcode auf dem Weg zum Ziel Intrusion Detection Systeme (IDS), die die übertragenen Daten auf bekannte Schad- Signaturen hin überprüfen. Seite - 29 -
  • 30. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Anforderungen an die Logik des Shellcodes: 1. Betriebssystemabhängig: Da die meisten Betriebssysteme einen grundliegend unterschiedlichen Zugriff auf Systemfunktionen haben, muss der Shellcode meistens Betriebssystemspezifisch sein. 2. Behandlung der Anwendung: Unter bestimmten Umständen hat man die Möglichkeit, das angegriffene Programm weiter laufen zu lassen und so die Wahrscheinlichkeit, das der Angriff entdeckt wird stark zu reduzieren. 3.2.2 Besonderheiten von Windows-Shellcode Verglichen mit Shellcode der unter Linux lauffähig ist, gibt es für Windows-Shellcode eine Reihe von Besonderheiten 1. Windows-Shellcode ist größer als Linux-Shellcode [ 21 ] 2. Keine 1-zu-1-Abbildung der Bibliotheksfunktionen des Betriebssystems zu den Systemcalls auf Kernelebene [ 21 ] 3. Die verwundbaren Buffer liegen meist in einem Speicherbereich, der mit 0x00… beginnt und somit ein nicht verwendbares Zeichen darstellt. 3.2.3 Was möglich ist Je nachdem welchen Anforderungen (siehe 3.2.1) der eingeschleuste Code unterliegt, ergeben sich unterschiedliche Möglichkeiten: 1. Erweitern der Rechte durch angreifen von lokalen Prozessen die mit umfangreicheren Privilegien ausgestattet sind. 2. Nachladen von Systemfunktionen, falls der Prozess eine für den Angriff wichtige Dll-Funktion5 nicht geladen hat, kann man diese Dll selbst nachladen. 3. Nachladen von Code, falls der eingeschleuste Code in seiner Größe beschränkt war, hat man die Möglichkeit z. B. aus dem Internet Code nachzuladen. [ 21 ] Wenn man davon ausgeht, dass man die Möglichkeit hat, eine unbegrenzte Menge an eingeschleusten Code, mit Administrator-Rechten auszuführen, dann hat man praktisch keine Einschränkungen mehr. Man kann beispielsweise: 1. Ein lokales Benutzer-Konto erstellen (Local Access) 2. Einen Prozess starten, der beliebige Befehle über das Netzwerk ausführt (Remote Access) 3. Einen Keylogger installieren, der sämtliche Aktivitäten des Benutzers aufzeichnet, inklusive Tastatur- und Mausinteraktion (Spionage) 5 Dynamic Link Library (Dll) Seite - 30 -
  • 31. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 3.2.4 Abstraktes Beispiel: Portbind Shellcode für Windows An dieser Stelle folgt ein eine allgemeine Beschreibung, der Vorgehensweise, zum Starten einer Remote-Shell: [ 21 ] 1. Suchen der Datei kernel32.dll 2. Auflösen der Symbole GetProcAddressA(), LoadLibraryA() sowie ExitProcess() 3. Laden der Winsock Bibliothek ws2_32.dll 4. eine Shell an einen TCP-Port binden 4.1. Socket erzeugen 4.2. Socket an Port binden 4.3. Auf dem Port auf eingehende Verbindung warten 4.4. Verbindung akzeptieren 5. Kommandointerpreter starten 6. Den Elternprozess sauber beenden oder die beschädigten Strukturen reparieren Um nicht zu sehr vom eigentlichen Thema, den Buffer Overflows, abzukommen, wird an dieser Stelle nicht weiter auf das Themengebiet Shellcode eingegangen. Interessierten sei [ 21 ] für weitere Informationen empfohlen. Es folgt nun eine Vorstellung der Verschiedenen Mechanismen in Windows, die dazu dienen den Schaden durch Buffer-Overflows so gering wie möglich zu halten. Seite - 31 -
  • 32. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 4 Das Sicherheits-Ensemble von Windows und seine Schwachstellen Dieses Kapitels hat es zum Ziel, die Frage nach dem betitelten Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista zu klären. Nicht eingegangen wird auf weitere nützliche Funktionen zur Erhöhung der Sicherheit, die nichts direkt mit Buffer-Overflows zu tun haben. Die vorhergegangenen beiden Grundlagenkapitel, haben sich im Wesentlichen damit beschäftigt, welche Bedingungen, unter früheren Windowsversionen, gegeben sein mussten, um einem Buffer- Overflow erfolgreich auszunutzen. Dieses Kapitel beschäftigt sich zu Beginn damit, wie sich der Schaden, eines unter fremder Kontrolle stehenden Programms, einschränken lässt und welche neuen Ansätze mit Windows Vista verfolgt werden (bis einschließlich Kapitel 4.2). Anschließend werden Methoden von Windows XP und Vista analysiert und bewertet, die es zum Ziel haben, Angriffe mittels Buffer-Overflows so früh wie möglich zu entschärfen. 4.1 Das Windows-Zwei-Schichten-Modell Den grundlegendsten Schutzmechanismus der Windows-Architektur bildet das Zwei- Schichtenmodell. Unter Windows ist die gesamte Funktionalität in zwei unterschiedlich privilegierte Bereiche eingeteilt: [ 4 ]  Kernel-Mode (Ring 0): Der Modus mit den höchstmöglichen Privilegien – Keine Einschränkungen beim Zugriff auf die Ressourcen des Kernels  User-Mode (Ring 1): Eingeschränkter Modus – Beim Zugriff auf Systemressourcen muss eine ganze Abfolge von Funktionen abgearbeitet werden, die vom Ring0 als Services zur Verfügung gestellt werden. Häufig werden User- und Kernel-Mode wie in Abbildung 4-1 grafisch dargestellt. Dabei soll symbolisiert werden, dass auf die innen liegenden Funktionen und Ressourcen nur über die verbindende Schicht zugegriffen werden kann. Seite - 32 -
  • 33. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Ring1 (User-Mode) Ring0 (Kernel-Mode) Kernel Abbildung 4-1: Windows-Zwei-Schichten-Modell [ 4 ] Im Ring0 finden sich vorrangig Gerätetreiber und Systemfunktionen. Im Ring1 hingegen findet sich alles Übrige, z. B. ein Browser oder ein FTP-Server. Schafft es ein Angreifer direkt im Ring0 einen Programmierfehler für sich zu nutzen, so stehen im praktisch keine Hindernisse mehr im Weg. Allerdings erfordern Angriffe auf dieser Ebene sehr spezifisches Detailwissen, das äußerst selten offen zugänglich ist. Dies ist auch der Grund dafür, dass Angriffe auf den Ring0 verhältnismäßig selten vorkommen. Ein Beispiel für einen erfolgreichen Angriff auf den Gerätetreiber diverser W-LAN-Chips ist der Angriff von Jon Ellch (Pseudonym: Jonny Cache)[ 22 ] . [ 4 ] [ 23 ] Meist ist es aus dem oben genannten Grund einfacher, eine verwundbare Anwendung im Ring1 anzugreifen. Jedoch haben Prozesse im Ring1 einen Prozess-Kontext, der einen rechtebeschreibenden Token beinhaltet. Näheres dazu im nächsten Abschnitt. 4.2 Zugriffsrechte, Authentifizierung und Access Token Meldet sich ein Benutzer an einem Windows-System an, so bekommt er vom Local Security Authority Subsystem Service (LSA) ein Access Token zugeteilt. Dieser Token besteht aus einem Security Identifier (SID), der zu seinem Benutzerkonto gehört, und einigen weiteren SIDs, die zu seinen Gruppen gehören in denen er Mitglied ist. Eine solche SID ist eine 48 Bit lange Nummer, die domainweit einzigartig ist. Außerdem enthält der Token, falls definiert, eine Liste von nicht erlaubten SIDs und eine Liste mit Privilegien, die zu den jeweiligen SIDs gehören. Privilegien sind z. B. das Recht einen Treiber zu laden (SeLoadDriverPrivilege) oder das Recht den Computer herunterzufahren (SeShutdownPrivilege).[ 4 ] [ 24 ] Seite - 33 -
  • 34. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Alle Ressourcen, auf die ein Benutzer theoretisch zugreifen kann, haben eine so genannte Discretionary Access Control List (DACL)6. Praktisch wird der Zugang auf die Ressource durch LSA reglementiert. Das bedeutet, der Zugriff ist nur erlaubt, wenn eine der SIDs aus dem Token des Benutzers in der DACL-Liste unter dem entsprechenden Privileg (Lesen, Schreiben usw.) zu finden ist. [4] Nach [ 4 ] lässt sich zur besseren Übersicht eine Einteilung in Subjekte und Objekte vornehmen 1. Subjekte: Einheiten, die Aktionen durchführt – unter Windows sind das Prozesse 2. Objekte: Sind die Empfänger einer Aktion – unter Windows zugriffsgeschützte Objekte mit zugehörigen DACL wie z. B. Dateien und Registry-Einträge 4.2.1 Rechte von Prozessen unter Windows Startet ein am System angemeldeter Benutzer einen Prozess, so wird in dem Prozess-Kontext der Token des Benutzers gespeichert. Fortan darf der Prozess (Subjekt) auf alle Ressourcen (Objekte) zugreifen, auf die auch der Benutzer durch seinen Token Zugriff hat. Ist nun unter Windows XP ein Administrator am System angemeldet und startet den Internet Explorer, so hat der Browser denselben Token wie der Administrator. Surft der Administrator nun beispielsweise über eine Seite, die den populären Animated-Cursor Exploit7 eingebaut hat, so stehen dem Angreifer keinerlei Hindernisse im Weg. Er hat dank des hoch-privilegierten Token, mit der SID des angemeldeten Administrators, vollständigen Zugriff auf das gesamte System. Mit Windows 2000 wurde die Möglichkeit eingeführt, einen Kindprozess/Thread mit einem eingeschränkten Token zu starten. Der eingeschränkte Token kann, ausgehend von dem Original- Token, wie folgt definiert werden: Privilegien entfernen, SIDs entfernen, nicht erlaubte SIDs hinzufügen. Wurde ein Thread mit einem eingeschränkten Token gestartet, so ist er an die neuen Einschränkungen gebunden. Allerdings ist es möglich dem Thread mit dem eingeschränkten Token, wieder den uneingeschränkten Token zuzuweisen. Weil diese Möglichkeit besteht, empfiehlt es sich die API-Funktion CreateProcessAsUser( ) zu verwenden, so dass diese Möglichkeit nicht mehr besteht. [ 24 ] Mit Windows Server 2003 wurde es möglich die Privilegien eines Prozess-Token dauerhaft einzuschränken. Diese Möglichkeit ist aber ungleich der des eingeschränkten Token, da hier der Prozess dauerhaft seine eigenen Privilegien beschneidet. Alle von ihm erzeugten Kindprozesse erben, nach demselben Prinzip wie bei Windows 2000, den Token des Elternprozesses, z. B. einen, in den Privilegien eingeschränkten. Die Möglichkeit die SIDs des Token zu modifizieren besteht hier nicht.[ 24 ] 6 DACL wird in der Literatur häufig auch ohne das führende D, nur als Access Control List (ACL), verwendet. Außerdem wird häufig der Ausdruck Security Descriptor als Synonym verwendet. 7 Animated-Cursor Exploit: http://www.securityfocus.com/advisories/7814 Seite - 34 -
  • 35. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 4.2.2 Windows Service Hardening Da auch Windows-Services nichts weiter wie Prozesse sind, haben auch sie einen Token über den sie im Betrieb ihre Berechtigungen erhalten. Für einen Angreifer sind damit die Standard-Services einer Windows-Installation besonders interessant, weil sie aufgrund ihrer Funktionalität ständig laufen und typischer Weise Systemfunktionen ausführen, die hohe Privilegien benötigen. Mit Windows Server 2003 wurden zwei neue Service-Konten eingerichtet die den bisher übermächtigen LocalSystem entlasten sollten: LocalService und NetworkService. Dabei sind die beiden neuen Konten in den Rechten derart beschnitten, dass viele Services nicht mit ihnen arbeiten können und deshalb den bisherigen LocalSystem weiterhin verwenden.[ 4 ] Zur verbesserten Schadensbegrenzung wurde bei Windows Vista eine Reihe von Maßnahmen eingeführt, die ein erheblich granulareres Rechtemanagement wie bisher ermöglichen: 1. Service-spezifische SIDs: Jeder Service hat eine eigene SID. Sinnvoller Weise bekommen nur diejenigen Objekte in ihrer DACL die SIDs des speziellen Services eingetragen, die sie auch wirklich benötigen.[ 4 ] 2. Eingeschränkte SIDs: Die RESTRICTED-SID kennzeichnet einen Prozess, der ein eingeschränktes Token enthält. [ 25 ] (nicht auf Services beschränkt) 3. Reduzierte Privilegien für Services: Überflüssige Privilegien, wie das Debugging Privileg wurden den Diensten generell entzogen [ 4 ] 4. Session 0 Isolation: Unter Windows Server 2003 und früheren Versionen, war es unter Umständen möglich seine Privilegien durch Angriffe auf Services mit höheren Privilegien innerhalb derselben Session zu erweitern. Diese Problem wird dadurch entschärft, dass nur noch Services in der Session 0 laufen und alle vom User gestarteten Anwendungen in eigenen Session (1 und höher) gestartet werden. [ 26 ] [ 27 ] 5. UAC: siehe nächster Abschnitt (nicht auf Services beschränkt) 4.2.3 Rechte von Prozessen unter Windows Vista: UAC Es ist zwar bekannt, dass es sich nicht empfiehlt mit Administratorrechten im Internet zu surfen, die Konsequenz als normaler Benutzer an einem System zu arbeiten, haben jedoch nur wenige Anwender gezogen. Der Hauptgrund hierfür ist die mangelnde Unterstützung seitens Windows. So ist es bis zu der Version "Vista" nötig gewesen, eine Druckerinstallation oder das Stellen der Uhrzeit mit Administratorrechten zu erledigen. UAC steht für User Account Control und soll das Arbeiten als eingeschränkter Benutzer erleichtern. Dazu fragt Windows Vista den Benutzer, falls nötig, automatisch nach dem Passwort, für das Benutzerkonto, dass zum ausführen der Aktion nötig ist. Dabei ist die Interaktion mit den BS solange gesperrt, bis der Benutzer auf die Anfrage reagiert hat. Seite - 35 -
  • 36. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Im Detail verwendet UAC eine neue Technik namens Mandatory Integrity Control (MIC). MIC ist eine Erweiterung für die DACLs um sogenannte Integrity Level (IL). Gespeichert werden diese neuen Rechte in den Mandatory ACLs (MACL). Als IL wurden folgende vier Möglichkeiten eingeführt:  Low  Medium (als Standard)  High  System Jeder Prozess hat in seinem Token zusätzlich ein IL gespeichert. Will ein Prozess mit einem niedrigen IL auf eine Ressource mit einem höheren IL zugreifen, so ist die oben beschriebene Interaktion mit dem Benutzer vonnöten. Damit UAC funktioniert wurde der LSA modifiziert, so dass es jetzt zwei Arten von Token gibt:  einen gefilterten: ohne erweiterte Rechte  einen gelinkten: mit allen Originalrechten Außerdem muss der Programmierer im Manifest seiner Anwendung das benötigte IL angeben. Standardmäßig werden alle Prozesse mit dem gefilterten Token ausgeführt. Der gelinkte Token wird nur verwendet, wenn eine Applikation, aufgrund des Eintrags im Manifest, nach erweiterten Rechten fragt. Dieses Verhalten führt auch dazu, dass selbst ein angemeldeter Administrator zuerst vom System um Erlaubnis gefragt wird, wenn die Aufgabe ein höheres IL erfordert. Als Beispiel für eine sinnvolle Anwendung des UAC kann der Internet Explorer betrachtet werden: Der Internet Explorer läuft per Voreinstellung mit Low-IL, was bedeutet, er kann nur auf Objekte mit LOW-IL SIDs zugreifen. Standardmäßig sind das nur %USERPROFILE%AppDataLocalLow und der Registry-Key HKCUSoftwareAppDataLow. Auf alle anderen Systemressourcen hat der Prozess des IE keinen Zugriff, was die Sicherheit stark erhöht. Wird der IE mit aktivierten UAC betrieben, so wird diese Konstellation häufig als Protected Mode IE (PMIE) oder auch Low Rights Internet Explorer (LoRIE) bezeichnet. UAC ist die für den Benutzer offensichtlichste und auch eine der nützlichsten Sicherheitsmaßnahmen zur Schadensbegrenzung. Allerdings empfinden viele Benutzer die ständige Nachfrage nach erweiterten Rechten als störend. Außerdem funktionieren nicht alle Programme einwandfrei mit UAC. Diese zwei Kritikpunkte führen dazu, dass viele Benutzer UAC Systemweit deaktivieren, was den gesamten Nutzen hinfällig macht. Seite - 36 -
  • 37. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista 4.3 PatchGuard PatchGuard wurde von Microsoft entwickelt und hat es zum Ziel Software von Drittherstellern daran zu hindern Änderungen am Kernel vorzunehmen. Dadurch soll potentiell gefährlicher Software erschwert werden sich einzunisten. Eingeführt wurde PatchGuard mit den Windows-Versionen Server 2003 mit SP1 (64-Bit) und XP Professional x64. Eine Portierung auf die x86-Versionen ist derzeit nicht geplant. [ 28 ] Da PatchGuard eine Reihe von Unzulänglichkeiten besitzt, die das Umgehen des Schutzes relativ einfach machen, ist der tatsächliche Nutzen in Bezug auf Schadsoftware fraglich. Häufig wird sogar behauptet, PatchGuard sei lediglich ein Instrument von Microsoft, um die Dritthersteller dazu zu zwingen, saubereren und stabileren Code zu schreiben. [ 29 ] Viel Kritik an dem System gab es auch von den Herstellern von Sicherheits-Software, wie Anti-Viren- Software und Rootkit-Detection-Tools, da sich diese Produkte häufig an den, durch dieses System nicht mehr erreichbaren Stellen, eingenistet haben, um so von einem möglichst uneingeschränkten Punkt aus operieren zu können. [ 29 ] Eine umfassende Analyse der neusten Version (v3) von PatchGuard ist im Internet unter [ 28 ] zu finden. 4.4 Process Exection Block (PEB) Randomization Überblick Mit Windows XP SP28 wurde eine Funktionalität namens PEB-Randomization eingeführt. Ihr Ziel ist es, Angriffe zu erschweren, die zur Codeausführung PEB-Einträge manipulieren. Im Process Exection Block (PEB) sind vor allem für Angriffe interessante Funktions-Pointer gespeichert, wie ein Pointer auf die Funktion ExitProcess(). Funktionsweise Beim Programmstart wird die Basisadresse des PEB dynamisch gewählt. Dadurch ist ein Remote- Exploit9, dass den PEB als Einstiegspunkt verwendet, nicht mehr zuverlässig, da die zu überschreibende Adresse des Funktions-Pointers in dem, jetzt an einem zufälligen Ort im Speicher liegenden, PEB gespeichert ist. 8 Funktionalität ist auch in Server 2003 SP1 / Vista vorhanden 9 Ein Remote-Exploit ist dadurch charakterisiert, dass der Angreifer entfernt über ein Netzwerk zugreift und nicht an der Maschine selbst sitzt. Seite - 37 -
  • 38. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Schwachstellen Wie sich ein einer Untersuchung von Symantec herausstellte [ 30 ] , beschränkt sich die Anzahl der Möglichkeiten, für die Basisadresse des PEB, auf 16. Hinzu kommt noch, dass die Adresse in 20% aller Fälle gleich ist. Effektiver Schutz Die Möglichkeit verlässliche Remote-Exploits zu schreiben wurde reduziert. Die Option der Brute- Force-Angriffe bleibt jedoch, mit einem recht hohen Erfolgsgrad, bestehen. 4.5 Heap-Schutz Überblick Wie in Abschnitt 3.1.6 beschrieben, kann es auch auf dem Heap zu Buffer-Overflows kommen. Um den Schaden begrenzen zu können, wurden mit dem SP2 von Windows XP im Wesentlichen zwei neue Features, für die von Windows bereitgestellte Heap-Verwaltung, eingeführt: [ 8 ]  Cookies auf dem Heap (vergleichbar mit denen der GS-Funktionalität des Stacks)  Plausibilitätsprüfung (sanity checking) der Headerinformationen der einzelnen Heap- Segmente Funktionsweise Cookies Bei jedem Aufruf von HeapAlloc() wird vor und nach jedem Puffer auf dem Heap ein 1-Byte großes, mit Pseudo-Zufallszahlen initialisiertes Cookie, abgelegt. [ 8 ] Im Betrieb wird vor jedem Entnehmen eines Blocks aus der "Frei-Liste" geprüft, ob der Wert des Cookies noch der Selbe ist, wie zum Zeitpunkt der Initialisierung. Dazu wird der Wert mit dem des separat gespeicherten Original-Cookies verglichen. Hat sich der Wert geändert, so wird zuerst ein HeapDestory() ausgeführt und anschließend wird eine Exception geworfen. Falls diese nicht behandelt wird, wird das Programm beendet. [ 8 ] Außerdem wurde ein sogenannter Low Fragmentation Heap (LFH) entwickelt, der Primär der Performancesteigerung dient. Dieser LFH ist ebenfalls durch ein Cookie geschützt. Allerdings durch eins der Größe 4-Byte. Durch das größere Cookie, wird das erraten des Werts und die Wahrscheinlichkeit für erfolgreiche Brute-Force-Angriffe, stark reduziert. [ 17 ] Funktionsweise Plausibilitätsprüfung Die "Frei-Listen" sind als doppelt verkette Listen aufgebaut. Die Pointer, die zur Verkettung der Liste in beide Richtungen dienen, nennen sich Blink, für das vorhergegangene Element und Flink, für das nächste Element. [ 8 ] Seite - 38 -
  • 39. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Vor jedem Entnehmen eines Blocks aus der "Free-List" werden Blink und Flink auf ihre Plausibilität hin geprüft. Das bedeutet, es wird geprüft, ob der der Blink aus dem Header des Folgeelements auch tatsächlich auf das aktuelle Element zeigt. Dieselbe Prüfung wird in die andere Richtung vollzogen. [8] Schwachstellen Innerhalb der Heap-Verwaltung gibt es nicht nur die oben genannten "Frei-Listen", sondern auch Lookaside-Liste, die primär der Geschwindigkeitserhöhung dienen. Diese Listen sind nur einfach verkette Listen und aus diesem Grund könnten die Pointer nicht auf Plausibilität hin geprüft werden.. Da aber unter XP mit SP2 der PEB und der Heap dynamische Startadressen haben, benötigt ein Brute- Force-Angriff nach mehr wie 2000 Versuche, vorausgesetzt die Applikation läuft nach einem fehlgeschlagenen Versuch weiter. [ 17 ] Eine weitere Schwachstelle ist das nur 1-Byte große Cookie und die in Abschnitt 4.4 beschriebene, schwache Zufallswahl des PEB-Speicherortes. Hinzu kommt noch die Tatsache, dass der LFH unter Windows XP SP2 standardmäßig von keiner Applikation verwendet wird. [ 17 ] Einen weiteren Angriff auf das Heap-Management beschreibt Alexander Anisimov. Dabei nutzt er die Tatsache, dass die Cookie-Prüfung und die Plausibilitätsprüfung der Pointer nur unter bestimmten Umständen stattfinden. So ist es Alexander Anisimov, unter einer Reihe von Voraussetzungen möglich, die Blink- und Flink-Pointer derart zu manipulieren, dass ein wahlfreier Schreibzugriff von 1016 Bytes möglich ist. Die Voraussetzungen für diese Art von Angriff sind jedoch derart exotisch, dass Microsoft an dieser Stelle keinen unmittelbaren Handlungsbedarf sieht. [ 8 ] Eine etwas robustere Methode, als die von Alexander Anisimov, beschreibt Nicolas Falliere in [ 15 ] . Dabei ist das Prinzip das gleiche: Es werden Blink- und Flink-Pointer überschrieben, dadurch kommt es zu einem wahlfreien Schreibzugriff der Größe von 4-Byte. Dabei unterliegt dieser Angriff aber den gleichen Problemen wie der Angriff, auf die einfach verketteten Lookaside-Listen. Es ist also auch hier nur ein Brute-Force-Angriff möglich. [ 15 ] Nach [ 31 ] werden die Lookaside-Listen in den aktuellen Windows-Versionen nicht mehr verwendet, wodurch auf ihnen basierende Angriffe nicht mehr möglich sind. Effektiver Schutz Unter Windows XP mit SP1 war es aufgrund der fixen Speicherorte des PEB und des Heaps, sowie des Fehlens, der oben beschrieben Neuerungen, möglich zuverlässige Remote-Exploits zu schreiben [ 17 ] Mit Windows XP SP2 wurden Maßnahmen eingeführt, die das Entwickeln zuverlässiger Exploits deutlich erschwert haben. Dennoch sind Brute-Force-Angriffe weiterhin möglich. Hinweis: Die in diesem Abschnitt beschriebenen Schwachstellen und Verbesserungen beziehen sich ausschließlich auf das Heap-Management von Windows. Programme die ihr eigenes Heap- Seite - 39 -
  • 40. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Management implementieren, profitieren von keiner der hier vorgestellten Neuerungen. Der Heap- Schutz wurde von Microsoft dennoch durch andere, in diesem Dokument vorgestellte Sicherheitsfunktionen, stark verbessert. Zu nennen sind in diesem Zusammenhang vor allem DEP, SafeSEH und ASLR (Vista). Insgesamt hat sich die Gefahr durch Heap-basierende Angriffe stark verringert. So dass sich Angriffe zukünftig verstärkt auf Anwendungsdaten konzentrieren werden, die auf dem Heap liegen (frei übersetzt nach [ 9 ] ). 4.6 Visual Studio 2005 - Der "Windows-Compiler" Wie jedes andere Programm auch, müssen die Quelldateien eins Betriebssystems mit einem Compiler in ausführbare Dateien übersetzt werden. Im Falle von Windows kommt bei Microsoft Visual Studio (VS) zum Einsatz [ 32 ] . Im Folgenden werden die, für Buffer-Overflows relevanten, Funktionen von VS 2005 vorgestellt und mit den Verbesserungen gegenüber früher Schutzfunktionen verglichen. Der Focus wird hierbei auf VS 200510 liegen, da dies der Compiler ist, mit dem das Betriebssystem Windows Vista übersetzt wird. Außerdem wird der effektive Nutzen der Maßnahmen diskutiert. 4.6.1 Standard Annotation Language (SAL) Option Beschreibung [ 33 ] Standard Windows Binaries /analyze Codeanalyse in der Enterprise-Version - Windows Vista (Compiler) Überblick SAL dient der Compiler gestützten Qualitätssicherung des Codes und ist in den Versionen "Team System" und "Team Edition" von VS 2005 mittels der Option /analyze verfügbar. Ihr Ziel ist es dem Entwickler beim Übersetzen seiner Programme Hinweise zu geben, an welcher Stelle der Programmcode möglicher weise nicht so arbeitet wie erwartet. Die Hauptaufgabe von SAL ist es dabei, auf potentielle Buffer-Overflows aufmerksam zu machen. [ 31 ] [ 34 ] 10 In den verwendeten Quellen ist häufig die Rede von dem „Whidbey-Compiler“. Whidbey ist der Codename für Visual Studio 2005 Seite - 40 -
  • 41. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Funktionsweise Beim Erstellen des Quellcodes muss der Programmierer sogenannte Annotations in seinen Code einbringen. Zum Beispiel die Annotation __out_ecount, wie in folgendem Beispiel zu sehen ist: void FillString( __out_ecount(cchBuf) TCHAR* buf, size_t cchBuf, char ch) { for (size_t i = 0; i < cchBuf; i++) { buf[i] = ch; } } Quelle: [ 34 ] Beim Kompilieren mit der /analyse Option wird VS in diesem Beispiel folgende Ausgabe erstellen: c:codesaltestsaltest.cpp(54) : warning C6203: Buffer overrun for non-stack buffer 'b' in call to 'FillString': length '420' exceeds buffer size '400' c:codesaltestsaltest.cpp(54) : warning C6386: Buffer overrun: accessing 'argument 1', the writable size is '200*2' bytes, but '420' bytes might be written: Lines: 53, 54 c:codesaltestsaltest.cpp(54) : warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'FillString': Lines: 53, 54 Quelle: (43) Dabei gibt es eine Reihe weiterer Annotations, die der Programmierer in seinen Code verwenden kann. Da die ursprüngliche Hauptaufgabe von SAL darin bestand Buffer zu beschreiben, auf die lesend und schreibend zugegriffen wird, versehen die Entwickler von Windows Vista alle entsprechenden Funktionen mit Annotations, bevor das Produkt an den Kunden ausgeliefert wird. [ 34 ] Schwachstellen Da SAL eine Meta-Sprache ist, die zur Unterstützung von statischen Analysetools entwickelt wurde, können dem entsprechend nur Fehler gefunden werden, die beim Kompilieren erkennbar sind. Buffer-Overflows, die zur Laufzeit auftreten, weil die Puffergröße beispielsweise von einer Benutzereingabe abhängt, können nicht erkannt werden. [ 34 ] Seite - 41 -
  • 42. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Effektiver Nutzen Durch die Verwendung von SAL ist der Programmierer gezwungen sich mehr Gedanken über den Ablauf seiner Funktionen zu machen. Zusammen mit einer konsequenten Anwendung und der Berücksichtigung der Compilerwarnungen, hätten mit SAL bereits in der Vergangenheit einige Buffer- Overflows vermieden werden können. Da SAL eine automatisierte Neuerung zur Verbesserung der Code-Qualität ist, ist SAL eine effektive Maßnahme gegen die Entstehung von Sicherheitslücken. 4.6.2 Optimierte Anordnung von Stack-Elementen (O2) Option Beschreibung [ 33 ] Standard Windows Binaries /O2 Erstellt schnellen Code. bei Release-Build Theoretisch alle (Compiler) Überblick Seit VS 2003 wird, beim Erstellen eins Release-Builds11, die Anordnung von Variablen (Puffern) auf dem Stack optimiert. Dabei ist es das Ziel potentiell verwundbare Puffer an einer ungefährlicheren Position anzulegen [ 35 ] . Funktionsweise Wie auf der Abbildung 4-2 zu erkennen ist, wird die lokale Variable auth_flag, vor dem potentiell verwundbaren Puffer im Speicher angelegt. Da der Schreibzugriff immer nur in Richtung größer werdender Adressen stattfindet, ist die Variable auth_flag in Sicherheit. Dasselbe Verfahren wird auch bei dem Anlegen von Pointer angewendet. 11 Standardmäßig ist bei dem Profil „Release“, in Visual Studio, die Compiler-Option O2 aktiviert Seite - 42 -
  • 43. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Stackframe Stackframe Quellcode Ohne Mit Optimierung Optimierung 0000000h int check_authentication(char *password) { (Kleine Adressen) int auth_flag = 0; char password_buffer[8]; password_buffer: char[32] auth_flag: int Wachstumsrichtung strcpy(password_buffer, password); Overflowrichtung auth_flag: int password_buffer: char[32] if(strcmp(password_buffer, "password") == 0) auth_flag = 1; if(strcmp(password_buffer, "adminpw") == 0) Stack Frame Pointer (SFP) Stack Frame Pointer (SFP) auth_flag = 1; Return Adresse (RET) Return Adresse (RET) return auth_flag; } Parameter Parameter FFFFFFFFh (Große Adressen) Abbildung 4-2: Optimierte Anordnung von Stack-Elementen bei VS 2003 Effektiver Schutz Durch dieses Verfahren wird zwar der Schutz, von lokalen Variablen und Pointern, vor Buffer- Overflows gewährt. Nicht geschützt sind diese Ziele jedoch vor Buffer-Underruns12. Die größte Gefahr bleibt nach wie vor bestehen. Wie in der Abbildung 4-2 zu sehen ist, ist es immer noch möglich, ausgehend von dem verwundbaren Puffer, die Returnadresse (RET) zu überschreiben und so beliebigen Code zur Ausführung zu bringen. Kombiniert mit den im Folgenden vorgestellten Mechanismen ergibt sich dennoch ein echter Mehrwert durch diese Optimierungen. 4.6.3 Puffer-Sicherheitsüberprüfung (GS) Option Beschreibung [ 33 ] Standard [ 35 ] Windows Binaries [ 32 ] /GS Puffer-Sicherheitsüberprüfung aktiviert teilweise bei (Compiler) seit VS 2005 XP SP2, Vista /GS- Puffer-Sicherheitsüberprüfung deaktivieren deaktiviert teilweise bei (Compiler) seit Visual C++ 2002 XP SP2, Vista 12 Zu einem Buffer-Underrun kann es beispielsweise durch fehlerhafte Pointerarithmetik kommen Seite - 43 -
  • 44. Stack- und Heap-Overflow-Schutz bei Windows XP und Windows Vista Überblick Hinter dem Compilerswitch /GS verbirgt sich eine Funktion namens Buffer Security Check. Das Haupt-Ziel dieser Funktion ist es, ein Überschreiben der Return-Adresse zu erkennen und das Programm gegebenenfalls abzubrechen. Unter Linux gibt es mit StackGuard und ProPolice Lösungen, die diesem Prinzip ebenfalls folgen. Verfügbar ist diese Option seit Visual C++ 2002 und standardmäßig aktiviert ist die Option seit VS 2005[ 35 ] . Dabei gilt es allerdings zu beachten, dass sich die Implementierungsdetails und damit der effektive Schutz, seit der ersten Version stark verändert haben. Im Folgenden wird nur auf die Implementierung von VS 2005 eingegangen. Seit Windows XP SP2 bzw. Windows Server 2003 ist die Option auch in den meisten Windows- Binaries Standard. [ 32 ] Funktionsweise Programme, die mit der /GS Option kompiliert wurden, initialisieren nach dem Programmstart 4Byte großes Master-Cookie, mit nicht vorhersagbarem Inhalt. Wird anschließend im Programmverlauf eine geschützte Funktion aufgerufen, so wird innerhalb des Funktionsprologs eine Kopie des Master- Cookies, wie auf Abbildung 4-3 zu sehen, auf dem Stack platziert. Wichtig ist hierbei vor allem die Position des Cookies auf dem Stack: es liegt nach den potentiell verbundbaren lokalen Variablen und vor der Return-Adresse. Hat die Funktion ihre Arbeit erledigt, so wird innerhalb des Funktionsepilogs der momentane Wert, des auf dem Stack liegenden Cookies, mit dem Master-Cookie verglichen. Sollte innerhalb der Funktion ein Buffer-Overflow vorgekommen sein, der bis zu dem Cookie reicht, so wird das an dieser Stelle erkannt. Gegebenenfalls wird anschließend eine Ausnahme geworfen und falls diese nicht entsprechend behandelt wird, wird das Programm wird mit einer Fehlermeldung abgebrochen. [ 32 ] Seite - 44 -