von Thomas
Erstellt am 23.07.2022
In diesem Artikel vermittel ich euch umfassendes Wissen zum Buffer Overflow. Ein Buffer Overflow ist eine Sicherheitslücke, mit der auf Arbeitsspeicher lesend oder schreibend zugegriffen werden kann, der für das ausführende Programm eigentlich abgeschottet sein sollte.
In modernen Betriebssystemen hat sich die virtuelle Speicherverwaltung durchgesetzt. Dabei ist es üblich, dass einem Prozess durch eine Memory Management Unit (MMU) der virtuelle Speicher, welcher größer als der physikalisch vorhandene Speicher sein kann, zugewiesen wird. Für den ausgeführten Prozess ist der virtuell zur Verfügung stehende Speicher ein logisch zusammenhängender physikalischer Speicher. Physikalisch sind die Speicherräume aber alles andere als beieinander, wodurch es zu Fehlern kommen kann, wenn ein Prozess außerhalb seines von der MMU zugewiesenen Speicher schreibt oder liest - der Buffer Overflow. Die häufigste Variante sind heutzutage der Stack-Buffer Overflow und Heap-Buffer Overflow.
Computersysteme sind meist so komplex aufgebaut, dass ein Prozess diverse Unterprogramme
aufruft. Nach dem Aufruf, benötigt der Programmcode die eigentliche
Rücksprungadresse, wo das Programm vor dem Unterprogramm-Aufruf weiter ausgeführt werden
soll. Bei geschickter Wahl des Eingabestroms, welcher von einem Unterprogramm unter Umständen benötigt
wird,
kann die Rücksprungadresse gezielt überschrieben werden. Ist das einem
Angreifer gelungen, ermöglicht dieses Vorgehen möglicherweise
Shellcode
auszuführen.
Eine weitere fatale Möglichkeit ist, dass Passwörter durch einen Buffer Overflow überschrieben werden können.
Das gezeigte Bild kann ganz einfach mit folgendem C-Code selbst getestet werden. Achtet darauf, beim compilieren mit gcc die Option, -fno-stack-protector zu verwenden. Denn der gcc verhindert solche simplenen Buffer Overflow mittlerweile:
#include#include int main(void) { /* Der Buffer für das Passwort wird auf sechs Zeichen festgelegt */ char buffer[6]; /* Die Variable wird unmittelbar nach dem Buffer auf den Stack gelegt */ int pass = 0; printf("Password eingeben : \n"); gets(buffer); if(strcmp(buffer, "secret")) { printf ("Falsches Password \n"); } else { printf ("Password korrekt \n"); pass = 1; } if(pass) { printf ("Hallo Root User \n"); } return 0; }
Dadurch, dass die Variable pass unmittelbar nach dem buffer definiert wird, liegt sie auch unmittelbar nach dem buffer auf dem Stack. Werden nun sieben Zeichen eingegeben überschreibt das siebte Zeichen die Variable pass auf dem Stack. Durch Längenüberprüfung der Eingabe von gets lässt sich das Problem vermeiden.
Die NIST schreibt in Ihrem Report von 2011, dass die meisten Systeme auf Grund der schlechten Implementierungsphase unsicher sind. So sind laut dem Bericht die meisten Sicherheitslücken auf die Programmiersprachen C,C++ und Java zurückzuführen. Die Programmiersprachen bringen zwar Vorteile mit sich, da sie hardwareorientiert und in Bezug auf die Laufzeit mit zu den schnellsten Programmiersprachen gehören. Der Nachteil ist, dass die manuelle Verwaltung des Arbeitsspeichers Aufgabe der Programmierer ist und teilweise schlecht beziehungsweise gar nicht implementiert wird. Aus diesen Gründen kann bereits die Wahl der Programmiersprache, welche für das Softwaresystem genutzt werden soll, die Sicherheit signifikant erhöhen oder verschlechtern. Ein positiv Beispiel sind die Sprachen Ada, C# und Rust, welche Typensicherheit gewährleisten und bei der Speicherverwaltung auf syntaktische und semantische Prüfung setzten Dadurch ist kein Buffer Overflow möglich. Anzumerken ist, dass die Sicherheitsmechanismen bewusst von Programmieren umgangen werden können und dann keinen entsprechenden Schutz mehr bieten.