Ten wpis jest częścią cyklu “Od zera do bughuntera” – listę wszystkich postów znajdziesz tutaj.
W listopadzie miną dwa lata od ostatniej aktualizacji pierwszego fuzzera opartego o badanie pokrycia kodu czyli AFL. W międzyczasie cały ekosystem skupiony wokół projektu rozrósł się do sporych rozmiarów, aczkolwiek rozwiązań dodających metody znacząco poprawiające pokrycie kodu jest niewiele. Takim rozwiązaniem jest QSYM, który umożliwia hybrydowy fuzzing: klasyczne podejście AFL oraz “drążenie” ścieżek w kodzie za pomocą symbolic execution.
WTF! Concolic? Symbolic?
Na początku warto stworzyć pewne “szufladki” ku owocnej pracy z artykułem i jednocześnie wprowadzić w temat mniej zaawansowanych Czytelników.
Zacznijmy od fuzzingu wykorzystującego concolic execution: ten rodzaj wykonania oparty jest o śledzenie ścieżki w kodzie uruchomionego przypadku testowego oraz gromadzenie informacji o rozgałęzieniach warunkowych w formie relacji symboli (ale nie tych osadzonych w binarce, dzięki którym możemy znaleźć nazwy funkcji 😉 ). Działanie w ten sposób umożliwia reprodukowalne przejście po ścieżce w kodzie, bo znamy wszystkie niezbędne warunki oraz wartości których należy unikać (podstawowe), aby wspomóc pokrycie kodu.
Symbolic execution nie wymaga nic na start, jest w stanie działać bez dostarczonego pliku (aczkolwiek mało efektywnie) a przede wszystkim “dzieje się statycznie” – binarka nie jest uruchamiana w środowisku systemowym, lecz na swój sposób silnik “tłumaczy” jej wykonanie na równania matematyczne, przy okazji je rozwiązując. Pomaga to przy dochodzeniu do nowych ścieżek w kodzie, znajdując wartości warunków jego rozgałęzień.
Łącząc obydwa podejścia wraz z AFL możemy zapewnić sobie lepsze code coverage a w konsekwencji jeszcze więcej znalezionych błędów!
Teoria, teorią… Czas na praktykę hybrid fuzzingu!
Niedługie (lekko ponad 20 minut) wprowadzenie do fuzzingu hybrydowego od twórców projektu QSYM w formie wideo (a dla entuzjastów czytania dokument):
Myślę, że po obejrzeniu Czytelniku rozumiesz dokładnie charakterystykę działania projektu i jego zalety, przejdźmy zatem do implementacji fuzzingu projektu Yara z jego udziałem 🙂
Kompilacja wygląda nieco inaczej niż w przypadku “czystego” AFL, wymagane jest odpalenie skryptu budującego całe środowisko (dla czytelności listingu pominąłem nieistotny output skryptów):
git clone https://github.com/sslab-gatech/qsym cd qsym/ echo 0|sudo tee /proc/sys/kernel/yama/ptrace_scope ./setup.sh # Instalacja biblioteki python virtualenv venv source venv/bin/activate pip install .
Szczerze mówiąc to połowa “narzutu” wdrożenia QSYM już za nami, czas na standardową kompilację Yary:
git clone https://github.com/VirusTotal/yara cd yara/ ./bootstrap.sh # afl-clang-fast ze zbudowanego wcześniej QSYMa env CC='afl-clang-fast' CXX='afl-clang-fast++' ./configure make -j sudo make install
Fuzzery uruchamiamy jak normalnego AFL, z tą różnicą, że po slave’ach wymagane jest odpalenie implementacji QSYM w pythonie:
# Pobranie mojego korpusu reguł Yara i lekkie porządki w folderze git clone https://github.com/fumfel/yara-fuzzing-corpus cd yara-fuzzing-corpus unzip yara_corpus_100616.zip rm yara_corpus_100616.zip rm README.md mv crashes/* . rm -rf crashes/ # Odpalenie fuzzera "master" afl-fuzz -M f01 -i yara-fuzzing-corpus/ -o yara_qsym_out -m none -- yara @@ /usr/bin/strings # Odpalenie fuzzera "slave" afl-fuzz -S f02 -i yara-fuzzing-corpus/ -o yara_qsym_out -m none -- yara @@ /usr/bin/strings # Odpalenie QSYM ./qsym/bin/run_qsym_afl.py -a ./yara_qsym_out/f02 -o yara_qsym_out -n qsym -- yara @@ /usr/bin/strings
Tyle! QSYM działa sobie w tle i czasami “szturcha” AFL nowymi przypadkami testowymi. Jak widać nie było to nic skomplikowanego 🙂