Sonntag, 21. August 2011


Topthema

Donnerstag, 12. Mai 2005 | Topthema

About Security #5: Der Pufferüberlauf

(Link zum Artikel: http://www.entwickler.de/entwicklerde/kolumnen/021633)
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share

Der Pufferüberlauf (Buffer Overflow) ist eine vor allem in C-Programmen häufig vorkommende Schwachstelle. In dieser Folge erfahren Sie, was ein Pufferüberlauf ist und wie er durch einen Angreifer ausgenutzt werden kann.

N E U ! Security aktuell
Täglich aktuelle Security-Infos!

Wie kommt es zu einem Pufferüberlauf?

Ein Programm kopiert Daten in einen reservierten Puffer fester Größe, ohne zuvor die Größe dieser Daten zu überprüfen. Ist der Puffer zu klein, kommt es zu einem Überlauf. Die überzähligen Daten werden dadurch in die auf den reservierten Puffer folgenden Speicherstellen geschrieben. Dabei werden die dort ursprünglich gespeicherten Daten überschrieben, was bei deren späterer Verwendung zu Problemen führt.

Was passiert bei einem Pufferüberlauf?

Das Folgende gilt insbesondere für C-/C++-Programme, ist aber auch allgemein übertragbar. Programme in C und C++ bestehen aus einer Reihe von Unterprogrammen, die jeweils lokale Variablen besitzen. Der jedem Programm zur Verfügung stehende Speicher ist in drei Segmente unterteilt: Das Codesegment mit dem Programmcode, das Datensegment (auch Heap genannt), und das Stack-Segment zur Zwischenspeicherung lokaler Variablen und gesicherter Prozessorregister.

Diese Segmente sind folgendermaßen angeordnet: Am unteren Ende des Adressraums des Programms liegt das Codesegment, dessen Größe durch die Länge des Programmcodes vorgegeben ist. Daran schließt sich das Datensegment an, das nach oben wächst. Der Stack beginnt am oberen Ende des Adressraums des Programms und wächst nach unten. Der Stackpointer (ESP) markiert das Ende des Stacks, der nach dem Prinzip "last in, first out" funktioniert. Beim Aufruf eines Unterprogramms werden zuerst die Übergabeparameter und die Rücksprungadresse auf den Stack geschrieben, danach die lokalen Variablen.

Der Überlauf und seine Folgen

Nehmen wir ein Unterprogramm, das n Bytes in einen Puffer schreiben soll. Zuerst werden ggf. vorhandene Übergabeparameter und danach die Rücksprungadresse auf den Stack geschrieben, danach ein Puffer von n Bytes Länge reserviert. Der Stackpointer zeigt auf das unterste Byte des Puffers, da die lokalen Variablen von unten nach oben geschrieben werden. Der Stack sieht dann folgerndermaßen aus:


...
ggf. Übergabeparameter
Rücksprungadresse
Platz für das n. Bytes
...
Platz für das 2. Byte
Platz für das 1. Byte <- Stackpointer

Jetzt werden die Daten ohne Prüfung in den Puffer geschrieben. Solange es nicht mehr als n Byte sind, ist alles in Ordnung. Werden mehr als n Byte geliefert, wird die Rücksprungadresse, die meist mehr als 1 Byte belegt, ganz oder teilweise überschrieben:


...
überschriebene Rücksprungadresse
Daten, n. Bytes
...
Daten, 2. Byte
Daten, 1. Byte
About Security: Die komplette Serie

Wenn das Unterprogramm beendet wird, zeigt der Stackpointer auf die Speicherstelle, an der die Rücksprungadresse gespeichert wurde. Dort steht nun aber statt der ursprünglichen Adresse ein Teil der Eingabe. Der Prozessor versucht nun, von dieser Adresse den nächsten Befehl zu laden. Meist führt dies zu einem Fehler, beispielsweise einer Speicherschutzverletzung, wenn das Programm auf die angegebene Adresse nicht zugreifen kann. Je nachdem, um was für ein Programm es sich handelt, kann das schon unangenehm sein, da es einen Denial of Service ergibt. Richtig 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. Dann wird der dort gespeicherte Befehl ausgeführt. Ein Angreifer kann nun im Puffer eigenen Code unterbringen und die Rücksprungadresse so wählen, dass dieser Code angesprungen und ausgeführt wird. Dies ist nicht ganz einfach, da die Rücksprungadresse als absoluter Wert angegeben werden muss und der Angreifer nicht weiß, 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 Rücksprungadresse falsch, zeigt sie eventuell auf einen der NOP-Befehle, und diese Befehle werden dann so lange abgearbeitet, bis der eigentliche Schadcode erreicht ist.

Die Ausnutzung der Schwachstelle

Was hat der Angreifer nun davon, wenn er etwas eigenen Code ausführen kann? Auch mit wenig Code kann ein Angreifer großen Schaden anrichten. Er muss ja nicht alles, was er mit seinem Code erreichen will, selbst programmieren. Stattdessen kann er auf alle Funktionen des Betriebssystems und der installierten Bibliotheken zugreifen, auf die das betroffene Programm ebenfalls zugreifen darf. So genannter Shell-Code zum Öffnen einer Shell auf einem beliebigen TCP-Port kommt mit rund 200 Bytes aus und ist im Internet verfügbar. Gelingt einem Angreifer das Einschleusen derartigen Codes, kann er danach die Kontrolle über das System übernehmen.

In dieser Folge erfuhren Sie, wie es zu einem Pufferüberlauf kommt. In der nächsten Folge erfahren Sie, wo Pufferüberläufe auftreten und wie sie wozu ausgenutzt werden können.

Wenn Sie Fragen oder Themenvorschläge haben, können Sie diese gerne an die angegebene E-Mail-Adresse senden oder im Security-Forum einbringen!

Carsten Eilers

About Security – Übersicht zum aktuellen Thema "Eine typische Schwachstelle: Der Pufferüberlauf"

Kommentare

Folgende Links könnten Sie auch interessieren