
Hochperformantes Fuzzing mithilfe von RISC-V-zu-x86-Binärübersetzungen und modernen Fuzzing-Techniken
Startdatum: Dez. 2021
Dies ist ein coverage-gesteuerter, emulationsbasierter Greybox-Fuzzer, der einen eigenen Just-In-Time-Compiler nutzt, um eine nahezu native Leistung zu erreichen. Er funktioniert, indem RISC-V-ELF-Binaries in eine Zwischendarstellung (Intermediate Representation) angehoben werden, bevor sie während der Ausführung per JIT zu x86 kompiliert werden. Während der JIT-Kompilierung wird der Code instrumentiert, um Fuzzing-Verbesserungen wie Coverage-Tracking, ASAN, CmpCov oder Snapshot-basiertes Fuzzing zu ermöglichen.

Das Ziel dieses Projekts ist es, die Vorteile einer emulierten Umgebung für das Fuzzing hervorzuheben. Es gibt bereits viele frühere Fuzzer, die auf Emulation basieren, aber sie verwenden fast ausschließlich die QEMU-Emulationsengine für die zugrunde liegende Emulation. Diese Engine verfügt zwar über einen recht ausgereiften Just-In-Time-Compiler und erzeugt sehr guten Code, ist aber nicht für Fuzzing ausgelegt. Beim Fuzzing beabsichtigen wir, denselben Prozess tausende Male pro Sekunde auszuführen. Das schafft Raum für spezialisierte Optimierungen, die QEMU nicht intensiv nutzt, wie etwa die Wiederverwendung desselben Speicherplatzes für jeden Prozesslauf und das Zurücksetzen nur einer begrenzten Menge an Speicher über Dirty-Bit-Mechaniken.
In vielerlei Hinsicht ist dies eher ein Proof-of-Concept, an dem ich arbeiten wollte, um etwas über Compiler-Interna zu lernen, und um einen emulationsbasierten Spielplatz zu haben, auf dem ich mit verschiedenen Fuzzing-Techniken wie unterschiedlichen Coverage-Metriken, Seed-Schedulern und Snapshot-basiertem Fuzzing herumspielen kann. Mit weiteren JIT-Optimierungen und vor allem Erweiterungen um gängigere Architekturen wie MIPS oder ARM könnte dies jedoch durchaus genutzt werden, um Closed-Source-Code effizient zu fuzzen, der nicht einfach durch Rekompilierung instrumentiert werden kann.
Angesichts der bisher durchgeführten Tests hat Sfuzz deutlich weniger Overhead als viele andere verbreitete Fuzzer, was zu einer sehr schnellen Leistung führt, insbesondere bei kleinen Fuzz-Fällen.
Weitere Details zu den Funktionen/Entscheidungen dieses Fuzzers finden sich im zugehörigen Blogbeitrag (https://seal9055.com/blog/fuzzing/sfuzz) und in den unten aufgeführten Dokumentationsdateien:
Dieser gesamte Fuzzer ist in Rust geschrieben. Nach dem Klonen des Repositorys genügt es, cargo build --release auszuführen, um ihn zu kompilieren.
Da der Fuzzer derzeit nur RISC-V unterstützt, muss das Ziel mit der folgenden Toolchain (oder einer ähnlichen) zu RISC-V kompiliert werden. Alternativ kannst du auch eine bereits vorhandene RISC-V-Binary verwenden, die ebenfalls einwandfrei funktioniert.
Sobald dies eingerichtet ist, erstelle einfach Eingabe-/Ausgabeverzeichnisse, lege ein paar anfängliche Seed-Dateien in das Eingabeverzeichnis und starte den Fuzzer.
./sfuzz -i in -o out -- ./test_cases/simple_test @@
Zusätzliche Flags können über die Befehlszeilenoptionen übergeben werden, um z. B. die Anzahl der Threads festzulegen, Snapshot-Fuzzing zu aktivieren, dem Mutator ein Wörterbuch hinzuzufügen usw. Die zusätzlichen Optionen können aufgelistet werden, indem Sfuzz mit dem Flag -h ausgeführt wird.
Wenn du den Fuzzer gegen einige Ziele mit unterschiedlicher Komplexität testen möchtest, kann der Programm-Generator unter tools/program_generator verwendet werden, um automatisch Programme unterschiedlicher Komplexität zu erzeugen. Beachte, dass du eine RISC-V-Toolchain benötigst, um das Ziel anschließend zu kompilieren.
Dies richtet eine Toolchain ein, um RISC-V-Binaries zu kompilieren, die von diesem Projekt geladen/verwendet werden können.
RISC-V-Compiler/Werkzeuge:
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
Diese Liste stellt eine Reihe von Funktionen dar, die ich in Zukunft zu implementieren plane.