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

Фаззинг, часть 1: теория

Fuzzing

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

Фаззинг особенно полезен в программах, написанных на низкоуровневых или более старых языках, таких как Basic, Assembly, C/C++ и т. д., которые не мешают программисту писать такие ошибки, как неправильное приведение типов, использование после освобождения [указателя], или переполнение памяти (буфер, стек, целое число), которые обычно не обязательно приводят к сбоям во время выполнения или ошибкам во время компиляции и могут быть использованы хитрым злоумышленником. Обычно, когда упоминается термин «фаззинг», он относится к этому типу фаззинга.

Типы фаззинга

Есть три подхода к фаззингу, которые мы должны перечислить.

Белый ящик Фаззинг

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

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

Фаззинг черного ящика

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

Фаззинг серого ящика

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

Техники фаззинга

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

Фаззинг на основе мутаций

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

Наиболее известным инструментом (и, возможно, самым известным фаззером в целом) является AFL, или американский пушистый лоп.

AFL использует три типа основных мутаций:

  • Удаление случайного бита.
  • Вставка случайного бита.
  • Переворот случайного бита (с 0 на 1 или наоборот)

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

Грамматический фаззинг

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

Далее – Часть 2

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

перейти к содержанию