фаззинг часть 2

Фаззинг, часть 2 – Фаззинг с AFL

Как я писал в последняя глава, в этой статье я только объясню, как фаззить, когда есть доступ к исходному коду, используя AFL. Для демонстрации я взял старую программу с открытым исходным кодом, которую нашел на GitHub, под названием ccalc. Как следует из названия, это простой калькулятор, написанный на C. Я выбрал его, потому что вполне вероятно, что фаззинг легко найдет в нем много сбоев, и, как вы увидите, эта гипотеза оказалась верной.

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

Оригинальный main() выглядит примерно так (я немного сократил его):

Основная функция

Как правило, он содержит две ветви: он либо получает вход через аргк, аргв[]или через стандартный ввод (stdin). Я не уверен, почему, но создатель AFL явно рекомендует не прошерстить аргк, аргв[], как и все другие источники в Интернете. Поэтому я немного изменил его: сначала я удалил раздел, касающийся входных данных, поступающих из аргк, аргв, а затем я попытался оптимизировать его, насколько это возможно. В конце концов, каждый раз, когда фаззер пробует другой ввод, эта функция выполняется. Таким образом, малый может привести к серьезным замедлениям. Вот конечный результат:

Основная функция после модификации

Обратите внимание на цикл, который я добавил. Он указывает AFL использовать постоянный режим. В режиме AFL по умолчанию каждый раз, когда AFL запускает программы, он использует системный вызов fork() для создания нового подпроцесса. Этот системный вызов имеет серьезные накладные расходы, которые серьезно замедляют весь процесс фаззинга. Когда используется постоянный режим, фаззер снова и снова использует один и тот же процесс. Единственное требование — стирать все переменные и буферы при каждом запуске цикла.

Я назвал файл с изменённой функциейharge.c, а для того, чтобы компилировать программу с этим файлом, а не с main.c, я изменил форму файла automake:

automake файл перед изменением

в

automake файл после изменения

Пришло время фаззить программу. Я запустил automake, чтобы сгенерировать make-файлы, а затем сделать сам с помощью команды:

команда автоматического создания

Так что программа будет компилироваться с инструментами AFL.

Единственное, что осталось сделать сейчас, это создать начальную входную папку для AFL, которую я назвал «in», и выходную папку «out». Во входную папку помещаются простые текстовые файлы (без формата), которые содержат допустимый ввод, например «20/2» или «5*4». Поскольку мы хотим запустить пару процессов AFL (каждый процесс работает на своем собственном ядре ЦП), мы создаем отдельную входную папку для каждого процесса.

входная папка для каждого процесса

Для фаззинга достаточно выполнить команду:

Команда запуска AFL для первого использования

Для первого процесса вторым является

Команда запуска AFL для второго использования

И так далее.

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

Графический интерфейс прогресса AFL
Графический интерфейс прогресса AFL

После того, как будет найдено достаточное количество (точное количество зависит от вас) сбоев, обычно после длительного периода времени без нового уникального сбоя, вы можете завершить процесс. Для каждого запущенного вами процесса AFL в выходной папке будет создана отдельная папка:

Папки вывода

Внутри этих папок данные расположены следующим образом:

Содержимое выходной папки

Важно объяснить, какую информацию содержит каждый из них:

  • plot_data — представляет информацию в форме, способствующей созданию графика.
  • fuzzer_stats — статистика о процессе фаззинга.
  • fuzz_bitmap — растровое изображение, в котором каждый байт соответствует ветке программы.
    «AFL поддерживает «нечеткое растровое изображение», в котором каждый байт в растровом изображении представляет количество раз, когда была выбрана конкретная ветвь в нечеткой программе. AFL не выполняет сопоставление один к одному между конкретной ветвью и байтом в растровом изображении. Вместо этого встроенный инструментарий AFL помещает случайный двухбайтовый постоянный идентификатор в каждую ветвь. Всякий раз, когда выполнение достигает инструментальной ветки, AFL выполняет операцию XOR с идентификатором новой ветки и последним идентификатором ветки, увиденным перед достижением новой ветки. Это фиксирует как текущую ветвь, так и уникальный путь, по которому ее можно достичь (например, когда одна и та же функция вызывается из нескольких мест в коде). Затем AFL применяет функцию хеширования к значению XOR, чтобы определить, какая запись в растровом изображении представляет эту комбинацию ветвей. Всякий раз, когда выполняется конкретная комбинация переходов, соответствующий байт увеличивается в растровом изображении».
  • cmdline — команда, которая была передана в AFL. На практике название программы.
  • .cur_input — текущий ввод.
  • очередь — весь ввод, который был опробован до сих пор.
  • вылеты и зависания - результаты. Каждый файл в этих папках содержит входные данные, вызвавшие сбои или зависания. В названии каждого файла указан сигнал ядра о сбое (конечно, не имеет отношения к зависаниям), идентификатор ввода, использованного AFL для создания ввода, вызвавшего сбой, время, прошедшее с начала запуска AFL, и мутация, используемая для создания входных данных из его семени.
перейти к содержанию