Фаззинг все чаще применяют и программисты — для проверки своих приложений на прочность, и исследователи безопасности, и хакеры. Но пользоваться фаззерами не выйдет, если не понимаешь, что именно они делают. Зато, узнав это, ты освоишь современную технику, с помощью которой сейчас находят все новые и новые уязвимости в самых разных приложениях. В этой статье я расскажу, как работают разные виды фаззинга, и на примере WinAFL покажу, как пользоваться этим инструментом.

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

Так что же мы ищем при помощи фаззинга? Это достаточно широкий спектр ошибок программного обеспечения, который включает в себя такие вещи, как переполнение буферов, некорректная обработка пользовательских данных, разнообразные утечки ресурсов (в том числе при работе с памятью), различные ошибки синхронизации, приводящие к состоянию гонки, и прочее. Каждое такое событие фиксируется фаззером и более подробно исследуется в дальнейшем.

 

Техники

Существуют две основные техники фаззинга — это мутационное и порождающее тестирования. При мутационном тестировании генерация последовательностей происходит на основе заранее определенных данных и шаблонов. Именно они составляют стартовый корпус фаззера. Изменяя байт за байтом значения на входе и проверяя работу программы, фаззер может делать выводы об успешности тех или иных «мутаций», чтобы в следующем раунде сгенерировать более эффективные последовательности.

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

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

Конечно, существуют и более продвинутые техники. Например, фаззинг с использованием трассировки и построением уравнений для SMT-решателей. В теории это помогает покрывать даже труднодоступные ветки кода. При этом включается трасса внутри ядра ОС, с одновременным исключением известных участков (нет никакого смысла фаззить внутренности функций WinAPI и прочего). Однако заставить все правильно работать непросто, и сегодня это скорее «черная магия», чем распространенная практика.

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

INFO

Одним из первых прототипов фаззеров считается программа The Monkey, созданная в далеком 1983 году. В названии очевидна отсылка к теореме о бесконечных обезьянах, которые пытаются напечатать «Войну и мир». Несмотря на свою практическую бесполезность, теорема популярна в массовой культуре (например, упоминается в романе «Автостопом по галактике» и сериале «Симпсоны») и даже получила собственный RFC 2795.

 

Типы фаззеров

С техниками фаззинга более-менее разобрались, теперь перейдем к типам фаззеров.

Форматы файлов

Будем считать входными данными пользователя любой файл любого формата, который наше тестируемое приложение возьмется обработать. Это значит, что мы можем подсунуть файл «неправильного» формата и посмотреть, как справится с ним подопытная программа. Первое, что приходит в голову, — антивирус. Антивирусный сканер должен определять формат файла, как-то с ним взаимодействовать: пытаться распаковать, включить эвристический анализ и так далее.

Чем обернется простая проверка, если антивирусный сканер решит, что перед ним файл PE, упакованный UPX, а при распаковке выяснится, что это вовсе не UPX, а что-то, что лишь притворяется им? Естественно, алгоритм распаковки будет другой, но поведение сканера при этом предугадать сложно. Может быть, он обрушится. Может быть, просто повесит на файл флаг «поврежден» и пропустит. И это далеко не полный перечень возможных исходов. Фаззеры форматов файлов помогут протестировать подобные вещи.

Аргументы командной строки и переменные окружения

Зачастую утилитам требуются параметры командной строки: это может быть путь файла, аргумент выполнения, да много чего еще. Но что, если передать на вход нечто, чего программа совсем не ждет? Как самое простое, если программа просит указать какой-то путь, то вряд ли она всерьез рассчитывает, что путь будет состоять из тысячи символов. Вполне возможно, что передача такого аргумента «неподготовленному» приложению переполнит стек и вызовет обрушение.

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

Запросы IOCTL

Достаточно полезная штука, когда нужно посмотреть, как реагируют на запросы IOCTL различные драйверы режима ядра. Помимо устройств и периферии, драйверами зачастую пользуются некоторые программы для взаимодействия с системой. Конечно, структура IRP-запроса почти всегда неизвестна, но перехваченные пакеты можно использовать в качестве основы для корпуса.

Сетевые протоколы

Такие фаззеры бывают заточены под известные протоколы, но есть и всеядные экземпляры. Например, фаззер OWASP JBroFuzz тестирует реализации известных протоколов на предмет наличия таких уязвимостей, как межсайтовый скриптинг, переполнение буферов, SQL-инъекции и многое другое. С другой стороны, есть утилита SPIKE, которая может протестировать незнакомые протоколы на многие уязвимости.

Браузерные движки

Да, даже для поиска дыр в браузерах есть специальные фаззеры. На сегодняшний день современные браузеры очень сложны и содержат множество движков: они обрабатывают различные версии документов, протоколов, CSS, COM, DOM и многое другое. Так что участники различных bug bounty ищут дыры не только голыми руками. 🙂

Оперативная память

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

Продолжение доступно только подписчикам

Материалы из последних выпусков можно покупать отдельно только через два месяца после публикации. Чтобы продолжить чтение, необходимо купить подписку.

Подпишись на «Хакер» по выгодной цене!

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

2 комментария

  1. Аватар

    Oleg2

    23.05.2019 at 21:03

  2. Аватар

    slinkin

    31.05.2019 at 01:10

    Не совсем понятно как происходит инструментация в описанных тулзах. Например в libFuzzer-е мы компилируем наш код с clang (llvm) в следствии чего происходит инструментация. По libFuzzer-у кстати был отличный воркшоп у Мороза на ZN.

Оставить мнение

Check Also

Специалисты Netflix нашли несколько DoS-уязвимостей в Linux и FreeBSD

DoS-уязвимости обнаружены в TCP-стеках Linux и FreeBSD. эксплуатация багов может спровоцир…