Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossar

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)

FunktionProblem
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"
FlagWirkung
-Wall -Wextra -WerrorAlle Warnings als Fehler
-fstack-protector-allStack Canaries
-D_FORTIFY_SOURCE=2Fortify
-fPIE -piePIE
-Wl,-z,relro,-z,nowRELRO
-fsanitize=address,undefinedASan + UBSan

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung