
Fuzzer dirigé au niveau binaire, spécialisé dans la détection de vulnérabilités Use-After-Free via des métriques d'entrée sensibles à l'ordre et une analyse statique, permettant la reproduction de bugs, le test de correctifs et la vérification de rapports.
Le fuzzing dirigé en boîte grise (Directed Greybox Fuzzing, DGF) comme AFLGo a pour but d’effectuer des tests de contrainte sur des emplacements cibles potentiellement vulnérables présélectionnés, avec des applications dans différents contextes de sécurité : (1) reproduction de bugs, (2) test de correctifs ou (3) vérification de rapports d’analyse statique. Des travaux de recherche récents ont amélioré l’efficacité et l’efficience du fuzzing dirigé (voir awesome-directed-fuzzing).
Nous proposons UAFuzz, un fuzzer dirigé dédié aux bugs Use-After-Free (UAF) au niveau binaire en ajustant soigneusement les composants clés du fuzzing dirigé pour répondre aux caractéristiques spécifiques de cette classe de bugs. Les bugs UAF surviennent lorsqu’un élément du tas est utilisé après avoir été libéré. Détecter les bugs UAF est difficile : (1) complexité car une entrée de preuve de concept (PoC) doit déclencher une séquence de trois événements – allocation, libération et utilisation – sur le même emplacement mémoire, traversant plusieurs fonctions du programme testé, et (2) silence sans défaut de segmentation.
Globalement, UAFuzz suit un flux de travail similaire à celui des fuzzers dirigés, nos modifications étant surlignées en orange tout au long du processus de fuzzing, comme le montre la figure suivante. Comme nous nous concentrons sur (1) la reproduction de bugs et (2) le test de correctifs, il est plus probable que nous disposions de traces de pile (presque) complètes de tous les événements UAF liés à la mémoire. Contrairement aux approches dirigées générales existantes où les cibles peuvent être sélectionnées indépendamment, nous prenons en compte la relation entre les cibles (par exemple, l’ordre, essentiel pour les UAF) pour améliorer le caractère dirigé. Premièrement, le précalcul statique d’UAFuzz est rapide au niveau binaire. Deuxièmement, nous introduisons de nouvelles métriques d’entrée conscientes de l’ordre pour guider le fuzzer vers les cibles à l’exécution. Enfin, nous ne trions que les entrées potentielles couvrant toutes les cibles dans la trace attendue et nous pré-filtrons les entrées libres qui sont moins susceptibles de déclencher le bug.
Plus de détails dans notre article à RAID'20 et notre présentation à Black Hat USA'20. Merci également à Sébastien Bardin, Matthieu Lemerre, le Prof. Roland Groz et tout particulièrement Richard Bonichon (@rbonichon) pour son aide sur Ocaml.
Notre environnement de test est Ubuntu 16.04 64 bits.
# Installer Ocaml et les paquets prérequis pour BINSEC via 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`
# Installer les paquets Python (Python 2 pour les scripts IDA)
sudo python -m pip install networkx pydot
sudo apt install graphviz
# Installer 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
# Récupérer le code source
git clone https://github.com/strongcourage/uafuzz.git
# Variables d’environnement
export IDA_PATH = /path/to/ida-6.9/idaq
export GRAPH_EASY_PATH=/path/to/graph-easy
cd uafuzz; export UAFUZZ_PATH=`pwd`
# Compiler le code source
./scripts/build.sh uafuzz
# Aide pour l’interface IDA/UAFuzz
./binsec/src/binsec -ida-help
./binsec/src/binsec -uafuzz-help
Notre fuzzer est construit sur AFL v2.52b en mode QEMU pour le fuzzing et sur BINSEC pour l’analyse statique légère (voir uafuzz/README.md). Nous utilisons actuellement IDA Pro v6.9 pour extraire les graphes de flot de contrôle (CFG) et le graphe d’appels du binaire testé (voir ida/README.md).
uafuzz
├── binsec/src
│ └── ida : un plugin pour importer et traiter les CFG et le graphe d’appels d’IDA
│ └── uafuzz : code de fuzzing
│ │ └── afl-2.52b : noyau de fuzzing construit sur AFL-QEMU
│ │ └── uafuzz_*.ml(i) : un plugin pour calculer les informations statiques et communiquer avec AFL-QEMU
└── scripts : quelques scripts pour la construction et le tri des bugs
Considérons d’abord un bug UAF simple. AFL-QEMU et même le fuzzer dirigé AFLGo avec des cibles au niveau source ne peuvent pas détecter ce bug en 6 heures, alors qu’UAFuzz peut le détecter en quelques minutes à l’aide d’un rapport UAF de Valgrind.
# Exécuter AFL-QEMU
$UAFUZZ_PATH/tests/example.sh aflqemu 360
# Exécuter AFLGo avec des cibles au niveau source
$UAFUZZ_PATH/tests/example.sh aflgo 360
# Exécuter UAFuzz
$UAFUZZ_PATH/tests/example.sh uafuzz 360 $UAFUZZ_PATH/tests/example/example.valgrind
Pour les programmes réels, nous utilisons le UAF Fuzzing Benchmark pour nos évaluations.
# Récupérer le benchmark
git clone https://github.com/strongcourage/uafbench.git
cd uafbench; export UAFBENCH_PATH=`pwd`