Skip to content

Schutzmaßnahmen: Der Pufferüberlauf im Überblick

Ab dieser Folge geht es um Schutzmaßnahmen, die Angriffe über Pufferüberlauf-Schwachstellen wenn schon nicht verhindern, dann doch zumindest erschweren sollen. Aber zuvor muss geklärt werden, was ein Pufferüberlauf (Buffer Overflow) überhaupt ist. Es handelt sich dabei um eine vor allem in C-Programmen schnell entstehende Schwachstelle, die für IT-Verhältnisse uralt ist. Die erste ausführliche Beschreibung wurde von Aleph One nach einer Reihe entsprechender Angriffe 1996 veröffentlicht: Smashing The Stack For Fun And Profit.

Der Pufferüberlauf im Schnelldurchlauf

Wie der Name schon vermuten lässt tritt ein Pufferüberlauf auf, wenn ein Programm Daten in einen zu kleinen Puffer kopiert. Am einfachsten lässt sich das Problem an einem Beispiel erklären:

Ein Audioplayer für Windows kopiert nacheinander die Einträge einer Playlist in den Speicher. Für die Dateinamen wird jeweils ein Puffer mit Platz für 256 Zeichen reserviert. Dass ist die maximale Länge eines Dateinamens auf NTFS-Dateisystemen, längere Namen sind also nicht möglich. Denkt der Entwickler und verzichtet darauf, die Länge des Namens vor dem Kopieren in den Puffer zu prüfen.

Soweit es die Dateinamen im System betrifft, hat der Entwickler völlig recht: Namen mit mehr als 256 Zeichen sind nicht möglich. Das hindert einen Angreifer aber nicht daran, in der Playlist einen längeren Namen einzutragen, der zum Beispiel 300 Zeichen lang ist. Damit lässt sich zwar auf keine Datei zugreifen, das will der Angreifer aber auch gar nicht.

Wenn das Programm diesen präparierten Eintrag kopiert, landen die ersten 256 Zeichen im Puffer, die restlichen 44 Zeichen werden in die auf den reservierten Puffer folgenden Speicherstellen geschrieben und überschreiben die dort ursprünglich gespeicherten Daten. Wenn die dann später verwendet werden, kommt es natürlich zu Problemen.

Am häufigsten treten Pufferüberläufe auf dem Stack auf. Da dabei für den Programmablauf wichtige Parameter überschrieben werden können, ist das für den weiteren Programmablauf äußerst ungünstig. Im Folgenden werden daher speziell solche stackbasierten Pufferüberläufe beschrieben.

Ein C-Programm und sein Speicher

Ein C-Programm besteht aus einer Reihe von Unterprogrammen, die jeweils lokale Variablen besitzen. Der dem Programm zur Verfügung stehende Speicher ist in drei Segmente unterteilt:

  • Am unteren Ende des Adressraums des Programms liegt das Codesegment mit dem Programmcode, dessen Größe durch die Länge des Codes vorgegeben ist.
  • Darauf folgt das nach oben wachsende, auch Heap genannte Datensegment für die Daten des Programms.
  • Das Stack-Segment zur Zwischenspeicherung lokaler Variablen und gesicherter Prozessorregister beginnt am oberen Ende des Adressraums des Programms und wächst nach unten.

Der Stack funktioniert nach dem Prinzip "last in, first out": Beim Aufruf eines Unterprogramms werden zuerst die übergebenen Parameter und die Rücksprungadresse auf den Stack geschrieben, danach die lokalen Variablen. Das aktuelle Ende des Stacks wird vom Stackpointer markiert.

Ein Pufferüberlauf auf dem Stack

Im obigen Beispiel werden also zuerst die übergebenen Parameter und danach die Rücksprungadresse auf den Stack geschrieben, danach ein Puffer für 256 Zeichen reserviert. Der Stackpointer zeigt auf das unterste Byte des Puffers, da die lokalen Variablen von unten nach oben geschrieben werden. Der Stack sieht dann wie in Abbildung 1 aus.

Der Stack beim Start des Unterprogramms
Abb. 1: Der Stack beim Start des Unterprogramms

Jetzt schreibt das Unterprogramm den Dateinamen in den reservierten Puffer. Solange der nicht länger als 256 Zeichen ist, ist alles in Ordnung. Ist der Name länger, wird die meist mehr als ein Byte belegende Rücksprungadresse ganz oder teilweise überschrieben, siehe Abbildung 2.

Der Stack nach dem Pufferüberlauf
Abb. 2: Der Stack nach dem Pufferüberlauf

Wird das Unterprogramm beendet, zeigt der Stackpointer auf die Speicherstelle, an der bei seinem Start die Rücksprungadresse gespeichert wurde. Dort steht nun aber nicht mehr die ursprünglichen Adresse, sondern ein Teil der Eingabe, siehe Abbildung 3.

Der Stack mit überschriebener Rücksprungadresse
Abb. 3: Der Stack mit überschriebener Rücksprungadresse

