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:
fgetsstattgets,snprintf/strlcpystattsprintf/strcpy, Längen immer prüfen. - C++:
std::string,std::vector,std::span,.at(), Smart Pointer stattnew/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