A.7 Buffer Overflow (Pufferüberlauf)
Der Buffer Overflow ist keineswegs eine aussterbende Spezies, sondern hat jetzt in der Zeit der einfachen Informationsbeschaffung Hochkonjunktur. Das Problem ist in der Tat eines der am meist auftretenden Sicherheitslücken. Es gibt keine Woche, indem es keine Berichte gibt, dass im Programm x eine Sicherheitslücke in Form eines Buffer Overflows entdeckt wurde. Buffer Overflow in einer Suchmaschine eingegeben, liefert Ihnen einen schlagartigen Beweis, wie problematisch das Thema mittlerweile (nicht nur unter Linux) ist.
Buffer Overflows sind meistens das Ergebnis einer »fehlerhaften« Programmierung. Wobei hierbei keine Programmierfehler wie ein Zeiger der ins Nichts zeigt oder dergleichen gemeint sind, sondern die Verwendung von unsicheren Funktionen. Wobei man zu Beginn erst mal niemandem einen Vorwurf machen kann, denn letztendlich benutzte man dazu mal alle Routinen, die einem zur Verfügung standen. Erst im Laufe der Zeit, als es schon zu spät war und unzählbar viele Programme in C erstellt wurden, erkannte man das Übel. Theoretisch kann der Buffer Overflow überall dort »ausgenutzt« werden, wo Daten von einem Netzwerk (bspw. an einem Browser, FTP-Client, ...) oder (seltener) von der Tastatur in einem Speicherbereich mit fester Größe geschrieben werden. Wird die Längenangabe des Speicherbereichs nicht überprüft, ist der Weg für einen Hacker zum so genannten Exploit nicht mehr weit. Zwar hat ein solcher Speicherüberlauf häufig zur Folge, dass ein Programm abstürzt, aber es gibt dennoch (nicht selten) ein paar Möglichkeiten, um diesen Umstand so zu manipulieren, dass programmfremder Code ausgeführt werden kann, um an besondere Privilegien des Systems zu kommen.
Mit dem Buffer Overflow lassen sich folgende Aktionen ausführen:
|
Die Werte von Variablen auf dem Stack könnten manipuliert werden. |
|
Die Rücksprungadresse kann verändert werden, so dass ein Programm an einer beliebigen Stelle mit der Ausführung fortfahren kann. Bspw. könnte der Angreifer die Rücksprungadresse auf einem von ihm erstellten Maschinencode verändern – oder eventuell eine (primitive) Passwortabfrage überspringen. Ausführung von fremdem Code sei nicht möglich, sagen Sie? Der Angreifer hat den Heap auch noch zur Verfügung – da herrschen andere Rechte. |
|
Dasselbe Schema lässt sich auch mit Zeigern auf Funktionen anwenden. Dabei ist theoretisch nicht einmal ein Buffer Overflow erforderlich, sondern es reicht die Speicheradresse aus, wo sich diese Funktion befindet. Die Daten, die für die Ausführung von fremdem Code nötig sind, werden vorzugsweise wieder in einer Variable gespeichert. |
|
Und natürlich der Programmabsturz, was dem Anwender zuhause wohl weniger wichtig erscheint als der Industrie. |
Da die meisten Buffer Overflows auf die Verwendung unsicherer Funktionen der Standard-Bibliothek zurückzuführen sind, finden Sie hier eine kurze Zusammenfassung der am häufigsten verwendeten unsicheren Funktionen und deren empfehlenswerten Alternativen. Natürlich ist es auch möglich, mit der angeblich sichereren Alternative bei falscher Anwendung einen Buffer Overflow auszulösen – allerdings ist dies dann wirklich IHR Fehler.
Tabelle A.1
Unsichere Standard-Funktionen und deren Alternativen
Unsichere Funktion
|
Sicherere Alternative
|
gets()
|
fgets()
|
strcpy()
|
strncpy()
|
strcat()
|
strncat()
|
sprintf()
|
snprintf()
|
getwd()
|
getcwd()
|
Als negatives Beispiel soll die folgende Anwendung dienen:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char puffer1[10];
char puffer2[12];
printf("Eine Zeichenkette eingeben: ");
fgets(puffer1, 10, stdin);
sprintf(puffer2, "%s123", puffer1);
printf("%s\n",puffer2);
return EXIT_SUCCESS;
}
Führen Sie das Beispiel wie folgt aus:
$ ~> gcc -o overf overf.c
$ ~> ./mist
Eine Zeichenkette eingeben: 1234567890
123456789123
Zuerst haben Sie zwar mit fgets() den Buffer Overflow bei der Überschreitung der Eingabe verhindert, aber dann bei der Funktion sprintf() den Puffer überschritten. Sicherere Alternativen zur Funktion sprintf() wären in diesem Beispiel snprintf() oder strncpy() gewesen.
Leider erkennt man an diesem Beispiel häufig noch nicht das Problem des Buffer Overflows. Daher ein weiteres Beispiel:
#include <stdio.h>
int main(void) {
int granted = 0;
char password[8];
sprintf(password, "i-want-to-get-root");
if(granted) { printf("Let's rock\n"); /* setuid(0); */ }
else { printf("Wrong idea\n"); }
}
Nach der Grundidee dürfte der granted-Block ja nie ausgeführt werden, aber ... testen Sie selbst.
Hinweis Vielleicht ist jetzt der eine oder andere über den kurzen Abschnitt zum Thema Buffer Overflow enttäuscht, aber bitte bedenken Sie, dass es sich hier nicht um ein Lehrbuch in C handelt – und das Thema ist zum Teil reines C! Im Kapitel zu den Programmierwerkzeugen finden Sie noch einiges dazu, wie Sie bestimmte Tools bzw. Bibliotheken effektiv gegen Buffer Overflows einsetzen können. Außerdem finden Sie auf der CD im Buch C von A bis Z ein Kapitel zu diesem Thema.
|
|