На Хабре уже пару раз появлялись статьи, поднимающие тему American Fuzzy Lop (AFL) (1,2). Но в данной статье речь пойдет не о классическом AFL, а о вспомогательных утилитах для него и его модификациях, которые, на наш взгляд, могут значительно улучшить качество фаззинга. Если вам интересно узнать, как можно прокачать AFL и искать быстрее и больше уязвимостей, то добро пожаловать под кат!
Что такое AFL и чем он так хорош
AFL — Coverage-guided fuzzer или Feedback-based fuzzer. Подробнее о данных концепциях можно узнать из такого классного пейпера, как «Fuzzing: Art, Science, and Engineering». Если обобщить информацию об AFL, то можно сказать следующее:
- Инструментирует исполняемый файл для сбора информации о покрытии
- Мутирует входные данные так, чтобы покрытие было максимальным
- Повторяет предыдущий шаг, чтобы найти падения программы
- На практике доказано, что очень эффективен
- Очень прост в использовании
- На практике доказано, что очень эффективен
Графически это можно представить следующим образом:
Если вы не в курсе что такое AFL, то для старта рекомендуем:
- Официальная страница проекта
- afl-training — краткий экскурс в AFL
- afl-demo — простая демонстрация того, как фаззить C++ программу, используя AFL
- afl-cve — Коллекция уязвимостей, обнаруженных с помощью AFL (не обновляется с 2017)
- О том, что AFL добавляет в программу в процессе его сборки, можно прочитать тут
- Несколько полезных советов для фаззинга сетевых приложений тут
На момент написания статьи последней версией AFL была версия 2.52b. Фаззер активно развивается, и со временем некоторые сторонние разработки включаются в основную ветку AFL и становятся сами по себе неактуальными. В настоящее время можно выделить несколько полезных вспомогательных инструментов — они перечислены в следующем разделе.
При этом некоторые пользователи AFL отмечают, что автор фазера Michal Zalewski походу забил на поддержку своего детища, так как последние изменения датируются 5 ноября 2017 года. Это предположительно связывают с его уходом из Google и новыми проектами. В связи с этим люди стали самостоятельно собирать и делать патчи последней текущей версии 2.52b.
Есть так же разные варианты и производные от AFL которые позволяют фаззить Python, Go, Rust, OCaml, GCJ Java, kernel syscalls, или даже целые VM.
— afl.rs — для фаззинга программы на Rust
— afl-fuzz-js — afl-fuzz для javascript.
— java-afl — AFL фаззинг для Java
— kelinci — еще один фазер для Java со статьей на эту тему
— javan-warty-pig — AFL-like фаззер для JVM.
— afl-swift — для фаззинга программы на swift
— ocamlopt-afl — для OCaml.
— sharpfuzz — основанный на afl для .net фаззер.
Вспомогательные инструменты
В данном разделе мы подобрали различные скрипты и инструменты для работы с AFL и разделили их на несколько категорий:
Обработка крешей
- afl-utils — набор утилит для автоматической обработки / анализа крешей и минимизации тесткейсов.
- afl-crash-analyzer — Еще один анализатор крешей для AFL.
- fuzzer-utils — набор скриптов для анализа результатов.
- atriage — простенький triage инструмент.
- afl-kit — Переписанная на питоне afl-сmin.
- AFLize — инструмент, который автоматически генерирует пригодные для afl билды дебиан пакетов.
- afl-fid — набор инструментов, для работы с входными данными.
Работа с покрытием кода
- afl-cov — предоставляет человекочитаемые данные о покрытии.
- count-afl-calls — оценка ратио. Скрипт считает число инструментированных блоков в бинаре.
- afl-sancov — как afl-cov, но использует clang санитайзер.
- covnavi — скрипт для покрытия кода и анализа от Cisco Talos Group.
- LAF LLVM Passes — что-то вроде коллекции патчей для afl, которые видоизменяют код так, чтобы фаззеру было проще пройти ветки
Несколько скриптов для минимизации test cases
- afl-pytmin — обертка для afl-tmin, которая пытается ускорить процесс минимизации тесткейса за счет использования множества CPU ядер.
- afl-ddmin-mod — вариация afl-tmin на базе ddmin алгоритма.
- halfempty — быстрая утилита, базирующаяся на паралелизации, для минимизации тесткейсов от Tavis Ormandy.
Для распределенного запуска
- disfuzz-afl — распределенный фаззинг для afl.
- AFLDFF — фреймворк для распределенного фаззинга с AFL.
- afl-launch — инструмент для запуска множества инстансов afl.
- afl-mothership — управление и запуск множества синхронизированных AFL фаззеров на AWS cloud.
- afl-in-the-cloud — еще один скрипт для запуска afl в AWS.
- VU_BSc_project — фаззинг-тестирование библиотек с открытым исходным кодом с libFuzzer и AFL.
Также совсем недавно по данной теме вышла очень хорошая статья “Scaling AFL to a 256 thread machine”, описывающая запуск AFL на 256 потоков.
Развертывание, управление, мониторинг, отчетность
- afl-other-arch — набор патчей и скриптов для простого добавления поддержки различных (не x86) архитектур в AFL.
- afl-trivia — несколько небольших скриптов для упрощения управления AFL.
- afl-monitor — скрипт для мониторинга работы AFL.
- afl-manager — веб сервер на python для управления multi-afl.
- afl-tools — образ докера с afl-latest, afl-dyninst и Triforce-afl.
- afl-remote — веб-сервер для удаленного управление инстансами afl.
Модификации AFL
AFL очень сильно повлиял на сообщество, занимающееся поиском уязвимостей, на само направление фаззинга. И не удивительно, что со временем на базе его идеи стали появляться различные модификации, вдохновленные оригинальным AFL. В этом разделе мы их и рассмотрим. У каждой из этих модификаций есть как свои преимущества, так и свои недостатки по сравнению с оригинальной версией AFL разных ситуациях.
Сразу скажем, что если есть проблемы с установкой или не хочется тратить время — почти любую модификацию можно найти на hub.docker.com
Зачем?
- Увеличить скорость работы и/или покрытие кода
- Алгоритмы
- Окружение
- ОС
- Железо
- Работать в условиях без исходного кода
- Эмуляция кода
- Инструментация кода
- Статическая
- Динамическая
Встроенные режимы работы AFL
Перед тем как перейти к рассмотрению различных модификаций и форков AFL необходимо рассказать о двух важных режимах, которые когда-то также были модификациями, а со временем стали встроенными режимами. Это Syzygy режим и режим Qemu.
Syzygy mode — является режимом работы в инструменте instrument.exe
instrument.exe --mode=afl --input-image=test.exe --output-image=test.instr.exe
Для данного режима необходимо: Statically rewrite PE32 binaries with AFL, необходимы символы, Requires additional dev to make WinAFL kernel aware.
Qemu mode — О том, как это работает под QEMU, можно посмотреть тут “Internals of AFL fuzzer — QEMU Instrumentation”. Поддержка работы с бинарями с помощью QEMU появилось в upstream AFL с версии Version 1.31b. Режим afl qemu mode работает с помощью добавленной функциональности инструментации бинарного кода в движок бинарной трансляции qemu tcg (tiny code generator). Для этого в afl есть скрипт сборки qemu, который выкачивает исходники определенной (2.10.0) версии qemu, накладывает на них несколько небольших патчей и собирает для заданной архитектуры. После этого сдается файл afl-qemu-trace, который является на самом деле файлом юзермодной (эмуляция только исполняемых файлов ELF) эмуляции qemu-. Благодаря этому можно использовать фаззинг с обратной связью на elf-бинарях, причем для кучи разных архитектур, поддерживаемых qemu. К тому же вы получаете все классные инструменты afl, начиная с удобного экрана с информацией о текущей сессии и заканчивая продвинутыми штуками, типа afl-analyze. Но надо помнить, что вы также получаете и ограничения qemu. Также, например, если файл собран toolchain, использующим аппаратные фичи SoC, на котором запускается бинарь и который не поддерживается qemu, фаззинг оборвется, как только встретится специфическая инструкция, или, например, будет использован специфический MMIO.
Есть еще и вот такой интересный форк qemu mode, где скорость увеличили в 3x-4x раза за счет инструментации TCG кода и кэширования.
Форки
Появление форков AFL в первую очередь связано с изменениями, улучшениями самих алгоритмов работы классического AFL.
- afl-cygwin — это попытка портировать классический AFL на Windows посредством Cygwin. К сожалению, данная попытка достаточно бажная, медленная и разработка, можно сказать, заброшена.
- AFLFast (extends AFL with Power Schedules) — один из первых форков AFL, были добавлены всякие эвристики, благодаря которым он мог проходить больше путей за короткий период.
- FairFuzz — расширение для AFL, цель которого пытаться больше уделять времени на более редкие ветви.
- AFLGo — расширение для AFL, которое в первую очередь предназначено для целенаправленного достижения определенных участков кода, а не общего покрытия кода программы. Это может быть использовано для тестирования патчей или новых добавленных участков кода.
- PerfFuzz — расширение для AFL, которое ищет тесткейсы, которые могли бы как можно сильнее замедлить программу.
- Pythia — расширение для AFL, которое призвано добавить элементы прогнозирования в процесс фазинга касаемо сложности обнаружения новых путей.
- Angora — один из самых последних релизнутых фаззеров, написан на rust. Использует свои новые стратегии для мутации и для увеличения покрытия.
- Neuzz — попытка фаззинга с использованием нейронных сетей.
- UnTracer-AFL — интеграция afl с UnTracer, для эффективной трассировки.
- Qsym — Practical Concolic Execution Engine Tailored для Hybrid Fuzzing. По сути, это движок символьного исполнения (основные компоненты реализованы в виде плагина к intel pin), который в сочетании с afl реализует hybrid fuzzing. Это является уже дальнейшей эволюцией в теме feedback based fuzzing и заслуживает отдельного разговора. Его главная заслуга в том, что он очень быстро (относительно остальных) может исполнять concolic execution. Это происходит за счет нативного исполнения команд без промежуточного представления кода, избавления от механизма снапшотов и еще ряда эвристик. Он использует старый Intel pin (из-за ряда проблем поддержки между libz3 и другими DBT), и в данный момент может работать с elf x86 и x86_64 архитектурами.
Стоит сказать, что есть большое количество академических работ, связанных с реализацией новых подходов, техник по фаззингу, где берется и модифицируется AFL. Кроме whitepaper больше ничего не доступно, поэтому такие реализации мы даже не стали упоминать. Если вам интересно, то их несложно нагуглить. Например, из последнего это CollAFL: Path Sensitive Fuzzing, EnFuzz, Smart Greybox Fuzzing, ML для afl.
Модификации на базе Qemu
- TriforceAFL — AFL/QEMU фаззинг с полной эмуляцией системы. Форк от nccgroup. Позволяет в qemu режиме фаззить операционную систему целиком. Реализован через специальную инструкцию (aflCall (0f 24)), которая была добавлена в QEMU x64 CPU. К сожалению, уже не поддерживается, последняя версия afl в нем 2.06b.
- TriforceLinuxSyscallFuzzer — фаззинг системных вызовов Linux.
- afl-qai — небольшой demo проект с QEMU Augmented Instrumentation (qai).
Модификация на базе KLEE
kleefl — для генерации тест кейсов средствами символьного исполнения (очень медленный на больших программах).
Модификации на базе Unicorn
afl-unicorn — позволяет фаззить куски кода, эмулируя его на Unicorn Engine. Данную вариацию AFL мы также успешно использовали в нашей практике, а именно на участках кода одной RTOS, которая выполнялась на SOC’е, и использовать QEMU mode было невозможно. Данную модификацию целесообразно использовать в том случае, когда исходников нет (нельзя собрать stand-alone бинарь для анализа парсера) и программа не принимает входные данные в прямом виде (например, зашифрованы или представляют собой семплы сигнала как в одном CGC бинаре), тогда можно её пореверсить и найти предполагаемые места-функции, где эти данные обрабатываются в удобном для фаззера формате и которые можно пофаззить. Это самая общая модификация AFL. В том смысле, что она позволяет фаззить буквально все. То есть не зависит от архитектуры, наличия исходников, формата входных данных и формата самого бинаря (самый яркий пример как раз bare-metal — просто куски кода из памяти контроллера). Исследователь предварительно исследует этот самый бинарь и пишет фаззер, который эмулирует состояние на входе у процедуры парсера, например. Видно, что в отличие от AFL нужно предварительно проделать какое-то исследование бинаря. Для bare-metal прошивок, типа Wi-Fi или baseband есть просто ряд недостатков, которые нужно иметь ввиду:
- Нужно как-то локализовать проверку контрольных сумм.
- Нужно иметь в виду, что состояние фаззера — это состояние памяти, которое было сохранено в дампе памяти, это может препятствовать достижению определенных путей для фаззера.
- Санитизация обращений к динамической памяти отсутствует, но ее можно реализовать вручную (тоже потратив усилия), и она будет зависеть от RTOS (тоже её надо предварительно исследовать).
- Не эмулируется межзадачное взаимодействие RTOS — тоже можно препятствовать нахождению определенных путей фаззером.
Пример работы с данной модификацией “afl-unicorn: Fuzzing Arbitrary Binary Code” и
“afl-unicorn: Part 2 — Fuzzing the ‘Unfuzzable’”.
Перед тем как мы перейдем к модификациям на основе фреймворков динамической бинарной инструментации (DBI), сразу напомним, что наибольшую скорость из этих фреймворков показывает DynamoRIO, потом DynInst и в конце уже PIN.
Модификации на базе PIN
- aflpin — AFL с инструментацией Intel PIN.
- afl_pin_mode — Еще одна AFL инструментация, реализованная через Intel PIN.
- afl-pin — AFL с PINtool.
- NaFl — A clone (of the basic core) of AFL fuzzer.
- PinAFL — автор инструмента попытался перенести AFL на Windows для фаззинга уже скомпилированных бинарей. Судя по всему, сделано было больше по фану за один вечер, и далее проект не развивается. Репозиторий не содержит исходников, только собранные бинарники и инструкцию для запуска. Версия AFL, на которой основан этот инструмент не указана, и поддерживает только 32-битные приложения.
Как вы видите, разных модификаций много, но толку от них в реальной жизни на практике не много.
Модификации на базе Dyninst
afl-dyninst — American Fuzzy Lop + Dyninst == AFL blackbox фаззинг. Фишка данной версии заключается в том, что первоначально исследуемая программа (без исходного кода) статически инструментируется (static binary instrumentation, static binary rewriting) с помощью DynInst, а затем фаззится классическим AFL, который думает, что программа собрана с помощью afl-gcc/afl-g++/afl-as ;) В итоге это дает нам возможность работы без исходного кода и с очень хорошей производительностью — It used to be at 0.25x speed compared to a native compile. При этом есть значительное преимущество перед QEMU, которое заключается в возможности инструментировать динамически слинкованные библиотеки. В то время, как QEMU способен только инструментировать основной исполняемый файл статически слинкованный с библиотеками. К сожалению, сейчас это актуально только для операционной системы Linux. Для поддержки Windows необходимы изменения в самом DynInst и там над этим идет работа.
Можно также обратить внимание еще на такой форк где его неплохо прокачали по различным возможностям (поддержка AARCH64 и PPC архитектур) и скорости ;)
Модификации на базе DynamoRIO
- drAFL — AFL + DynamoRIO = фаззинг без исходников на Linux.
- afl-dr — ещё одна реализация на базе DynamoRIO, которая очень подробно уже расписана на просторах Хабра.
- afl-dynamorio — модификация от vanhauser-thc (любителя прокачать и стабилизировать AFL). про данную верчию он говорит так: «run AFL with DynamoRIO when normal afl-dyninst is crashing the binary and qemu mode -Q is not an option». Из приятного тут добавлена поддержка ARM и AARCH64. Касаемо производительности: DynamoRIO примерно в ~10 медленнее чем Qemu, в ~25 медленнее чем dyninst — однако в ~10 быстрее чем Pintool.
- WinAFL — самый известный форк afl для Windows. (DynamoRIO, также есть syzygy mode). Появление этой модификации было только вопросом времени, так как желание опробовать AFL под Windows на приложениях, к которым нет исходников, появлялось у многих. В данный момент инструмент активно дорабатывается, и, несмотря на использование немного отстающей кодовой базы AFL, (2.43b на момент написания статьи) с его помощью уже нашли несколько уязвимостей (CVE-2016-7212, CVE-2017-0073, CVE-2017-0190, CVE-2017-11816). Следует отметить, что основными разработчиками являются специалисты из Google Zero Project team и MSRC Vulnerabilities and Mitigations Team, что дает основания надеяться на дальнейшее активное развитие проекта. Для реализации фаззера разработчики ушли от инструментации времени компиляции к использованию динамической инструментации (на базе DynamoRIO), что ожидаемо замедлило исполнение исследуемого софта, но полученный оверхед (двукратный) сравним с работой классического AFL в binary mode. Также разработчики решили вопрос долгого старта процесса, назвав это persistent fuzzing mode, они выбирают функцию, которую надо фаззить (по смещению внутри файла или по имени если функция представлена в таблице экспорта) и инструментируют ее таким образом, чтобы можно было вызывать ее в цикле, тем самым запуская несколько семплов входных данных без перезапуска процесса. Также недавно появилась интересная статья, в которой исследователи показали, как они нашли с помощью winafl ~50 уязвимостей за ~50 дней. При этом практически перед самой публикации статьи в WinAFL добавили еще и режим Intel PT (подробнее об этом чуть дальше) — детали тут.
Продвинутый/искушенный читатель может отметить, что есть модификации со всеми популярными фреймворками инструментации, за исключением Frida — действительно так оно и есть. Единственное упоминание об использование Frida с AFL нам встречалось только в работе «Chizpurfle: A Gray-Box Android Fuzzer for Vendor Service Customizations». Версия AFL с Frida была действительно полезна в виду того, что Frida хорошо поддерживает ряд RISC архитектур.
Многие исследователи также с нетерпением ждут релиза DBI фреймворка Scorpio от создателя Capstone, Unicorne, Keystone. На основе данного фреймворка сами авторы уже сделали фаззер (Darko) и, по их словам, успешно его используют для фаззинга embedded устройств. Подробнее с этим можно ознакомиться в работе «Digging Deep: Finding 0days in Embedded Systems with Code Coverage Guided Fuzzing».
Модификации, базирующиеся на аппаратных возможностях процессора
Когда речь заходит о модификациях AFL с поддержкой аппаратных возможностей процессора, то это в первую очередь говорит о возможности фаззить kernel код, и во вторую очередь — о более высокой скорости фаззинга для приложений без исходного кода.
И, конечно, в первую очередь мы говорим о такой аппаратной возможности процессора, как Intel PT (Processor Tracing). Которая доступна начиная с 6 поколения процессоров (это примерно с 2015). Естественно, для того, чтобы воспользоваться следующими фаззерами, вам понадобится железо с соответствующей поддержкой Intel PT.
- WinAFL-IntelPT — сторонняя модификация WinAFL в которой уже вместо DynamoRIO используется технология Intel PT.
- kAFL — академическая разработка, нацеленная на решение проблемы coverage-guided для фазинга ядра в OS независимой манере. Что решается с помощью использования гипервизора и Intel PT технологии. Подробнее можно узнать из их whitepaper «kAFL: Hardware-Assisted Feedback Fuzzing for OS Kernels».
Заключение
Как вы могли заметить, данная тема активно развивается. При этом тут есть большое пространство для творчества, чтобы создать новую, интересную и полезную модификацию AFL.
Спасибо за внимание и удачного фаззинга!
Соавтор: Никита Кныжов
P.S. Спасибо всей команде исследовательского центра за помощь в подготовке данного материала, без их опыта и помощи подготовить такое было бы невозможно.
Автор: d1g1