[Перевод] Фаззинг с AFL++. Знакомство
Для реверсера, пывнера и бинари ресерчера очень важно уметь фаззить, поэтому данные статьи посвященны новичкам, которые только начинают фаззить и знакомятся со зверьком — AFL++.
Фаззинг (fuzzing) — техника тестирования программного обеспечения, часто автоматическая или полуавтоматическая, заключающаяся в передаче приложению на вход неправильных, неожиданных или случайных данных. Предметом интереса являются падения и зависания, нарушения внутренней логики и проверок в коде приложения, утечки памяти, вызванные такими данными на входе. Фаззинг является разновидностью выборочного тестирования, часто используемого для проверки проблем безопасности в программном обеспечении и компьютерных системах. (Wikipedia)
Упражнение 1 — Xpdf
В этом упражнении мы проведем фаззинг просмотрщика Xpdf PDF. Цель — найти сбой для CVE-2019–13288 в XPDF 3.02.
Чему вы научитесь
После выполнения этого упражнения вы будете знать основы фаззинга с помощью AFL, такие как:
Компиляция целевого приложения с инструментарием
Запуск фаззера (afl-fuzz)
Выявление сбоев с помощью отладчика (GDB)
Прочитать перед началом
Я предлагаю вам попробовать решить упражнение самостоятельно, не проверяя решение. Старайтесь изо всех сил, и только если вы застряли, посмотрите пример решения ниже.
AFL использует недетерминированный алгоритм тестирования, поэтому две сессии фаззинга никогда не будут одинаковыми. Вот почему я настоятельно рекомендую установить фиксированное зерно (-s 123). Таким образом, ваши результаты фаззинга будут похожи на те, что показаны здесь, и это позволит вам легче выполнять упражнения.
Если вы обнаружили новую уязвимость, пожалуйста, отправьте отчет о безопасности в проект. Если вам нужна помощь или у вас есть сомнения по поводу процесса, GitHub Security Lab может помочь вам в этом :)
Окружение
Все упражнения были протестированы на Ubuntu 20.04.2 LTS. Я настоятельно рекомендую вам использовать ту же версию ОС, чтобы избежать различных результатов фаззинга, и запускать AFL++ на пустом оборудовании, а не на виртуальных машинах, для достижения наилучшей производительности.
Загрузите и создайте свою цель
Давайте сначала получим цель для фаззинга. Создайте новый каталог для проекта, который вы хотите запустить:
cd $HOME
mkdir fuzzing_xpdf && cd fuzzing_xpdf/
Чтобы ваша среда была полностью готова, вам может понадобиться установить некоторые дополнительные инструменты (а именно make и gcc):
sudo apt install build-essential
Качаем Xpdf 3.02:
wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
tar -xvzf xpdf-3.02.tar.gz
Собираем Xpdf:
cd xpdf-3.02
sudo apt update && sudo apt install -y build-essential gcc
./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
Пришло время протестировать сборку. Прежде всего, вам нужно загрузить несколько примеров в формате PDF:
cd $HOME/fuzzing_xpdf
mkdir pdf_examples && cd pdf_examples
wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
wget http://www.africau.edu/images/default/sample.pdf
wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
Теперь мы можем протестировать двоичный файл pdfinfo:
$HOME/fuzzing_xpdf/install/bin/pdfinfo -box -meta HOME/fuzzing_xpdf/pdf_examples/helloworld.pdf
Первая половина это запуск тулзы, -box означает печать в ограниченых рамках страницы, -meta печать метаданных документа (XML).
Вы должны увидеть что-то вроде этого:
Tagged: no
Pages: 1
Encrypted: no
Page size: 200 x 200 pts
MediaBox: 0.00 0.00 200.00 200.00
CropBox: 0.00 0.00 200.00 200.00
BleedBox: 0.00 0.00 200.00 200.00
TrimBox: 0.00 0.00 200.00 200.00
ArtBox: 0.00 0.00 200.00 200.00
File size: 678 bytes
Optimized: no
PDF version: 1.7
Установка AFL++
В этом курсе мы будем использовать последнюю версию фаззера AFL++. Вы можете установить все двумя способами:
Локальная установка (рекомендуемый вариант)
Docker-образ
Локальная установка
Установите зависимости:
sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools
sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-dev
Проверка и сборка AFL++ :
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"
make distrib
sudo make install
Docker-образ
Устанавливаем докер:
sudo apt install docker
Скачайте образ:
docker pull aflplusplus/aflplusplus
Запустите докер-контейнер AFLPlusPlus:
docker run -ti -v $HOME:/home aflplusplus/aflplusplus
а затем введите:
export $HOME="/home"
Теперь, если все прошло хорошо, вы сможете запустить afl-fuzz. Просто введите afl-fuzz
и вы должны увидеть что-то вроде этого:
afl-fuzz++4.06a based on afl by Michal Zalewski and a large online community
afl-fuzz [ options ] -- /path/to/fuzzed_app [ ... ]
Required parameters:
-i dir - input directory with test cases
-o dir - output directory for fuzzer findings
Execution control settings:
-p schedule - power schedules compute a seed's performance score:
fast(default), explore, exploit, seek, rare, mmopt, coe, lin
quad -- see docs/FAQ.md for more information
-f file - location read by the fuzzed program (default: stdin or @@)
-t msec - timeout for each run (auto-scaled, default 1000 ms). Add a '+'
to auto-calculate the timeout, the value being the maximum.
-m megs - memory limit for child process (0 MB, 0 = no limit [default])
-O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode)
-U - use unicorn-based instrumentation (Unicorn mode)
-W - use qemu-based instrumentation with Wine (Wine mode)
-X - use VM fuzzing (NYX mode - standalone mode)
-Y - use VM fuzzing (NYX mode - multiple instances mode)
Знакомьтесь, AFL++
AFL — это фаззер, ориентированный на покрытие, что означает, что он собирает информацию о покрытии для каждого измененного входа, чтобы обнаружить новые пути выполнения и потенциальные ошибки. При наличии исходного кода AFL может использовать инструментарий, вставляя вызовы функций в начало каждого базового блока (функции, циклы и т.д.).
Чтобы включить инструментарий для нашего целевого приложения, нам нужно скомпилировать код с помощью компиляторов AFL.
Прежде всего, мы очистим все ранее скомпилированные объектные файлы и исполняемые файлы:
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
А теперь мы собираем xpdf с помощью компилятора afl-clang-fast:
export LLVM_CONFIG="llvm-config-11"
CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++ ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
configure находится в папке xpdf и от туда надо запускать соответсвенно
Теперь вы можете запустить фаззер с помощью следующей команды:
afl-fuzz -i $HOME/fuzzing_xpdf/pdf_examples/ -o $HOME/fuzzing_xpdf/out/ -s 123 -- $HOME/fuzzing_xpdf/install/bin/pdftotext @@ $HOME/fuzzing_xpdf/output
Краткое объяснение каждого флага:
-i указывает каталог, куда мы должны поместить входные примеры (они же примеры файлов)
-o указывает каталог, в котором AFL++ будет хранить мутировавшие файлы
-s указывает статическое случайное зерно для использования
@@ это командная строка цели, которую AFL будет подставлять в каждое имя входного файла
Итак, в основном, фаззер будет запускаться командой $HOME/fuzzing_xpdf/install/bin/pdftotext
для каждого отдельного входного файла.
Если вы получите сообщение типа «Hmm, your system is configured to send core dump notifications to an external utility…», просто сделайте это:
sudo su
echo core >/proc/sys/kernel/core_pattern
exit
Если же возникла ошибка как у меня »Whoops, your system uses on-demand CPU frequency scaling, adjusted between 1558 and 2338 MHz. Unfortunately, the scaling algorithm in the kernel is imperfect and can miss the short-lived processes spawned by afl-fuzz. To keep things moving, run these commands as root: …» , то я решил эту проблему так:
export AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
export AFL_SKIP_CPUFREQ=1
Через несколько минут вы должны увидеть что-то вроде этого:
american fuzzy lop ++4.06a {default} (..._xpdf/install/bin/pdftotext) [fast]
┌─ process timing ────────────────────────────────────┬─ overall results
Вы можете увидеть значение «uniq. crashes» красным цветом, показывающее количество найденных уникальных аварий. Вы можете найти эти файлы аварий в папке $HOME/fuzzing_xpdf/out/
. Вы можете остановить фаззер после обнаружения первого сбоя, именно над ним мы и будем работать. В зависимости от производительности вашей машины может пройти до одного-двух часов, прежде чем вы получите сбой.
На этом этапе вы уже научились:
Как скомпилировать цель с помощью компилятора afl с инструментарием
Как запустить afl++
Как обнаружить уникальные сбои вашей цели
И что дальше? У нас нет никакой информации об этой ошибке, только крах программы… Пришло время для отладки и устранения!
Задание
Для того чтобы выполнить это упражнение, вам необходимо:
Воспроизвести сбой с помощью указанного файла
Отладить сбой, чтобы найти проблему
Устранить проблему
Решение
Воспроизводим сбой
Найдите файл, соответствующий краху, в файле, который находится по адресу $HOME/fuzzing_xpdf/out/
. Имя файла выглядит следующим образом id:000000,sig:11,src:001504+000002,time:924544,op:splice,rep:16
(ну или что-то в этом роде). Передайте этот файл в качестве входного файла pdftotext:
$HOME/fuzzing_xpdf/install/bin/pdftotext '$HOME/fuzzing_xpdf/out/default/crashes/' $HOME/fuzzing_xpdf/output
Это вызовет ошибку сегментации и приведет к аварийному завершению программы:
Error: PDF file is damaged - attempting to reconstruct xref table...
Error (417): Illegal character <9a> in hex string
Error (418): Illegal character <6d> in hex string
Error (419): Illegal character <25> in hex string
Error (420): Illegal character <96> in hex string
Error (421): Illegal character <9d> in hex string
Error (423): Illegal character <50> in hex string
Error (449): Illegal character <4e> in hex string
Error: Missing 'endstream'
zsh: segmentation fault
Triage
Используйте gdb для выяснения причин сбоя программы при таком вводе. Вы можете взглянуть на http://people.cs.pitt.edu/~mosse/gdb-note.html для хорошего краткого учебника по GDB. Прежде всего, вам нужно перестроить Xpdf с отладочной информацией, чтобы получить символическую трассировку стека:
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/xpdf-3.02/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_xpdf/install/"
make
make install
Сейчас можно запустить GDB:
gdb --args $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/ $HOME/fuzzing_xpdf/output
Теперь пишем внутри GDB run
. Если все прошло успешно, вы должны увидеть следующий результат (или похожий):
Error: Missing 'endstream'
Program received signal SIGSEGV, Segmentation fault.
_int_malloc (av=av@entry=0x7ffff7bf1c60 , bytes=128) at ./malloc/malloc.c:3984
Затем введите bt
, чтобы получить обратную трассировку (backtrace). Прокрутите стек вызовов и вы увидите множество вызовов метода «Parser: getObj», что, похоже, указывает на бесконечную рекурсию. Если вы перейдете на https://www.cvedetails.com/cve/CVE-2019–13288/, то увидите, что описание совпадает с бэктрейсом, который мы получили из GDB (в моем случае, другая уязвимость не та, которую предполагалось увидеть).
Фиксим
Последний шаг упражнения — исправление ошибки! Пересоберите цель после исправления и проверьте, что ваш сценарий использования больше не вызывает ошибку сегментации. Эта последняя часть оставлена в качестве упражнения для студента.
В качестве альтернативы вы можете загрузить Xpdf 4.02, где ошибка уже исправлена, и проверить, что ошибка сегментации исчезла.