Snapchange - Snapshot-Based Fuzzing mit KVM Cheatsheet
Snapchange (von AWS) ist ein Rust-Framework für Snapshot-basiertes Fuzzing. Traditionelle Fuzzer starten das Ziel für jeden Input erneut, was fatal langsam ist, wenn der interessante Code hinter teurem Setup sitzt — einem Netzwerk-Handshake, einer Anmeldung, einer geparsten Config, einer geladenen Datenbank. Snapchange nimmt stattdessen einen Memory-Snapshot des Ziels genau im Moment von Interesse, stelle dann diesen Zustand wieder her und mutiere von dort mit KVM, um sehr hohe Iterations-Raten gegen Code zu erreichen, den konventionelles Fuzzing überhaupt nicht erreichen kann.
Snapshot-Fuzzing erfordert KVM und Root. Führe in einer isolierten Umgebung aus.
Anforderungen
| Anforderung | Anmerkung |
|---|
| Linux mit KVM | /dev/kvm zugänglich |
| Rust-Toolchain | Snapchange ist eine Rust-Bibliothek |
| QEMU | Wird zum Erstellen des Snapshots verwendet |
| Root | Für KVM und Speicherzugriff |
Das Modell
| Phase | Was passiert |
|---|
| 1. Snapshot | Führe das Ziel in QEMU zum Interessenspunkt aus, dump Memory + Register |
| 2. Harness | Schreibe einen Rust-Fuzzer, der beschreibt, wo Input geht und wann zu stoppen ist |
| 3. Fuzz | Snapchange stellt den Snapshot pro Iteration wieder her und mutiert den Input |
| 4. Triage | Crashes werden mit dem genauen Register/Memory-Zustand aufgezeichnet |
Der kritische Einblick: Alle Setup-Kosten werden einmal bezahlt, im Snapshot. Jede nachfolgende Iteration startet aus diesem Zustand in Mikrosekunden.
Erstellen eines Snapshots
# Konzeptionell: Führe das Ziel unter QEMU aus, break an der Funktion von Interesse,
# dann dump physischen Memory und Register-Zustand
./snapchange/qemu_snapshot/take_snapshot.sh --target ./my-server
| Artefakt | Enthält |
|---|
fuzzvm.physmem | Vollständiger Guest-Physischer Memory |
fuzzvm.qemuregs | CPU-Register-Zustand |
*.symbols | Symboltabelle für Coverage/Breakpoints |
vmlinux / binary | Für Symbolisierung |
Schreiben eines Fuzzers
// Konzeptionelle Form — siehe Snapchange-Beispiele für vollständigen Code
impl Fuzzer for MyFuzzer {
type Input = Vec<u8>;
const START_ADDRESS: u64 = 0x555555555000;
const MAX_INPUT_LENGTH: usize = 1024;
fn set_input(&mut self, input: &Self::Input, fuzzvm: &mut FuzzVm<Self>) -> Result<()> {
// Schreibe den mutierten Input in den Guest-Speicher, wo das Ziel ihn liest
fuzzvm.write_bytes_dirty(VirtAddr(BUFFER_ADDR), CR3, input)?;
Ok(())
}
fn reset_breakpoints(&self) -> Option<&[AddressLookup]> { /* stop conditions */ }
fn crash_breakpoints(&self) -> Option<&[AddressLookup]> { /* crash sites */ }
}
| Element | Zweck |
|---|
START_ADDRESS | Wo die Ausführung jeder Iteration fortgesetzt wird |
set_input | Injiziere den Fuzz-Input in den Guest-Speicher |
reset_breakpoints | Wo eine Iteration normalerweise endet |
crash_breakpoints | Adressen, die einen Crash anzeigen (z. B. panic, abort) |
Ausführung
| Befehl | Beschreibung |
|---|
cargo run -r -- fuzz -c 8 | Fuzz mit 8 Cores |
cargo run -r -- project translate | Symbolisiere/inspiziere den Snapshot |
cargo run -r -- coverage | Generiere Coverage aus dem Corpus |
cargo run -r -- trace <input> | Single-Step-Trace ein Input |
cargo run -r -- minimize <input> | Schrumpfe einen Crashing-Input |
Warum Snapshot-Fuzzing hier gewinnt
| Ziel-Charakteristik | Konventionelles Fuzzing | Snapchange |
|---|
| Teurer Startup | Bezahlt es jede Iteration | Bezahlt es einmal |
| Erfordert Auth/Handshake | Schwer zu erreichen | Snapshot dahinter |
| Stateful Protocol | Schwierig | Snapshot mid-session |
| Kernel/Hypervisor-Code | Sehr schwierig | Natürlicher Fit |
Coverage & Triage
| Fähigkeit | Anmerkung |
|---|
| Breakpoint-Coverage | Coverage via Breakpoints auf Basic-Blöcken |
| Crash-Dedup | Gruppiert nach Faulting-Adresse/Zustand |
| Single-Step-Traces | Vollständiger Anweisungs-Trace für einen gegebenen Input |
| Memory-Inspektionen | Lese Guest-Speicher zum Crash-Zeitpunkt |
Snapchange vs andere Fuzzer
| Aspekt | Snapchange | AFL++ | LibAFL (Nyx) | syzkaller |
|---|
| Ausführung | KVM-Snapshot-Wiederherstellung | Prozess-Fork | Snapshot (Nyx) | VM + Syscalls |
| Setup-Kosten | Einmal bezahlt | Jede Iteration | Einmal | Pro VM |
| Ziel | Anything in einem VM-Snapshot | Userspace-Binärdateien | Konfigurierbar | OS-Kernel |
| Aufwand | Hoch (schreibe einen Rust-Fuzzer) | Niedrig | Hoch | Mittel |
Vergleiche mit LibAFL (das auch Snapshot-Backends bietet) und syzkaller für Kernel-Syscall-Fuzzing.
Ressourcen