Ein Buffer-Overflow tritt auf, wenn ein Programm versucht, mehr Daten in den Speicher zu schreiben, als eigentlich für das Programm reserviert. Buffer Overflows sind erhebliche Schwachstellen in Programmen.

Entstehung

Ein einfaches Programmbeispiel, das für einen Buffer-Overflow anfällig ist:

#include <stdio.h>
 
int main(int argc, char** argv) {
    char buffer[512];
    strcpy(buffer, argv[1]);
    printf("You entered: %s\n", buffer);
    return 0;
}

Da strcpy beim die Bufferlänge ignoriert, können Eingaben die länger sind als der Buffer ihn überschreiben und Daten in nicht-allozierten Speicher gelangen.

Achtung

Statt strcpy sollte immer strncpy verwendet werden!

Schwachstellen

Ist ein Funktionsaufruf vorbei, springt das Programm normalerweise in die Rücksprungadresse, die auf dem Stack liegt. Ein gezielter Angriff kann mittels Buffer Overflow (z.B. durch nichtüberprüfte Nutzereingaben) Speicher einschließlich der Rücksprungadresse überschreiben. Das birgt Sicherheitsrisiken:

  • Ist die Rücksprungadresse ungültig, stürzt das Programm ab
  • Zeigt die Rücksprungadresse auf andere Funktionen des Programms oder dessen Bibliotheken, kann das Programm dem Nutzer Informationen geben die nicht für ihn bestimmt sind (z.B. Datenbanken)
  • Zeigt die Rüclsprungadresse zurück in den vorher überschriebenen Stack, und dieser enthält Binärcode, kommt dieser zur Ausführung

Konstruktion von Schwachstellen

Möchte man Code mittels Buffer Overflow einschleusen würde man seine Eingabe in das Programm wie folgt aufbauen:

  1. Einige NOP-Aufrufe (0x90), diese stehen für No Operation. Diese Befehle machen nichts außer zum nächsten Befehl zu springen.
  2. Den Schadhaften Code in Maschinensprache
  3. Geeignete Rücksprungadressen, die in den Bereich der NOPs zeigen

Abwehrmechanismen

  • Secure Programming
    • Regeln zur Vermeidung von Buffer Overflows
    • Hardwarenahe Sprachen nur einsetzen wo nötig
    • Vermeidung von Befehlen, die die Eingabelänge nicht verifizieren
  • Code Audit
    • Qualitätssicherung durch unabhängigen Auditor
    • Automatisierte Audits
  • Binary Audit
    • Im wesentlichen identisch zu Aktionen eines Angreifers
    • Ziel: Schwachstellen Identifizieren
  • Canary-Basierter Stack-Schutz
    • Im Stack wird zwischen gespeicherten Adressen und Variable ein (Zufalls-)Wert gespeichert
    • Wurde dieser vor dem Funktionsende verändert wird das Programm abgebrochen
  • Sicherung der Rücksprungadresse
    • Die Rücksprungadresse wird an einem weiteren Platz gesichert und als letzte Operation vor dem Beenden der Funktion wieder zurück geschrieben
  • Safe Library
    • Normale Aufrufe unsicherer Funktionen werden von einem Wrapper ersetzt
  • Non-Executable Stack
    • Daten im Stack werden als nicht ausführbar markiert
  • Address-Space-Layout-Randomisation (ASLR)
    • Adressbereiche werden vom Betriebssystem zufällig vergeben und so die Ermittlung einer geeigneten Rücksprungadresse erschwert