
Для реверсера, пывнера и бинари ресерчера очень важно уметь фаззить, поэтому данные статьи посвященны новичкам, которые только начинают фаззить и знакомятся со зверьком - 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 <input-file-name> $HOME/fuzzing_xpdf/output для каждого отдельного входного файла.
Если вы получите сообщение типа "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/<your_filename>' $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/<your_filename> $HOME/fuzzing_xpdf/output
Теперь пишем внутри GDB run . Если все прошло успешно, вы должны увидеть следующий результат (или похожий):
Error: Missing 'endstream' Program received signal SIGSEGV, Segmentation fault. _int_malloc (av=av@entry=0x7ffff7bf1c60 <main_arena>, bytes=128) at ./malloc/malloc.c:3984
Затем введите bt, чтобы получить обратную трассировку (backtrace). Прокрутите стек вызовов и вы увидите множество вызовов метода "Parser::getObj", что, похоже, указывает на бесконечную рекурсию. Если вы перейдете на https://www.cvedetails.com/cve/CVE-2019-13288/, то увидите, что описание совпадает с бэктрейсом, который мы получили из GDB (в моем случае, другая уязвимость не та, которую предполагалось увидеть).
Фиксим
Последний шаг упражнения - исправление ошибки! Пересоберите цель после исправления и проверьте, что ваш сценарий использования больше не вызывает ошибку сегментации. Эта последняя часть оставлена в качестве упражнения для студента.
В качестве альтернативы вы можете загрузить Xpdf 4.02, где ошибка уже исправлена, и проверить, что ошибка сегментации исчезла.
