Fuzzing

Von Boris Sander3. November 2025
Fuzzing

Fuzzing

„Millionen von zufälligen Eingaben auf dein Programm werfen und hoffen, dass es nicht explodiert. Wissenschaft!"


Sinn & Zweck

Fuzzing ist die Kunst, Software mit Müll zu füttern, bis sie sich verschluckt. Automatisiert. Millionenfach. 24/7.

Die Idee ist brutal simpel: Generiere zufällige, ungültige oder unerwartete Eingaben und beobachte, ob das Programm abstürzt, hängt oder sonstiges seltsames Verhalten zeigt. Jeder Crash ist ein potenzieller Bug – und jeder Bug könnte eine Sicherheitslücke sein.

Google hat mit Fuzzing tausende Bugs in Chrome gefunden. OSS-Fuzz hat zehntausende CVEs in Open-Source-Projekten aufgedeckt. Fuzzing funktioniert – auch wenn es sich wie digitaler Vandalismus anfühlt.


Arten von Fuzzing

Black-Box Fuzzing (Dumb Fuzzing)

Keine Kenntnis des Programms. Einfach zufällige Daten rein.

Input: jksd89f7as9d8f7asdf
Input: ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
Input: AAAAAAAAAAAAAAAAAAAAAAAAA (buffer overflow, anyone?)
Input: ../../../etc/passwd
Input: <script>alert(1)</script>

Pro: Einfach, braucht keine Vorbereitung Contra: Ineffizient, findet nur oberflächliche Bugs

White-Box Fuzzing

Vollständiger Zugriff auf Source Code. Symbolische Execution.

Generiert Inputs, die spezifische Code-Pfade auslösen – mathematisch berechnet.

Pro: Sehr gründlich Contra: Skaliert nicht, langsam, komplex

Grey-Box Fuzzing (Coverage-Guided)

Der Sweet Spot. Weiß nicht alles, beobachtet aber das Programmverhalten.

Prinzip:

  1. Starte mit Seed-Inputs
  2. Mutiere sie zufällig
  3. Führe das Programm aus
  4. Messe Code-Coverage
  5. Behalte Inputs, die neue Code-Pfade erreichen
  6. Repeat ∞
                    ┌──────────────┐
                    │  Seed Inputs │
                    └──────┬───────┘
                           ↓
                    ┌──────────────┐
          ┌────────→│   Mutator    │←────────┐
          │         └──────┬───────┘         │
          │                ↓                 │
          │         ┌──────────────┐         │
          │         │   Target     │         │
          │         │   Program    │         │
          │         └──────┬───────┘         │
          │                ↓                 │
          │         ┌──────────────┐         │
          │         │   Coverage   │         │
          │         │   Feedback   │         │
          │         └──────┬───────┘         │
          │                ↓                 │
     New path?      ┌──────────────┐         │
       Yes ←────────│   Analysis   │─────────┘
                    └──────────────┘   No
                           ↓
                      Crash? Bug!

Fuzzing-Tools

AFL / AFL++ (American Fuzzy Lop)

Der König des Grey-Box-Fuzzing.

# Compile with instrumentation
afl-gcc -o target target.c

# Start fuzzing
afl-fuzz -i seeds/ -o findings/ -- ./target @@

Features:

  • Coverage-guided
  • Intelligent mutation
  • Crash deduplication
  • Performance optimiert

LibFuzzer

In-process fuzzing, integriert in LLVM/Clang.

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    MyFunction(data, size);
    return 0;
}
clang++ -fsanitize=fuzzer,address -o fuzz_target fuzz_target.cpp
./fuzz_target corpus/

Honggfuzz

Google's Fuzzer, gut für Multi-Prozess-Fuzzing.

OSS-Fuzz

Google's kontinuierliche Fuzzing-Infrastruktur für Open Source.

  • 1000+ Projekte
  • 10.000+ Bugs gefunden
  • Kostenlos für qualifizierte Projekte

Jazzer

JVM-Fuzzing (Java, Kotlin, etc.)

go-fuzz / Fuzz (Go 1.18+)

Native Fuzzing für Go.

func FuzzParseJSON(f *testing.F) {
    f.Add([]byte(`{"valid": "json"}`))
    f.Fuzz(func(t *testing.T, data []byte) {
        var result interface{}
        json.Unmarshal(data, &result)
    })
}

Atheris

Python-Fuzzing mit Coverage-Feedback.

Boofuzz

Netzwerk-Protokoll-Fuzzing (Nachfolger von Sulley).


Mutation Strategies

Bit Flips

Original:  01001010 01010011 01001111 01001110
Mutated:   01001010 01010010 01001111 01001110
                         ^

Byte Replacement

Original:  { "id": 123 }
Mutated:   { "id": 0xFF }

Arithmetic Operations

Original:  length=100
Mutated:   length=101, length=99, length=0, length=-1

Dictionary-based

Interessante Tokens einfügen:

../
..\\
%00
' OR '1'='1
<script>
0xFFFFFFFF

Havoc Mode (AFL)

Kombination mehrerer Mutationen gleichzeitig – maximales Chaos.


Was Fuzzing findet

Memory Corruption

  • Buffer Overflows
  • Use-After-Free
  • Double Free
  • Heap Overflow
  • Stack Overflow

