
Fuzzer diretto a livello binario specializzato nel rilevamento di vulnerabilità Use-After-Free tramite metriche di input sensibili all'ordine e analisi statica, consentendo la riproduzione di bug, il test delle patch e la verifica dei report.
Il Fuzzing Greybox Diretto (DGF) come AFLGo mira a eseguire stress test su posizioni target potenzialmente vulnerabili preselezionate, con applicazioni a diversi contesti di sicurezza: (1) riproduzione di bug, (2) test di patch o (3) verifica di rapporti di analisi statica. Ci sono recentemente più lavori di ricerca che hanno migliorato l'efficacia e l'efficienza del fuzzing diretto (vedi awesome-directed-fuzzing).
Proponiamo UAFuzz, un fuzzer diretto dedicato ai bug Use-After-Free (UAF) a livello binario, ottimizzando attentamente i componenti chiave del fuzzing diretto per soddisfare le caratteristiche specifiche di questa classe di bug. I bug UAF si verificano quando un elemento dell'heap viene utilizzato dopo essere stato liberato. Rilevare i bug UAF è difficile: (1) complessità perché un input Proof-of-Concept (PoC) deve innescare una sequenza di tre eventi – alloc, free e use – sulla stessa posizione di memoria, coinvolgendo più funzioni del programma testato e (2) silenzio senza segmentation fault.
In generale, UAFuzz ha un flusso di lavoro simile a quello dei fuzzer diretti, con le nostre modifiche evidenziate in arancione lungo tutto il processo di fuzzing, come mostrato nella figura seguente. Poiché ci concentriamo su (1) riproduzione di bug e (2) test di patch applicazioni, è più probabile che abbiamo stack trace (per lo più) completi di tutti gli eventi UAF correlati alla memoria. A differenza degli approcci diretti generali esistenti in cui i target potrebbero essere selezionati indipendentemente, prendiamo in considerazione la relazione tra i target (ad esempio, l'ordinamento che è essenziale per gli UAF) per migliorare la direzionalità. In primo luogo, la precomputazione statica di UAFuzz è veloce a livello binario. In secondo luogo, introduciamo nuove metriche di input sensibili all'ordine per guidare il fuzzer verso i target in fase di esecuzione. Infine, triage solo gli input potenziali che coprono tutti i target nella traccia prevista e pre-filtriamo per i free input che hanno meno probabilità di innescare il bug.
Maggiori dettagli nel nostro articolo al RAID'20 e nella nostra presentazione al Black Hat USA'20. Grazie anche a Sébastien Bardin, Matthieu Lemerre, Prof. Roland Groz e in particolare a Richard Bonichon (@rbonichon) per il suo aiuto su Ocaml.
Il nostro ambiente testato è Ubuntu 16.04 64-bit.
# Installare Ocaml e i pacchetti prerequisiti per BINSEC tramite OPAM
sudo apt update
sudo apt update
sudo apt install ocaml ocaml-native-compilers camlp4-extra opam emacs llvm-6.0-dev pkg-config protobuf-compiler libgmp-dev libzmq3-dev cmake valgrind
opam init
opam switch 4.05.0
opam depext conf-m4.1
opam install merlin ocp-indent caml-mode tuareg menhir ocamlgraph ocamlfind piqi zmq.5.0.0 zarith llvm.6.0.0
eval `opam config env`
# Installare i pacchetti Python (Python 2 per gli script di IDA)
sudo python -m pip install networkx pydot
sudo apt install graphviz
# Installare Graph Easy
wget https://cpan.metacpan.org/authors/id/S/SH/SHLOMIF/Graph-Easy-0.76.tar.gz
tar xzf Graph-Easy-0.76.tar.gz
cd Graph-Easy-0.76
perl Makefile.PL; make test; sudo make install
export GRAPH_EASY_PATH=/usr/local/bin/graph-easy
# Ottenere il codice sorgente
git clone https://github.com/strongcourage/uafuzz.git
# Variabili d'ambiente
export IDA_PATH = /path/to/ida-6.9/idaq
export GRAPH_EASY_PATH=/path/to/graph-easy
cd uafuzz; export UAFUZZ_PATH=`pwd`
# Compilare il codice sorgente
./scripts/build.sh uafuzz
# Aiuto per l'interfaccia IDA/UAFuzz
./binsec/src/binsec -ida-help
./binsec/src/binsec -uafuzz-help
Il nostro fuzzer è costruito su AFL v2.52b in modalità QEMU per il fuzzing e BINSEC per l'analisi statica leggera (vedi uafuzz/README.md). Attualmente utilizziamo IDA Pro v6.9 per estrarre i grafi di controllo di flusso (CFG) e il call graph del binario testato (vedi ida/README.md).
uafuzz
├── binsec/src
│ └── ida: un plugin per importare ed elaborare i CFG e il call graph di IDA
│ └── uafuzz: codice di fuzzing
│ │ └── afl-2.52b: fuzzing core costruito su AFL-QEMU
│ │ └── uafuzz_*.ml(i): un plugin per calcolare informazioni statiche e comunicare con AFL-QEMU
└── scripts: alcuni script per la compilazione e il triage dei bug
Consideriamo prima un semplice bug UAF. Sia AFL-QEMU che il fuzzer diretto AFLGo con target a livello sorgente non riescono a rilevare questo bug entro 6 ore, mentre UAFuzz può rilevarlo in pochi minuti con l'aiuto di un rapporto UAF di Valgrind.
# Eseguire AFL-QEMU
$UAFUZZ_PATH/tests/example.sh aflqemu 360
# Eseguire AFLGo con target a livello sorgente
$UAFUZZ_PATH/tests/example.sh aflgo 360
# Eseguire UAFuzz
$UAFUZZ_PATH/tests/example.sh uafuzz 360 $UAFUZZ_PATH/tests/example/example.valgrind
Per programmi reali, utilizziamo il UAF Fuzzing Benchmark per le nostre valutazioni.
# Ottenere il benchmark
git clone https://github.com/strongcourage/uafbench.git
cd uafbench; export UAFBENCH_PATH=`pwd`