EMZETT.
Login

Pufferüberlauf und Speichersicherheit: warum C-Programme angreifbar sind

Kurz: Ein Pufferüberlauf entsteht, wenn ein Programm mehr Daten in einen Speicherbereich schreibt, als dieser fasst; dadurch können Nachbardaten oder Rücksprungadressen überschrieben werden. Moderne Schutzmaßnahmen und speichersichere Sprachen bremsen das.

Genauer: In Sprachen wie C/C++ prüft niemand automatisch Arraygrenzen. Schreibt Code über das Ende hinaus, überschreibt er Nachbardaten: Das ist die Wurzel vieler schwerer Sicherheitslücken (Morris-Wurm 1988, Heartbleed 2014 als Überlesen). Der Artikel erklärt Prinzip und Schutz, ohne Angriffsanleitung.

Im Detail

Das Prinzip

Ein Programm reserviert z. B. 8 Bytes für einen Namen (char name[8]) auf dem Stack. Kopiert es ungeprüft mehr Zeichen (strcpy, gets), laufen die Daten in benachbarten Speicher: andere Variablen, gespeicherte Rücksprungadresse oder Funktionszeiger. Ein Angreifer kann so den Programmablauf umlenken. Verwandt: Heap-Überlauf, Use-after-free (Speicher nach Freigabe weiterverwendet), Double Free, Off-by-one, Format-String-Fehler, Integer-Überlauf (führt zu zu kleinem Puffer), Out-of-bounds-Lesen (Heartbleed).

Schutzmechanismen

  • Stack Canaries: Wächterwert vor der Rücksprungadresse, Prüfung vor dem Rücksprung.
  • ASLR (zufällige Speicheradressen), PIE.
  • NX/DEP (Datenbereiche nicht ausführbar), W^X.
  • RELRO, CFI/Shadow Stack (Kontrollfluss-Integrität), Intel CET, ARM PAC/MTE.
  • Compiler-Warnungen und -Schutz (-Wall -Wextra, -fstack-protector-strong, -D_FORTIFY_SOURCE=2).
  • Sanitizer (AddressSanitizer, UBSan) und Valgrind finden Fehler beim Testen, Fuzzing (AFL++, libFuzzer) provoziert sie. Kein einzelner Schutz ist perfekt; Angreifer kombinieren Techniken (ROP). Beste Strategie: Fehler vermeiden.

Sicherer programmieren

  • Sichere Funktionen: fgets statt gets, snprintf/strlcpy statt sprintf/strcpy, Längen immer prüfen.
  • C++: std::string, std::vector, std::span, .at(), Smart Pointer statt new/delete (RAII).
  • Eingaben validieren, Größen prüfen, Integer-Überläufe beachten.
  • Speichersichere Sprachen: Rust (Ownership, Borrow Checker), Go, Java, C#, Python, Swift: Arraygrenzen werden geprüft, Speicher verwaltet (aber unsafe-Blöcke/native Erweiterungen bleiben Risiko).
  • Politik: Behörden (BSI, CISA) empfehlen speichersichere Sprachen für neue Software; Linux-Kernel, Windows, Android führen Rust ein.
  • Code-Reviews, statische Analyse (clang-tidy, Coverity, CodeQL).

Einordnung und Lernen

  • Rund 60–70 % schwerer Schwachstellen in großen C/C++-Codebasen (Microsoft, Chromium) waren Speicherfehler.
  • Updates einspielen: Viele Angriffe nutzen bekannte Lücken in alten Programmen.
  • Lernen (legal): eigene kleine C-Programme mit ASan/Valgrind untersuchen, CTF-Aufgaben (pwn) auf Übungsplattformen, Vorlesungsmaterial (Stack/Heap, Assembler).
  • Heartbleed als Fallbeispiel: fehlende Längenprüfung → Server-Speicher lesbar.
  • Prüfung: Stack-Aufbau, Ursachen, Gegenmaßnahmen nennen.
  • Verwandt: Segmentation Fault, Speicherleck, Stack und Heap, Valgrind.

Siehe auch: Segmentation Fault, Stack und Heap, Valgrind und Sanitizer, Rust: Ownership und Borrowing