Der Prozessor versucht nun, von dieser Adresse den nächsten Befehl zu laden. Meist löst das einem Fehler aus, zum Beispiel eine Speicherschutzverletzung, wenn das Programm auf die angegebene Adresse nicht zugreifen darf. Das ist ärgerlich, aber mehr als dass der Audioplayer sich mit einer Fehlermeldung beendet oder einfach abstürzt passiert dabei nicht.

Gefährlich wird es, wenn die Rücksprungadresse auf eine Stelle im Speicher verweist, auf die das Programm zugreifen darf und an der ein ausführbarer Befehl steht. Denn dieser Befehl wird dann an Stelle des eigentlich vorgesehenen Befehls ausgeführt.

Angriff über die Pufferüberlaufschwachstelle

Ein Angreifer kann im Puffer eigenen Code unterbringen und die Rücksprungadresse so wählen, dass dieser Code angesprungen und ausgeführt wird. Das ist nicht ganz einfach, denn die Rücksprungadresse muss als absoluter Wert angegeben werden und der Angreifer weiß im Allgemeinen nicht, an welcher Adresse sein eingeschleuster Code beginnt. Um dieses Problem zu umgehen, werden vor dem eigentlichen Schadcode einige NOP-Befehle geschrieben. Diese belegen jeweils nur 1 Byte und tun nichts. Ist die erratene Rücksprungadresse falsch, zeigt sie mit etwas Glück auf einen der NOP-Befehle, und diese Befehle werden dann so lange abgearbeitet, bis der eigentliche Schadcode erreicht wird, siehe Abbildung 4.

Der Stack mit eingeschleustem Schadcode
Abb. 4: Der Stack mit eingeschleustem Schadcode

Dieser eingeschleuste Code lädt dann meist weiteren Schadcode nach, über den der Angreifer die Kontrolle über den angegriffenen Rechner erlangt.

Varianten des Pufferüberlaufs

Es gibt mehrere Varianten von Pufferüberlauf-Schwachstellen. Beispielsweise kann der Stackpointer überschrieben werden, so dass direkt zuvor eingeschleuster Schadcode angesteuert werden kann. Oder es ist nur das Überschreiben eines einzigen Bytes möglich (Off-By-One), zum Beispiel weil beim Kopieren eines Strings in ein Array das den String abschließende Null-Byte nicht berücksichtigt wurde. Und auch im normalen Speicher ist ein Pufferüberlauf möglich. Je nachdem, was jeweils überschrieben wird, lässt sich so ein Fehler mehr oder weniger leicht oder auch gar nicht zum Einschleusen von Code ausnutzen.

Schutzmaßnahmen

Pufferüberlauf-Schwachstellen entstehen durch Programmierfehler: Vom Benutzer manipulierbare Daten werden in einen Puffer fester Größe kopiert, ohne vorher zu prüfen, ob sie auch in den Puffer passen. Diese Schwachstellen können also ganz einfach verhindert werden, indem die Eingabedaten geprüft werden. Hier geht es aber um Schutzmaßnahmen gegen Angriffe über diese Schwachstellen, und in der nächsten Folge wird die erste davonvorgestellt: Die Data Execution Prevention DEP.

Carsten Eilers


Übersicht über alle Artikel zum Thema

Schutzmaßnahmen: Content Security Policy gegen XSS, Teil 1
Schutzmaßnahmen: Content Security Policy gegen XSS, Teil 2
Schutzmaßnahmen: Content Security Policy gegen XSS, Teil 3
Schutzmaßnahmen: Content Security Policy gegen XSS, Teil 4
Schutzmaßnahmen: Der Pufferüberlauf im Überblick
Schutzmaßnahmen: Canary und DEP gegen Pufferüberlauf-Schwachstellen
Schutzmaßnahmen: ASLR gegen Pufferüberlauf-Schwachstellen
Schutzmaßnahmen: ASLR kann unterlaufen werden
Schutzmaßnahmen: Weitere Angriffe trotz ASLR

Trackbacks

Dipl.-Inform. Carsten Eilers am : Drucksache: Windows Developer 7.16 - Alles im Fluss?

Vorschau anzeigen
Im windows.developer 7.16 ist ein Artikel über Angriffe auf den Kontrollfluss von .NET-Anwendungen erschienen. Das alte Wettrennen zwischen Angreifern auf und Verteidigern von Windows-Rechnern geht in eine neue Runde: Auf der DEF CON 2

Dipl.-Inform. Carsten Eilers am : Schutzmaßnahmen: Canary und DEP gegen Pufferüberlauf-Schwachstellen

Vorschau anzeigen
Was ein Pufferüberlauf (Buffer Overflow) ist und wie er ausgenutzt wird, wurde bereits beschrieben. Nun geht es um die Frage, wie man diese Angriffe verhindern oder zumindest erschweren kann. Der Pufferüberlauf ist Entwickler-Sache

Dipl.-Inform. Carsten Eilers am : Schutzmaßnahmen: ASLR gegen Pufferüberlauf-Schwachstellen

Vorschau anzeigen
Eine der ersten beiden Schutzmaßnahmen gegen Angriffe auf Pufferüberlauf-Schwachstellen ist die Data Execution Prevention (DEP). Aber die lässt sich umgehen, reicht also als alleiniger Schutz nicht aus. Angriff trotz DEP Be