Crashes & Assertions

  • NULL Pointer Dereferences
  • Unhandled Exceptions
  • Failed Assertions
  • Infinite Loops (Timeouts)

Undefined Behavior

  • Integer Overflow
  • Out-of-bounds Access
  • Data Races

Logic Errors

  • Unexpected States
  • Resource Exhaustion
  • Denial of Service

Sanitizers – Die besten Freunde des Fuzzers

Sanitizers instrumentieren den Code und finden Bugs, die nicht sofort crashen würden.

AddressSanitizer (ASan)

Findet Memory-Bugs: Buffer Overflow, Use-After-Free, etc.

gcc -fsanitize=address -o target target.c

MemorySanitizer (MSan)

Findet uninitialized Memory.

UndefinedBehaviorSanitizer (UBSan)

Findet Undefined Behavior: Integer Overflow, etc.

ThreadSanitizer (TSan)

Findet Data Races.

Best Practice: Immer mit ASan und UBSan fuzzen!


Risiken & Grenzen

Coverage ≠ Security

100% Coverage bedeutet nicht 100% sicher. Fuzzing findet, was crasht – nicht, was unsicher ist aber nicht crasht.

Semantic Bugs

Fuzzing ist schlecht bei:

  • Business Logic Errors
  • Authentication Bypasses
  • Authorization Failures
// Fuzzing findet diesen Bug nicht:
if (user.role == "admin" || user.role == "ADMIN") {
    // Bug: Case-sensitive, aber sollte es nicht sein
}

State Explosion

Programme mit viel State sind schwer zu fuzzen. Ein Input allein löst nicht alle Pfade aus.

Crash Triage

Fuzzer: "Ich habe 50.000 Crashes gefunden!"
You: "Wie viele sind unique?"
Fuzzer: "Äh... 3?"

Ressourcenhunger

Ernsthaftes Fuzzing braucht:

  • CPU (viel davon)
  • Zeit (Tage bis Wochen)
  • Storage (für Corpus und Crashes)

Reproduzierbarkeit

Manchmal sind Crashes nicht reproduzierbar. Non-determinismus, Timing-Issues, Heisenberg-Bugs.


Vorteile

Skaliert mit Hardware

Mehr CPUs = mehr Fuzzing = mehr Bugs. Parallelisierung ist einfach.

Findet das Unerwartete

Fuzzing findet Bugs, an die niemand gedacht hat – weil niemand so bescheuerte Inputs schicken würde. Außer Angreifer.

Automatisiert & Kontinuierlich

Einmal aufgesetzt, läuft es. 24/7. Ohne Pause. Ohne Urlaub.

Complement zu anderen Methoden

  • SAST: Findet Patterns
  • DAST: Findet bekannte Vulnerabilities
  • Fuzzing: Findet das, was beide übersehen

Real Bugs, Real Crashes

Wenn der Fuzzer einen Crash findet, gibt es den Bug. Kein False Positive. Reproduzierbar.


Best Practices

  1. Gute Seeds starten

    • Valide Inputs als Basis
    • Je mehr Varianten, desto besser
  2. Sanitizers aktivieren

    • ASan + UBSan mindestens
    • Findet Bugs, die sonst nicht crashen
  3. Corpus pflegen

    • Minimieren (afl-cmin)
    • Deduplizieren
    • Versionieren
  4. Harness schreiben

    • Direkt zur Parsing-Funktion
    • Keine I/O, keine Netzwerk
    • Schnell muss es sein
  5. Lange laufen lassen

    • Stunden sind Minimum
    • Tage/Wochen für tiefe Bugs
    • OSS-Fuzz läuft kontinuierlich
  6. Crash-Triage automatisieren

    • Deduplizierung
    • Severity-Bewertung
    • Root Cause Analysis
  7. In CI/CD integrieren

    • Regression Fuzzing
    • Neue Bugs sofort finden

Fuzzing in der Praxis

Beispiel: JSON-Parser fuzzen

// fuzz_target.c
#include <json.h>

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    char *input = malloc(size + 1);
    memcpy(input, data, size);
    input[size] = '\0';
    
    json_object *obj = json_tokener_parse(input);
    if (obj) {
        json_object_put(obj); // Free
    }
    
    free(input);
    return 0;
}

Seeds (Corpus)

{"valid": "json"}
[]
{"nested": {"deep": {"value": 123}}}
{"array": [1, 2, 3]}
{"unicode": "こんにちは"}

Run

clang -fsanitize=fuzzer,address -o fuzz_json fuzz_target.c -ljson-c
./fuzz_json corpus/

Fuzzing-Ergebnisse (Real-World)

ProjektToolBugs gefunden
ChromelibFuzzer, AFL27.000+
FirefoxAFL7.000+
Linux KernelSyzkaller5.000+
OpenSSLOSS-Fuzz200+
SQLiteAFLDutzende

Fazit

Fuzzing ist die Chainsaw unter den Security-Tools: Brutal, laut und verdammt effektiv. Es findet Bugs, die kein Mensch und kein statischer Analyzer je finden würde – weil es einfach alles ausprobiert, was ein kreativer Angreifer auch versuchen würde.

Dein Code denkt, er kann mit allem umgehen. Der Fuzzer beweist das Gegenteil.


Weiterführende Ressourcen: