Skip to main content

Command Palette

Search for a command to run...

Фаззинг cppcheck с помощью IJON+AFL

Updated
6 min read

Во время обучения на совместном курсе ФСТЭК России и ИСП РАН по фаззинг-тестированию одним из заданий было проведение фаззинга open-source программы с помощью связки фаззера AFL и механизма аннотаций IJON. Поскольку найти что-либо по данной теме в интернете было достаточно сложно, я решил разбавить тишину описанием своего решения.

Пост ориентирован на людей, похожих по уровню подготовки на меня сейчас - я понимаю, что такое фаззинг и зачем это нужно, но пока не могу сходу решать задачи по фаззингу чего-либо просто смотря на докерфайл и два скрипта по 40 команд. Мне бы хотелось прочитать более подробно описаный процесс фаззинга какого-нибудь ПО и поэтому я решил написать его сам.

Входные данные:

  1. Open-source программа на C/C++ входящая в top-200 (посмотреть его можно, например, здесь) - cppcheck.

  2. IJON - механизм создания аннотаций, которые помогают направлять фаззер AFL, который включен в репозиторий IJON.

  3. Задача - провести фаззинг-тестирование cppcheck, определив интересующие состояния программы и создав для этих состояний аннотации с помощью IJON.

В результате работы мы:

  • Сориентируемся, как работает cppcheck

  • Подготовим набор входных данных с помощью генератора программ на C

  • Соберем IJON и фаззер

  • Соберем cppcheck с инструментацией

  • Запустим фаззинг cppcheck с IJON в 4 потока

  • Посмотрим на результаты деятельности

Изучаем cppcheck

Для начала постараемся быстро понять, как работает попавшаяся нам программа - cppcheck. Создадим в домашней папке директорию kuzz (kuznetsov + fuzz, очень оригинально, да) и скопируем туда файлы cppcheck

ubuntu@ubuntu:~$ mkdir kuzz 
ubuntu@ubuntu:~$ cd kuzz ubuntu@ubuntu:~/kuzz$ git clone https://github.com/danmar/cppcheck.git --single-branch --depth=1 cppcheck_simple 
ubuntu@ubuntu:~/kuzz$ cd cppcheck_simple/

Собираем с помощью make.

ubuntu@ubuntu:~/kuzz$ make

Теперь читаем мануал и узнаем, как же работает программа. Узнаем, что на вход она получает файлы с кодом на C++ и потом их анализирует. Значит легитимными входными данными, которые мы будем подавать на вход фаззеру должны быть файлы с кодом на C++. Остается найти какое-то количество таких файлов.

Собираем входные данные

Для подготовки входных данных воспользуемся программой csmith, которая генерирует программы на C. Установим csmith, предварительно установив cmake и m4 согласно документации:

ubuntu@ubuntu:~/kuzz$ sudo apt install cmake m4
ubuntu@ubuntu:~/kuzz$ git clone https://github.com/csmith-project/csmith.git --single-branch --depth=1 
ubuntu@ubuntu:~/kuzz$ cd csmith/ 
ubuntu@ubuntu:~/kuzz/csmith$ cmake . 
ubuntu@ubuntu:~/kuzz/csmith$ make 
ubuntu@ubuntu:~/kuzz/csmith$ sudo make install

Теперь создадим папку "in" для входных данных, куда положим 10 сгенерированных csmith файлов:

ubuntu@ubuntu:~/kuzz/csmith$ cd .. 
ubuntu@ubuntu:~/kuzz$ mkdir in ubuntu@ubuntu:~/kuzz$ cd in 
ubuntu@ubuntu:~/kuzz/in$ for i in {1..10}; do csmith > random"${i}".cpp; done

Попробуем подать один из этих файлов на вход нашей cppcheck

ubuntu@ubuntu:~/kuzz$ cd ~/kuzz/cppcheck_simple/
ubuntu@ubuntu:~/kuzz/cppcheck_simple$ ./cppcheck ../in/random1.cpp

Как видим, cppcheck проверил наш файл и не обнаружил ошибок. Теперь нужно определить, какое состояние программы мы будем считать интересным настолько, чтобы написать к нему аннотацию для IJON.

Собираем IJON

IJON не обновлялся достаточно давно, поэтому могут быть различные проблемы, но в общем процесс в соответствии с документацией выглядит так (у меня, например, не было рекомендованного 6-го clang, поэтому пришлось его поставить).

ubuntu@ubuntu:~/kuzz$ git clone https://github.com/RUB-SysSec/ijon ubuntu@ubuntu:~/kuzz$ cd ijon/ 
ubuntu@ubuntu:~/kuzz/ijon$ make 
ubuntu@ubuntu:~/kuzz/ijon$ cd llvm_mode/ 
ubuntu@ubuntu:~/kuzz/ijon/llvm_mode$ sudo apt install clang-6.0 
ubuntu@ubuntu:~/kuzz/ijon/llvm_mode$ LLVM_CONFIG=llvm-config-6.0 CC=clang-6.0 make

Фаззер готов.

Пишем аннотацию под IJON

Для начала сделаем новую копию репозитория, с еще не собранными исходными кодами:

ubuntu@ubuntu:~/kuzz$ git clone https://github.com/danmar/cppcheck.git --single-branch --depth=1 
ubuntu@ubuntu:~/kuzz$ cd cppcheck/lib 
ubuntu@ubuntu:~/kuzz/cppcheck/lib$ ls

Как видно, cppcheck состоит из достаточно большого количества файлов. Быстро пробежавшись по ним, определяемся с тем, что представляется интересным. Поскольку cppcheck это по сути статический анализатор кода, его предназначение - находить ошибки. Поскольку мы хотим добиться покрытия большего количества кода cppcheck (одна из целей фаззинга изначально), то нам интересно, чтобы ошибки находились как можно чаще. В этом нам может помочь IJON, если мы установим аннотацию, которая будет направлять фаззер в сторону увеличения количества ошибок в генерируемых файлах. Механизм вывода уведомлений о найденных ошибках описан в файле errorlogger.cpp. Откроем его с помощью vim.

ubuntu@ubuntu:~/kuzz/cppcheck/lib$ vim errorlogger.cpp

Разные ошибки вызывают разные функции, но есть то, что их объединяет - вывод уведомления об ошибках setmsg.

Чем чаще вызывается вывод уведомления об ошибках, тем больше (логично) ошибок было найдено, а это как раз то, что нам и нужно. Аннотацию IJON можно было бы "прицепить" к существующей переменной, но поскольку таких нет, то придется сделать свою переменную, которая будет считать количество обращений к этой функции. По рекомендации со stackoverflow добавляем к этой функции статическую переменную, подсчитывающую количество обращений. А потом добавляем аннотацию IJON, которая будет направлять фаззер в сторону увеличения значения этой переменной.

Здесь следует сказать, что мы будем использовать именно максимизирующую аннотацию IJON_MAX(), о других аннотациях можно прочитать в README IJON, но стоит иметь ввиду, что аннотации IJON_ENABLE() и IJON_DISABLE() из коробки не работают (спасибо Дмитрию Пономареву за это замечание).

static unsigned int call_count = 0;
    call_count++;
IJON_MAX(call_count);

Собираем проект с инструментацией

Для того, чтобы собрать проект с инструментацией фаззера нужно проделать ту же команду make, только вместо стандартных компиляторов указать компиляторы, находящиеся в папке IJON.

ubuntu@ubuntu:~/kuzz/cppcheck/lib$ cd .. 
ubuntu@ubuntu:~/kuzz/cppcheck$ CC=/home/ubuntu/kuzz/ijon/afl-clang-fast  CXX=/home/ubuntu/kuzz/ijon/afl-clang-fast++ cmake .
ubuntu@ubuntu:~/kuzz/cppcheck$ CC=/home/ubuntu/kuzz/ijon/afl-clang-fast  CXX=/home/ubuntu/kuzz/ijon/afl-clang-fast++ make

Фаззинг

После того как завершена сборка с инструментацией можно наконец запустить фаззинг.

ubuntu@ubuntu:~/kuzz/cppcheck$ cd ../ijon/ 
ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -M fuzzer01 -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@

Появляется стандартная ошибка, в тексте которой сразу указано, что нужно исправить:

ubuntu@ubuntu:~/kuzz/ijon$ sudo su 
root@ubuntu:/home/ubuntu/kuzz/ijon# echo core >/proc/sys/kernel/core_pattern 
root@ubuntu:/home/ubuntu/kuzz/ijon# exit 
ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -M fuzzer01 -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@

Как мы могли заметить и ранее, cppcheck анализирует программы достаточно долго. Это усугубляется тем, что сгенерированные csmith тексты программ достаточно объемные, поэтому за установленную 1 секунду cppcheck может не успевать обработать входной сэмпл. Попробуем увеличить время обработки до 4 секунд и добавить знак "+", чтобы разрешить пропуск входных сэмплов по таймауту.

ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -M fuzzer01 -t 4000+ -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@

Как видим, часть тестов пропущена, но для нашей задачи это не принципиально. Теперь фаззинг запущен в одном потоке, откроем новые окна терминала и запустим в них параллельные потоки

**открываем новое окно терминала**
ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -S fuzzer02 -t 4000+ -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@ 
**открываем новое окно терминала**
ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -S fuzzer03 -t 4000+ -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@ 
**открываем новое окно терминала**
ubuntu@ubuntu:~/kuzz/ijon$ ./afl-fuzz -i /home/ubuntu/kuzz/in -o /home/ubuntu/kuzz/out -S fuzzer04 -t 4000+ -- /home/ubuntu/kuzz/cppcheck/bin/cppcheck @@

Поскольку cppcheck достаточно долго обрабатывает файлы, то количество выполнений не выглядит сильно ужасающим, хотя AFL и считает, что раз у нас меньше 100 запусков - мы slow. Посмотрим, получилось ли добиться каких-то результатов именно благодаря использованию IJON.

ubuntu@ubuntu:~/kuzz/ijon$ cd /home/ubuntu/kuzz/out/fuzzer01/ijon_max/ ubuntu@ubuntu:~/kuzz/out/fuzzer01/ijon_max$ ls

Как видим, папка ijon_max не пуста, следовательно есть результаты работы механизма аннотаций

Заключение

Выполнение задания было ограничено по времени, если бы его было больше, то можно было бы:

  1. Собрать покрытие кода (например с помощью llvm-cov), которое скорее всего было бы достаточно низким за то время, которое был запущен фаззер. Поскольку генерировать требуется программы на языке C с достаточно сложным синтаксисом, то для фаззинга нужны гораздо большие мощности и время.

  2. Определить другие состояния программы и установить аннотации IJON и на них тоже.

В целом использование аннотаций IJON кажется очень актуальным для фаззинга в ситуациях, когда нужно максимизировать какую-либо координату (это очень хорошо видно на примере фаззинга игр, но это скорее удобный способ демонстрации, чем реальное применение). Подробнее о возможных способах применения IJON можно прочитать в оригинальной научной публикации разработчика. Чтобы оценить, были ли аннотации полезны в данном задании требуется гораздо больше времени и мощностей для фаззинга.

Буду рад вашим комментариям и замечаниям к тому, что можно и нужно сделать лучше или по-другому. Отдельно хочется сказать большое спасибо коллективу ИСП РАН за проведение данного курса, команде Crusher и отдельно Виталию и Дмитрию за помощь и рекомендации по подготовке этого поста. Всем удачного фаззинга :)

More from this blog

Научная основа хорошего сна. Конспект мастеркласса Мэттью Уолкера

Мэттью Уолкер - ученый, занимающийся исследованием сна и его влияния на здоровье. У него достаточно много статей с хорошими показателями (Google Scholar), опубликованная научно-популярная книга "Зачем мы спим" (ЛитРес), серия подкастов и свой центр н...

Oct 1, 202410 min read
А

Александр Кузнецов

26 posts

Базальт СПО и МГТУ им. Н.Э. Баумана. Уведомления о новых постах — в telegram-канале.