
Suite di fuzzing in-process basata su Frida con proxy AFL++, modalità attiva/passiva standalone e comunicazione tramite memoria condivisa per la scoperta di vulnerabilità basata sulla copertura ad alte prestazioni su più piattaforme.
fpicker è una suite di fuzzing basata su Frida che offre una varietà di modalità di fuzzing per fuzzing in-process, come una modalità AFL++ o una modalità di tracciamento passivo. Dovrebbe funzionare su tutte le piattaforme supportate da Frida.
Alcune informazioni di base e i pensieri e le idee dietro fpicker si trovano in un post sul blog che ho scritto.
Fpicker si basa su precedenti sforzi su ToothPicker, sviluppato durante la mia tesi di laurea magistrale. La maggior parte di fpicker è stata sviluppata durante l'orario di lavoro presso il mio datore di lavoro (ERNW).
Requisiti per eseguire fpicker:
frida-core-devkit per la rispettiva piattaforma disponibile presso Frida releases su GitHub.
libfrida-core-ios.a, libfrida-core-macos.a o libfrida-core-linux.a.frida-core.h). Salvarli come frida-core-linux.h o frida-core-ios.h a seconda della piattaforma.update_frida_version.sh prima della compilazione.Requisiti solo quando si esegue in modalità AFL++:
CFLAGS="-DUSEMMAP=1".CFLAGS="-DUSEMMAP=1 -DTARGET_OS_IOS".Fpicker può essere compilato per macOS, iOS o Linux. Il Makefile attualmente supporta solo la compilazione per iOS su macOS, ma dovrebbe essere del tutto possibile compilare fpicker utilizzando un toolchain iOS su Linux.
A seconda del target desiderato, eseguire:
make fpicker-macos
make fpicker-ios
make fpicker-linux
per compilare fpicker.
Una volta compilato fpicker, è necessario compilare l'harness di fuzzing:
Vedere la cartella examples per diversi casi di fuzzing di esempio. L'approccio generale è il seguente:
examples/test/test.js) (vedere qui per maggiori informazioni sugli harness)frida-compile test.js -o harness.jsOra fpicker può iniziare il fuzzing. Il comando esatto dipende fortemente dalla configurazione e dall'impostazione. Di seguito vengono forniti alcuni casi di esempio. Questi corrispondono principalmente agli esempi nella cartella examples.
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"
Ogni target richiede il proprio harness di fuzzing. La parte più importante di questo harness è definire la funzione di ingresso di Frida's Stalker, che determina efficacemente a quale punto viene inserita l'instrumentazione. Nella modalità in-process questo è semplice. La funzione sarebbe solitamente quella che viene chiamata ad ogni iterazione di fuzzing. Tuttavia, potrebbe anche essere una diversa.
Un'implementazione di harness minimalista (in modalità command) potrebbe essere questa:
// 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;
Questo harness configura l'instrumentazione per seguire la funzione FUZZ_FUNCTION. L'instrumentazione inizierà quando questa funzione viene inserita e si ferma quando la funzione ritorna. Questa funzione dovrebbe essere scelta attentamente poiché è costosa e più parti (potenzialmente non importanti) del processo vengono instrumentate, più il fuzzer diventa lento. Ovviamente, è una considerazione tra velocità e copertura desiderata. Inoltre, il fuzzer attualmente supporta solo funzioni che vengono inserite una sola volta durante un'iterazione di fuzzing, cioè, la funzione non dovrebbe essere chiamata più di una volta durante un caso di fuzz, altrimenti le informazioni di copertura potrebbero diventare inaffidabili.
Quando si utilizza la modalità in-process, è richiesta un'altra funzione nello script del fuzzer. Il metodo fuzz. Verrà chiamato ad ogni iterazione. Verrà chiamato con due parametri, un puntatore a un buffer e la lunghezza del buffer. La nostra funzione target esemplificativa prende due parametri, un puntatore a un buffer e la sua lunghezza. Quindi, possiamo semplicemente passare i parametri che stiamo ottenendo nel metodo fuzz.
fuzz(payload, len) {
this.target_function(payload, parseInt(len));
}