Pufferüberlauf
Buffer Overflows gehören zu den verbreitetsten Sicherheitslücken aktueller Software, welche sich über das Internet ausnutzen lassen. Im wesentlichen können bei einem Buffer Overflow durch Designfehler im Programm zu große Datenmengen in einen unterdimensionierten Speicherbereich geschrieben werden, wodurch ungewollt nachfolgende Informationen im Speicher überschrieben werden. Dadurch kann die Rücksprungadresse des Unterprogramms mit beliebigen Daten überschrieben werden, wodurch dann auch übermittelter Maschinencode des Angreifers ausgeführt werden kann. Dieser Code ist in der Regel so genannter Shellcode und hat als Ziel das Öffnen einer Shell mit den Privilegien des Prozesses, welcher für den Buffer Overflow anfällig ist. Besonders begehrtes Ziel ist bei Unix-Systemen der Root-Zugang. Buffer Overflows in verbreiteter Server- und Clientsoftware werden auch von Internetwürmern ausgenutzt.
Buffer Overflow Attacken sind ein wichtiges Thema in der Computersicherheit & Netzwerksicherheit und können über jegliche Art von Netzwerken und auch lokal auf dem System ausgeführt werden. Behoben werden sie in der Regel durch kurzfristig gelieferte Patches der Hersteller.
Neben Nachlässigkeiten bei der Programmierung werden Buffer Overflows vor allem durch die auf der Von-Neumann-Architektur basierten modernen Computersysteme ermöglicht, gemäß welcher Daten und Programm im gleichen Speicher liegen. Durch diese Hardwarenähe sind sie auch nur unter assemblierten oder compilierten Programmiersprachen ein Problem, interpretierte Skriptsprachen sind, abgesehen von dem Interpreter selber, nicht anfällig, da es keine starren Variablengrenzen gibt.
Programmiersprachen
Die wesentliche Ursache für Buffer Overflows sind aber Programmiersprachen, die nicht die Möglichkeit bieten, Speicherbereiche automatisch zu überwachen, um eine Überschreitung von Speicherbereichen zu verhindern. Hierzu gehören die Sprache C und Assembler, die das Hauptgewicht auf Performance legen und auf eine Überwachung verzichten. Hier ist ein Programmierer gezwungen, von Hand den entsprechenden Code zu generieren, wobei oft entweder absichtlich oder aus Nachlässigkeit darauf verzichtet wird. Die Überprüfung ist häufig auch fehlerhaft codiert, da während der Programmtests diese Programmteile meist nicht oder ungenügend getestet werden.
Die häufig im professionellen Bereich verwendete Programmiersprache C++ bietet zwar die Möglichkeit einer automatischen Überprüfung von Feldgrenzen. Sie setzt aber auf C auf und kann damit auch mit C-Strukturen und -Felder umgehen. Da die Systemrufe der Betriebssysteme ebenfalls C-Strukturen verwenden, ist dies oft nicht zu umgehen, was wiederum unentdeckte Buffer Overflows im Programmcode zur Folge haben kann.
Prozessoren und Programmierstil
Weitere Eigentümlichkeiten sowohl der Sprachen C und C++ sowie die Eigentümlichkeiten der am häufigsten eingesetzten Prozessoren machen das Auftreten von Buffer Overflows wahrscheinlich. Die Programme in diesen Sprachen bestehen ausschliesslich aus Unterprogrammen. Diese Programme besitzen lokale Variablen.
Bei modernen Prozessoren ist es üblich, die Rücksprungadresse eines Unterprogramms und die lokalen Variablen auf einen als Stack bezeichneten Bereich zu legen. Dabei werden beim Unterprogrammaufruf zunächst die Rückkehradresse und danach die lokalen Variablen auf den Stack gelegt. Bei moderen Prozessoren wie dem Intel Pentium wird der Stack durch eingebaute Prozessorbefehle verwaltet und wächst zwingend nach unten. Werden Felder oder Zeichenketten in den lokalen Variablen verwendet, werden diese meist nach oben beschrieben. Wird die Feldgrenze nicht geprüft, kann man damit durch Überschreiten des Feldes die Rückkehradresse auf dem Stack erreichen und gegegenfalls absichtlich modifizieren.
Das folgende Programmstück in C, das in ähnlicher Form oft verwendet wird, zeigt einen solchen Buffer Overflow:
input_line() { char line[1000]; /* Feld ist eigentlich Zeiger */ if (gets(line)) /* gets erhält Zeiger, keine Überprüfung */ parse_line(line); }
Bei Prozessoren, die den Stack nach unten beschreiben, sieht der Stack bei Aufruf von gets (Bibliotheksfunktion unter Unix) so aus:
Rücksprungadresse | |
1000. Zeichen | |
... | |
3. Zeichen | |
2. Zeichen | |
1. Zeichen | Stackpointer |
- Der Stack wächst nach unten, die Variable wird nach oben überschrieben
gets liest eine Zeile von der Eingabe und schreibt die Zeichen ab line[0] in den Stack. Es überprüft die Länge der Zeile nicht. Unter C erhält gets nur die Speicheradresse als Pointer, jedoch keinerlei Information über die verfügbare Länge. Wenn man jetzt 1004 Zeichen eingibt, überschreiben die letzten 4 Bytes die Rücksprungadresse (unter der Annahme, dass eine Adresse hier 4 Bytes gross ist), die man auf ein Programmstück innerhalb des Stack richten kann. In den ersten 1000 Zeichen kann man gegebenfalls ein geeignetes Programm eingeben.
- 00@45eA/%A@4 ... ... ... ... ... ... ... ... ... ... ... ... .. 0A&%
- Eingabe, wird von gets in den Stack geschrieben (1004 Zeichen)
modifizierte Rücksprungadresse | |
line, 1000. Zeichen | |
... | ... |
line, 5. Zeichen | drittes Byte im Code |
line, 4. Zeichen | zweites Byte im Code |
line, 3. Zeichen | Ziel der Rücksprungadresse, Programmcodestart |
line, 2. Zeichen | |
line, 1. Zeichen | Stackpointer |
- Überschreiben der Rücksprungadresse und Programmcode im Stack
Falls das Programm höhere Privilegien besitzt als der Benutzer, kann dieser unter Ausnutzung des Bufferflows durch eine spezielle Eingabe diese Privilegien erlangen.
Gegenmaßnahmen
Programmerstellung
Bei der Erstellung von neuen Programmen sollte unbedingt auf die Überprüfung aller Feldgrenzen geachtet werden. Dadurch werden Buffer Overflows verhindert. Die Verwendung von Programmiersprachen, die automatisch Feldgrenzen überwachen, sollte in Erwägung gezogen werden. Dies ist jedoch nicht immer möglich. Bei Verwendung von C++ sollte die Verwendung von Feldern im C-Stil soweit wie möglich vermieden werden.
input_line() { char line[1000]; /* Feld ist eigentlich Zeiger */ if (fgets(line,1000,stdin)) /* fgets überprüft die Länge */ parse_line(line); }
- Gegenmaßname: fgets überprüft die Eingabelänge
Überprüfung des Programmcodes
Spezielle Überprüfungswerkzeuge erlauben die Analyse des Codes und entdecken gegebenenfalls mögliche Schwachstellen. Allerdings kann der Code zur Feldgrenzenüberprüfung fehlerhaft sein, was oftmals nicht getestet wird.
Unterstützung durch Compiler
In C und C++ steht eine sehr grosse Auswahl bestehender Programme zur Verfügung. Moderne Compiler wie neue Versionen des GNU C-Compilers erlauben die Aktivierung von Überprüfungscodeerzeugung bei der Übersetzung.
Sprachen wie C erlauben aufgrund ihres Designs nicht immer die Überprüfung der Feldgrenzen (Beispiel: gets). Die Compiler müssen andere Wege gehen: Sie fügen zwischen der Rücksprungadresse und den lokalen Variablen Platz für eine Zufallszahl ein. Beim Programmstart wird diese Zahl ermittelt, wobei sie jedesmal unterschiedliche Werte annimmt. Bei jedem Unterprogrammaufruf wird in den dafür vorgesehen Bereich die Zufallszahl geschrieben. Der erforderliche Code wird vom Compiler automatisch generiert. Vor dem Verlassen des Programms über die Rücksprungadresse fügt der Compiler Code ein, der die Zufallszahl auf den vorgesehenen Wert überprüft. Wurde sie geändert, ist auch der Rücksprungadresse nicht zu trauen. Das Programm wird mit einer entsprechenden Meldung abgebrochen.
Rücksprungadresse | |
Zufallszahlbarriere | |
line, 1000. Zeichen | |
... | |
line, 3. Zeichen | |
line, 2. Zeichen | |
line, 1. Zeichen | Stackpointer |
- Gegemaßnahme: Zufallszahlbarriere
Daneben kann man manche Compiler auch veranlassen, beim Unterprogrammaufruf eine Kopie der Rücksprungadresse unterhalb der lokalen Felder zu erzeugen. Diese Kopie wird beim Rücksprung verwendet, die Ausnutzung von Buffer Overflows ist dann wesentlich erschwert:
Rücksprungadresse | |
line, 1000. Zeichen | |
... | |
line, 3. Zeichen | |
line, 2. Zeichen | |
line, 1. Zeichen | |
Kopie der Rücksprungadresse | Stackpointer |
- Gegenmaßnahme: Kopie der Rücksprungadresse
Literatur
- Tobias Klein: Buffer Overflows und Format-String-Schwachstellen, Dpunkt Verlag, ISBN 3-89864-192-9
- Aleph One Smashing The Stack For Fun And Profit, Phrack-Magazin Nr. 49. Dieser Artikel veranschaulicht sehr gut die Wirkungsweise von Bufferoverflows.
- St.Kallnik,D.Pape,D.Schröter, St.Strobel,D.Bachfeld: Eingelocht. Buffer-Overflows und andere Sollbruchstellen Einführungsartikel bei heise Security, mit einfachen Beispielen in C