Для реверсера, пывнера и бинари ресерчера очень важно уметь фаззить, поэтому данные статьи посвященны новичкам, которые только начинают фаззить и знакомятся со зверьком - AFL++.

Фаззинг (fuzzing) — техника тестирования программного обеспечения, часто автоматическая или полуавтоматическая, заключающаяся в передаче приложению на вход неправильных, неожиданных или случайных данных. Предметом интереса являются падения и зависания, нарушения внутренней логики и проверок в коде приложения, утечки памяти, вызванные такими данными на входе. Фаззинг является разновидностью выборочного тестирования, часто используемого для проверки проблем безопасности в программном обеспечении и компьютерных системах. (Wikipedia)

Упражнение 1 - Xpdf

В этом упражнении мы проведем фаззинг просмотрщика Xpdf PDF. Цель - найти сбой для CVE-2019-13288 в XPDF 3.02.

Чему вы научитесь

После выполнения этого упражнения вы будете знать основы фаззинга с помощью AFL, такие как:

  1. Компиляция целевого приложения с инструментарием

  2. Запуск фаззера (afl-fuzz)

  3. Выявление сбоев с помощью отладчика (GDB)

Прочитать перед началом

  1. Я предлагаю вам попробовать решить упражнение самостоятельно, не проверяя решение. Старайтесь изо всех сил, и только если вы застряли, посмотрите пример решения ниже.

  2. AFL использует недетерминированный алгоритм тестирования, поэтому две сессии фаззинга никогда не будут одинаковыми. Вот почему я настоятельно рекомендую установить фиксированное зерно (-s 123). Таким образом, ваши результаты фаззинга будут похожи на те, что показаны здесь, и это позволит вам легче выполнять упражнения.

  3. Если вы обнаружили новую уязвимость, пожалуйста, отправьте отчет о безопасности в проект. Если вам нужна помощь или у вас есть сомнения по поводу процесса, 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++. Вы можете установить все двумя способами:

  1. Локальная установка (рекомендуемый вариант)

  2. 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

Краткое объяснение каждого флага:

  1. -i указывает каталог, куда мы должны поместить входные примеры (они же примеры файлов)

  2. -o указывает каталог, в котором AFL++ будет хранить мутировавшие файлы

  3. -s указывает статическое случайное зерно для использования

  4. @@ это командная строка цели, которую 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/ . Вы можете остановить фаззер после обнаружения первого сбоя, именно над ним мы и будем работать. В зависимости от производительности вашей машины может пройти до одного-двух часов, прежде чем вы получите сбой.

На этом этапе вы уже научились:

  1. Как скомпилировать цель с помощью компилятора afl с инструментарием

  2. Как запустить afl++

  3. Как обнаружить уникальные сбои вашей цели

И что дальше? У нас нет никакой информации об этой ошибке, только крах программы... Пришло время для отладки и устранения!

Задание

Для того чтобы выполнить это упражнение, вам необходимо:

  1. Воспроизвести сбой с помощью указанного файла

  2. Отладить сбой, чтобы найти проблему

  3. Устранить проблему

Решение

Воспроизводим сбой

Найдите файл, соответствующий краху, в файле, который находится по адресу $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, где ошибка уже исправлена, и проверить, что ошибка сегментации исчезла.

Комментарии (2)


  1. Johan_Palych
    07.11.2023 10:52
    +1

    Exercise 1 - Xpdf - устарело.
    https://github.com/antonio-morales/Fuzzing101/tree/main/Exercise 1

    Собирал по официальному доку на Debian 12 Bookworm
    https://aflplus.plus/docs/install/
    docker pull aflplusplus/aflplusplus на ubuntu 22.04
    https://github.com/AFLplusplus/AFLplusplus/blob/stable/Dockerfile
    Небольшая проблема с Python 3.11+ и решение:

    mkdir ~/.config/pip/
    cat >> ~/.config/pip/pip.conf << "EOF"
    [global]
    break-system-packages = true
    EOF

    Странный выбор древнего пакета для сборки. libt1-dev, libxp-dev и др. в современных версиях Debian и Ubuntu отсутствуют:

    grep -i -e "error" -e "warning" config.log
    conftest.c:66:10: fatal error: X11/extensions/Print.h: No such file or directory
    conftest.c:69:10: fatal error: t1lib.h: No such file or directory
    ---
    https://old-releases.ubuntu.com/ubuntu/pool/universe/x/xpdf/xpdf_3.02-1.2ubuntu1.1.dsc
    http://archive.debian.org/debian/pool/main/x/xpdf/xpdf_3.02-1.4+lenny4.dsc

    Для сборки и минимального функционирования:

    sudo apt-get install libfreetype6-dev libmotif-dev libxt-dev
     ./configure \
        --prefix="$HOME/fuzzing_xpdf/install" \
      # --enable-multithreaded \
        --with-freetype2-library=/usr/lib \
        --with-freetype2-includes=/usr/include/freetype2 \
      # --x-includes=/usr/include \
        --with-Xm-library=/usr/lib \
        --with-Xm-includes=/usr/include


    1. ap_security Автор
      07.11.2023 10:52
      +1

      Спасибо за ценное примечание!