
Fuzzing ad alte prestazioni tramite traduzioni binarie da riscv a x86 e tecniche di fuzzing moderne.
Data di inizio: dicembre 2021
Questo è un fuzzer greybox basato su emulazione e guidato dalla copertura, che utilizza un compilatore Just-In-Time personalizzato per ottenere prestazioni quasi native. Funziona portando i binari ELF RISC-V a una rappresentazione intermedia prima di compilarli JIT in x86 durante l'esecuzione. Durante la compilazione JIT, il codice viene strumentato per abilitare miglioramenti del fuzzing come il tracciamento della copertura, asan, cmpcov o il fuzzing basato su snapshot.

L'obiettivo di questo progetto è evidenziare i vantaggi dell'utilizzo di un ambiente emulato per il fuzzing. Esistono molti fuzzer precedenti basati su emulazione, ma quasi tutti usano il motore di emulazione qemu per l'emulazione sottostante. Sebbene questo motore abbia un compilatore just-in-time abbastanza maturo e generi codice molto buono, non è progettato per il fuzzing. Durante il fuzzing, intendiamo eseguire lo stesso processo migliaia di volte al secondo. Questo lascia spazio a ottimizzazioni specializzate che qemu non sfrutta appieno, come riutilizzare lo stesso spazio di memoria per ogni esecuzione del processo e resettare solo una quantità limitata di memoria tramite i meccanismi dei dirty bit.
Per molti versi, questo è più una proof-of-concept su cui volevo lavorare per imparare i meccanismi interni dei compilatori e avere un parco giochi basato su emulazione per sperimentare varie tecniche di fuzzing come diverse metriche di copertura, scheduler di seed e fuzzing basato su snapshot. Con più ottimizzazioni JIT e, soprattutto, estensioni per includere architetture più popolari come mips o arm, potrebbe comunque essere sicuramente utilizzato per fare fuzzing efficiente su codice closed source che non può essere semplicemente strumentato tramite ricompilazione.
Considerando i test che ho svolto finora, sfuzz ha un overhead significativamente inferiore rispetto a molti altri fuzzer popolari, il che si traduce in prestazioni molto elevate, soprattutto per piccoli casi di fuzz.
Maggiori dettagli sulle funzionalità/scelte adottate per questo fuzzer sono elencati nel blogpost allegato (https://seal9055.com/blog/fuzzing/sfuzz) e nei file di documentazione elencati di seguito:
Questo intero fuzzer è scritto in Rust, quindi dopo aver clonato il repository basta eseguire cargo build --release per compilare.
Poiché il fuzzer attualmente supporta solo RISC-V, il target deve essere compilato per RISC-V usando la toolchain qui sotto (o una simile). In alternativa, se hai già un binario RISC-V, funzionerà perfettamente lo stesso.
Una volta configurato tutto, crea le directory di input/output, aggiungi alcuni file seed iniziali alla directory di input e avvia il fuzzer.
./sfuzz -i in -o out -- ./test_cases/simple_test @@
È possibile passare flag aggiuntivi tramite le opzioni della riga di comando per specificare il numero di thread, abilitare il fuzzing basato su snapshot, aggiungere un dizionario al mutator, ecc. Le opzioni aggiuntive possono essere elencate eseguendo sfuzz con il flag -h.
Se desideri testare il fuzzer su alcuni target di complessità variabile, il progrem_generator in tools/program_generator può essere usato per generare automaticamente programmi di complessità variabile. Nota che ti servirà una toolchain RISC-V per compilare il target.
Questo configura una toolchain per compilare binari riscv che possono essere caricati/usati da questo progetto.
Riscv compiler/tooling:
sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev \
libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev \
libexpat-dev
git clone https://github.com/riscv/riscv-gnu-toolchain && cd riscv-gnu-toolchain
./configure --prefix=/opt/riscv --with-arch=rv64i
sudo make
Debugger:
gdb-multiarch
Questa lista rappresenta una serie di funzionalità che ho intenzione di implementare in futuro.