
Фаззинг встраиваемых систем с использованием аппаратных точек останова
Этот репозиторий содержит сопутствующий код для статьи: 'Fuzzing Embedded Systems using Debugger Interfaces'. Препринт статьи можно найти здесь https://publications.cispa.saarland/3950/. Код позволяет пользователям воспроизвести и расширить результаты, представленные в статье. Пожалуйста, цитируйте вышеуказанную статью при сообщении, воспроизведении или расширении результатов.
.
├── benchmark # Скрипты для сборки тестового набора фаззеров Google и запуска экспериментов
├── dependencies # Содержит Makefile для установки зависимостей GDBFuzz
├── evaluation # Необработанные данные экспериментов, представленные в статье
├── example_firmware # Примеры встроенных приложений, используемые для оценки
├── example_programs # Содержит скомпилированную примерную программу и конфиги для тестирования GDBFuzz
├── src # Содержит реализацию GDBFuzz
├── Dockerfile # Для создания Docker-образа со всеми установленными зависимостями GDBFuzz
├── LICENSE # Лицензия
├── Makefile # Makefile для создания docker-образа или локальной установки GDBFuzz
└── README.md # Этот файл README
Идея GDBFuzz заключается в использовании аппаратных точек останова от микроконтроллеров в качестве обратной связи для фаззинга, управляемого покрытием. Для этого GDB используется как универсальный интерфейс для обеспечения широкой применимости. Для бинарного анализа прошивки используется Ghidra. Код содержит настройки эталонного теста для оценки метода. Кроме того, включены примеры файлов прошивок.
GDBFuzz обеспечивает фаззинг, управляемый покрытием, для встраиваемых систем, но – в целях оценки – также может фаззить произвольные пользовательские приложения. Для фаззинга на микроконтроллерах мы рекомендуем локальную установку GDBFuzz, чтобы иметь возможность беспрепятственно отправлять фазз-данные на тестируемое устройство.
GDBFuzz протестирован на Ubuntu 20.04 LTS и Raspberry Pi OS (32-битная). Предварительные требования: java и python3. Сначала создайте новое виртуальное окружение и установите все зависимости.
virtualenv .venv
source .venv/bin/activate
make
chmod a+x ./src/GDBFuzz/main.py
GDBFuzz читает настройки из конфигурационного файла со следующими ключами.
[SUT]
# Путь к бинарному файлу SUT.
# Это может быть, например, файл .elf или .bin.
binary_file_path = <path>
# Адрес корневого узла CFG.
# Точки останова устанавливаются на узлы этого CFG.
# Например, 'LLVMFuzzerTestOneInput' или 'main'
entrypoint = <entrypoint>
# Количество входных данных, которые должны быть выполнены без попадания в точку останова, прежде чем
# точки останова будут повёрнуты.
until_rotate_breakpoints = <number>
# Максимальное количество точек останова, которое может быть установлено в любой момент времени.
max_breakpoints = <number>
# Функции, которые следует игнорировать (чёрный список).
# ignore_functions — это разделённый пробелами список имён функций, например 'malloc free'.
ignore_functions = <space separated list>
# Один из вариантов {Hardware, QEMU, SUTRunsOnHost}
# Hardware: Внешний компонент запускает gdb сервер, и GDBFuzz может подключиться к этому gdb серверу.
# QEMU: GDBFuzz запускает QEMU. QEMU эмулирует binary_file_path и запускает gdbserver.
# SUTRunsOnHost: GDBFuzz запускает целевую программу внутри GDB.
target_mode = <mode>
# Установите это значение в False, если вы хотите запустить ghidra, проанализировать SUT,
# и вручную запустить мост ghidra.
start_ghidra = True
# Разделённый пробелами список адресов, где установлены программные точки останова (для кода
# обработки ошибок). Выполнение по этим адресам считается сбоем.
# Пример: software_breakpoint_addresses = 0x123 0x432
software_breakpoint_addresses =
# Считаются ли все сработавшие программные точки останова сбоями
consider_sw_breakpoint_as_error = False
[SUTConnection]
# Класс 'SUT_connection_class' в файле 'SUT_connection_path' реализует
# способ отправки входных данных в SUT.
# Входные данные могут, например, отправляться по Wi-Fi, через последовательный порт, Bluetooth и т.д.
# Этот класс должен наследоваться от ./connections/SUTConnection.py.
# См. ./connections/SUTConnection.py для получения дополнительной информации.
SUT_connection_file = FIFOConnection.py
[GDB]
path_to_gdb = gdb-multiarch
# Запись в формате адрес:порт
gdb_server_address = localhost:4242
[Fuzzer]
# В байтах
maximum_input_length = 100000
# В секундах
single_run_timeout = 20
# В секундах
total_runtime = 3600
# Необязательно
# Путь к каталогу, где каждый файл содержит одно начальное значение (seed). Если вы не хотите
# использовать seeds, оставьте значение пустым.
seeds_directory =
[BreakpointStrategy]
# Стратегии выбора базовых блоков находятся в
# 'src/GDBFuzz/breakpoint_strategies/'
# Для статьи мы используем следующие стратегии
# 'RandomBasicBlockStrategy.py' - Случайный выбор недостигнутых базовых блоков
# 'RandomBasicBlockNoDomStrategy.py' - Как предыдущая, но не использует отношения доминирования для вывода транзитивно достигнутых узлов.
# 'RandomBasicBlockNoCorpusStrategy.py' - Как первая, но предотвращает рост корпуса входных данных и, следовательно, ведёт себя как фаззинг "чёрного ящика" с измерением покрытия.
# 'BlackboxStrategy.py' - Не устанавливает никаких точек останова
breakpoint_strategy_file = RandomBasicBlockStrategy.py
[Dependencies]
path_to_qemu = dependencies/qemu/build/x86_64-linux-user/qemu-x86_64
path_to_ghidra = dependencies/ghidra
[LogsAndVisualizations]
# Один из вариантов {DEBUG, INFO, WARNING, ERROR, CRITICAL}
loglevel = INFO
# Путь к каталогу, где будут храниться выходные файлы (например, графики, файлы журналов).
output_directory = ./output
# Если установлено в True, клиент MQTT отправляет элементы пользовательского интерфейса (например, графики)
enable_UI = False
Пример конфигурационного файла находится в ./example_programs/ вместе с примером программы, которая была скомпилирована с использованием нашей обвязки для фаззинга в benchmark/benchSUTs/GDBFuzz_wrapper/common/.
Запустите фаззинг на один час следующей командой.
chmod a+x ./example_programs/json-2017-02-12
./src/GDBFuzz/main.py --config ./example_programs/fuzz_json.cfg
Сначала мы видим вывод от Ghidra, анализирующего бинарный исполняемый файл, а затем сообщения о перемещении или срабатывании точек останова.
В зависимости от указанного output_directory в конфигурационном файле, теперь должна появиться папка trial-0 со следующей структурой
.
├── corpus # Папка, содержащая корпус входных данных.
├── crashes # Папка, содержащая сбойные входные данные (если есть).
├── cfg # Граф потока управления в виде списка смежности.
├── fuzzer_stats # Статистика кампании фаззинга.
├── plot_data # Таблица, показывающая, на каком относительном времени в кампании фаззинга был достигнут какой базовый блок.
├── reverse_cfg # Обратный граф потока управления.
Установив start_ghidra = False в конфигурационном файле, GDBFuzz подключается к экземпляру Ghidra, работающему в режиме GUI. Для этого плагин ghidra_bridge необходимо запустить вручную из менеджера скриптов. Во время фаззинга достигнутые программные блоки подсвечиваются зелёным цветом.