
Frida-basierte In-Prozess-Fuzzing-Suite mit AFL++-Proxy, eigenständigen aktiven/passiven Modi und Shared-Memory-Kommunikation für leistungsstarke, abdeckungsgesteuerte Schwachstellensuche auf verschiedenen Plattformen.
fpicker ist eine auf Frida basierende Fuzzing-Suite, die eine Vielzahl von Fuzzing-Modi für In-Process-Fuzzing bietet, wie zum Beispiel einen AFL++-Modus oder einen passiven Tracing-Modus. Sie sollte auf allen Plattformen laufen, die von Frida unterstützt werden.
Einige Hintergrundinformationen sowie die Gedanken und Ideen hinter fpicker finden sich in einem Blogbeitrag, den ich geschrieben habe.
Fpicker basiert auf früheren Arbeiten an ToothPicker, die im Rahmen meiner Masterarbeit entwickelt wurden. Der Großteil von fpicker wurde während der Arbeitszeit bei meinem Arbeitgeber (ERNW) entwickelt.
Erforderlich für die Ausführung von fpicker:
frida-core-devkit für die jeweilige Plattform, das unter Frida-Releases auf GitHub zu finden ist.
libfrida-core-ios.a, libfrida-core-macos.a oder libfrida-core-linux.a.frida-core.h). Speichern Sie sie je nach Plattform als frida-core-linux.h oder frida-core-ios.h.update_frida_version.sh aus.Nur erforderlich, wenn im AFL++-Modus ausgeführt:
CFLAGS="-DUSEMMAP=1".CFLAGS="-DUSEMMAP=1 -DTARGET_OS_IOS".Fpicker kann für macOS, iOS oder Linux erstellt werden. Das Makefile unterstützt derzeit nur das Erstellen für iOS unter macOS, aber es sollte durchaus möglich sein, fpicker mit einer iOS-Toolchain unter Linux zu bauen.
Führen Sie je nach gewünschtem Ziel aus:
make fpicker-macos
make fpicker-ios
make fpicker-linux
um fpicker zu bauen.
Sobald fpicker gebaut ist, muss als nächstes die Fuzzing-Harness erstellt werden:
Siehe den Beispiele-Ordner für verschiedene Beispiel-Fuzzing-Fälle. Der allgemeine Ansatz ist wie folgt:
examples/test/test.js) (siehe hier für weitere Informationen zu Harnesses)frida-compile test.js -o harness.jsJetzt kann fpicker mit dem Fuzzing beginnen. Der genaue Befehl hängt stark von der Konfiguration und dem Setup ab. Im Folgenden werden einige Beispielfälle aufgeführt. Diese entsprechen weitgehend den Beispielen im Beispiele-Ordner.
afl-fuzz -i examples/test-network/in -o ./examples/test-network/out -- \\
./fpicker --fuzzer-mode afl -e attach -p test-network -f ./examples/test-network/harness.js
./fpicker --fuzzer-mode standalone -e attach -p server-process -f harness.js --input-mode cmd \\
--command "./client-send @@" -i indir -o outdir
./fpicker --fuzzer-mode active --communication-mode shm -e attach -p server-process -f harness.js \\
-i indir -o outdir --standalone-mutator cmd --mutator-command "radamsa"
./fpicker --fuzzer-mode passive --communication-mode send -e attach -p server-process -o outdir -f harness.js
./fpicker --fuzzer-mode active -e attach -p test -D remote -o examples/test/out/ -i examples/test/in/ \\
-f fuzzer-agent.js --standalone-mutator cmd --mutator-command "radamsa"
Jedes Ziel erfordert eine eigene Fuzzing-Harness. Der wichtigste Teil dieser Harness ist die Definition der Einstiegsfunktion von Frida's Stalker, die effektiv bestimmt, an welchem Punkt die Instrumentierung eingefügt wird. Im in-process-Modus ist dies einfach. Die Funktion wäre normalerweise diejenige, die bei jeder Fuzzing-Iteration aufgerufen wird. Es könnte jedoch auch eine andere sein.
Eine minimalistische Harness-Implementierung (im command-Modus) könnte wie folgt aussehen:
// Import the fuzzer base class
const Fuzzer = require("harness/fuzzer.js");
// The custom fuzzer needs to subclass the Fuzzer class to work properly
class TestFuzzer extends Fuzzer.Fuzzer {
constructor() {
// The constructor needs to specify the address of the targeted function and a NativeFunction
// object that can later be called by the fuzzer.
const FUZZ_FUNCTION_ADDR = Module.getExportByName(null, "FUZZ_FUNCTION");
const FUZZ_FUNCTION = new NativeFunction(
FUZZ_FUNCTION_ADDR,
"void", ["pointer", "int64"], {
});
super("test", FUZZ_FUNCTION_ADDR, FUZZ_FUNCTION);
}
}
const f = new TestFuzzer();
exports.fuzzer = f;
Diese Harness konfiguriert die Instrumentierung, um der Funktion FUZZ_FUNCTION zu folgen. Die Instrumentierung beginnt, wenn diese Funktion betreten wird, und endet, wenn die Funktion zurückkehrt. Diese Funktion sollte sorgfältig ausgewählt werden, da sie teuer ist und je mehr (potenziell unwichtige) Teile des Prozesses instrumentiert werden, desto langsamer wird der Fuzzer. Natürlich ist dies eine Abwägung zwischen Geschwindigkeit und angestrebter Coverage. Darüber hinaus unterstützt der Fuzzer derzeit nur Funktionen, die während einer Fuzzing-Iteration nur einmal betreten werden, d.h., die Funktion sollte während eines Fuzz-Falls nicht mehr als einmal aufgerufen werden, da sonst die Coverage-Informationen unzuverlässig werden könnten.
Wenn der in-process-Modus verwendet wird, ist eine weitere Funktion im Fuzzer-Skript erforderlich. Die fuzz-Methode. Sie wird bei jeder Iteration aufgerufen. Sie wird mit zwei Parametern aufgerufen: einem Zeiger auf einen Puffer und der Länge des Puffers. Unsere beispielhafte Zielfunktion nimmt zwei Parameter entgegen: einen Zeiger auf einen Puffer und seine Länge. Daher können wir einfach die Parameter übergeben, die wir in der fuzz-Methode erhalten.
fuzz(payload, len) {
this.target_function(payload, parseInt(len));
}