Buffer Overflow - Pufferüberlauf
Ein Buffer Overflow (Pufferüberlauf) tritt auf wenn ein Programm mehr Daten in einen Puffer schreibt als dieser aufnehmen kann, wodurch benachbarte Speicherbereiche überschrieben werden. Buffer Overflows ermöglichen Stack-based Overflows (Rücksprungadresse überschreiben → beliebiger Code), Heap-based Overflows, und Format-String-Angriffe. Schutzmechanismen: ASLR, Stack Canaries, NX-Bit/DEP, PIE, Fortify Source. Buffer Overflows sind eine der ältesten Schwachstellenklassen und Grundlage vieler Exploits.
Buffer Overflows sind über 40 Jahre alt - und immer noch relevant. Der Morris Worm (1988) nutzte einen Stack Overflow in fingerd. OpenSSL Heartbleed (2014) war ein Heap Buffer Over-Read. Log4Shell wurde durch einen Buffer Overflow in Java-Deserialisierung begünstigt. C/C++-Code ohne moderne Schutzmechanismen bleibt anfällig: Embedded Systems, Network Equipment, SCADA/OT-Anlagen, Browser-Engines. Ein Buffer Overflow in der richtigen Stelle ermöglicht Remote Code Execution mit System-Rechten.
Stack-Based Buffer Overflow
Speicherlayout (x86, vereinfacht) - der Stack wächst von hoher zu niedriger Adresse:
[hohe Adresse]
| Funktionsargumente |
| Return Address (RET) | ← Zurücksprungadresse - kritisch!
| Saved Base Pointer |
| Lokale Variablen | ← Buffer liegt hier
| ... |
[niedrige Adresse]
Normaler Funktionsaufruf (anfälliger C-Code):
void vulnerable(char *input) {
char buffer[64]; // 64 Byte Stack-Puffer
strcpy(buffer, input); // UNSICHER: kein Längen-Check!
}
Wenn input > 64 Bytes: buffer läuft über, überschreibt Saved EBP und die Return Address.
Exploit-Ablauf (klassisch)
# Payload gesamt:
python3 -c "import sys; sys.stdout.buffer.write(
b'A' * 64 + # Buffer füllen
b'B' * 4 + # Saved EBP überschreiben
b'\xef\xbe\xad\xde' + # Return Address (little endian!)
b'\x90' * 20 + # NOP-Sled
shellcode # Eigentlicher Shellcode
)" | ./vulnerable_program
Unsafe C-Funktionen (immer vermeiden)
| Funktion | Problem |
|---|---|
gets() | kein Längencheck, immer unsicher |
strcpy() | kein Längencheck |
strcat() | kein Längencheck |
sprintf() | bei Format-String-Missbrauch |
scanf() | %s ohne Längenangabe |
Sichere Alternativen:
fgets(buffer, sizeof(buffer), stdin) // mit Längencheck!
strncpy(dest, src, sizeof(dest)-1) // mit Längencheck
strlcpy(dest, src, sizeof(dest)) // BSD-Erweiterung
snprintf(buf, sizeof(buf), ...) // sicher
Schutzmechanismen
1. Stack Canaries (Kanarienvögel im Bergwerk)
- Compiler fügt vor Return Address einen Zufallswert ein
- Bei Rückkehr aus Funktion: Wert prüfen
- Wenn verändert: Programm abbrechen
- GCC:
-fstack-protector-all
# Prüfen ob Canary aktiv:
checksec --file=./program
# Output: STACK CANARY: Enabled
Umgehung: Format String → Stack-Leak → Canary-Wert lesen → in Overflow einfügen → Prüfung besteht.
2. ASLR (Address Space Layout Randomization)
- Betriebssystem: Stack/Heap/Libraries bei jedem Start anders
- Angreifer kennt Zieladresse nicht → Exploit schwerer
# Linux - Status prüfen:
cat /proc/sys/kernel/randomize_va_space
# 0 = deaktiviert, 2 = vollständig aktiv
# Aktivieren:
echo 2 > /proc/sys/kernel/randomize_va_space
Umgehung:
- Information Leak: Programmadresse über anderen Bug lesen
- Brute Force (32-Bit: nur 16-Bit Entropie → 65.536 Versuche)
- ret2libc: libc immer an vorhersagbarer Stelle (ohne PIE)
3. NX-Bit / DEP (No-Execute / Data Execution Prevention)
- Speicherseiten: entweder beschreibbar ODER ausführbar (nicht beides!)
- Shellcode auf Stack → nicht ausführbar
- Hardware: CPU-NX-Bit (Intel XD, AMD NX)
- OS: W^X (Write XOR Execute)
checksec --file=./program
# NX: Enabled
Umgehung: Return Oriented Programming (ROP) - nutzt vorhandene Code-Fragmente (Gadgets) in legitimen Bibliotheken, ohne eigenen Code einzuschleusen.
4. PIE (Position Independent Executable)
- Programm selbst lädt an zufälliger Adresse (nicht nur Libraries)
- Verstärkt ASLR: alle Adressen unbekannt
- GCC:
-fPIE -pie
5. Fortify Source
- Compile-Time + Runtime: Puffergröße bekannt → prüfen
- GCC:
-D_FORTIFY_SOURCE=2 - Ersetzt
strcpy→__strcpy_chk(mit Größencheck!)
6. Control Flow Integrity (CFI)
- Erlaubte Sprungziele validieren
- Return-Adresse muss zu einer bekannten Rücksprungstelle zeigen
- Clang CFI, Microsoft Control Flow Guard (CFG)
- Erschwerend für ROP-Chains
Stack Canaries + ASLR + NX + PIE + CFI zusammen machen Exploit-Entwicklung sehr schwierig (aber nicht unmöglich!) - typischerweise werden mehrere Bugs benötigt: Info-Leak + Buffer Overflow + CFI-Bypass.
Moderne Varianten
Heap-Based Buffer Overflow
Heap-Speicher ist dynamisch alloziert (malloc/new) - auf dem Heap gibt es keine Return-Address, daher sind andere Angriffspfade nötig.
Ziele auf dem Heap:
- Heap-Metadaten (free list, chunk headers) → tcache poisoning
- Funktionszeiger auf Heap → überschreiben → Code Execution
- C++ vtable pointer → überschreiben → beliebige Methode aufrufen
// Beispiel (Use-After-Free + Heap Overflow):
char *buf = malloc(16); // 16 Bytes alloziert
read(0, buf, 256); // Schreibt 256 Bytes → Heap Overflow!
// → Überschreibt Nachbar-Chunk-Header
Format-String-Schwachstelle
printf(user_input); // FALSCH: user_input als Format-String!
printf("%s", user_input); // RICHTIG: festes Format
Angriff:
user_input = "%x.%x.%x.%x"→ liest Stack-Werte aus (Leak!)user_input = "%n"→ schreibt in Speicher (Code Execution!)- Angreifer: liest Canary-Wert, Adressen → ASLR-Bypass → klassischer Buffer Overflow mit bekannten Adressen
Integer Overflow → Buffer Overflow
uint8_t size = user_provided_size; // 0-255
size = size * 2; // Overflow! 200*2=400 → 400%256=144!
char *buf = malloc(size); // Zu kleiner Buffer!
memcpy(buf, src, user_provided_size * 2); // Overflow!
Erkennung und Prävention
Statische Analyse
flawfinder ./src/ # C/C++ Schwachstellen-Scanner
cppcheck --enable=all ./ # C++ statische Analyse
# CodeQL: github.com/github/codeql-action (CI/CD-Integration)
Dynamische Analyse
# AddressSanitizer (ASan):
# Compile mit: -fsanitize=address → Heap/Stack-Overflows erkennen
valgrind --tool=memcheck ./program # Memory-Fehler erkennen
# American Fuzzy Lop (AFL):
afl-fuzz -i inputs/ -o findings/ ./program
Compiler-Flags Best Practice
CFLAGS="-Wall -Wextra -Werror \
-fstack-protector-all \
-D_FORTIFY_SOURCE=2 \
-fPIE -pie \
-Wl,-z,relro,-z,now \
-fsanitize=address,undefined"
| Flag | Wirkung |
|---|---|
-Wall -Wextra -Werror | Alle Warnings als Fehler |
-fstack-protector-all | Stack Canaries |
-D_FORTIFY_SOURCE=2 | Fortify |
-fPIE -pie | PIE |
-Wl,-z,relro,-z,now | RELRO |
-fsanitize=address,undefined | ASan + UBSan |