Package: bugbane
BugBaneНабор утилит для аудита безопасности приложений.Основные принципы и особенности:BugBane образует пайплайн безопасной разработки, этапы которого описаны в виде кода. Без BugBane пайплайн сильно фрагментирован: часть низкоуровневых инструкций описана в Makefile, часть - в shell-скриптах, что-то - в файлах используемого решения для CI/CD, а что-то - в Dockerfile. При подобной фрагментации гораздо проще допустить ошибки.BugBane - вариант стандартизации структуры рабочей директории, структуры и формата отчётных материалов, а также пайплайна безопасной разработки как последовательности определённых действий.BugBane - это набор инструментов, которые могут использоваться как совместно, так и по отдельности.BugBane позволяет выполнить тестирование и подготовить отчётные материалы: в процессе тестирования собираются свидетельства выполняемых операций в виде команд, журналов работы, скриншотов и отчётов. Все материалы соответствуют фактически выполненным действиям и запущенным командам, что защищает пользователя от ошибок при ручном вводе и сборе этих сведений.BugBane является решением, открытым для улучшений с учётом пожеланий сообщества.Возможности BugBane на текущий момент:Сборка целей для фаззинг-тестирования, в том числе с санитайзерами и сбором покрытия: AFL++, libFuzzer.Фаззинг сборок с использованиемAFL++,libFuzzer,dvyukov/go-fuzzна заданном количестве ядер до заданного условия остановки.Синхронизация (импорт и экспорт) тестовых примеров между рабочей директорией фаззера и хранилищем (папкой). Включает отсеивание дубликатов (для всех фаззеров) и минимизацию на основе инструментов фаззера (пока только AFL++).Сбор покрытия тестируемого приложения на семплах, полученных в процессе фаззинг-тестирования, а также генерация HTML-отчётов о покрытии (lcov, lcov-llvm, go-tool-cover).Воспроизведение падений и зависаний, обнаруженных фаззером. Извлечение места возникновения ошибки (имя функции, путь к файлу исходного кода, номер строки в файле).Отправка сведений о воспроизводимых багах в систему управления уязвимостями:Defect Dojo.Получение скриншотов работы фаззера (tmux + ansifilter + pango-view) и главной страницы отчёта о покрытии исходного кода (WeasyPrint, Selenium).Генерация отчётов на основе шаблонов Jinja2.УстановкаЗависимостиUNIX-подобная ОСPython >= 3.6Зависимости, используемые утилитами BugBane:bb-build: компиляторы используемого фаззера в PATH (afl-g++-fast, clang, ...).bb-corpus: утилита минимизации в соответствии с используемым фаззером в PATH (afl-cmin, ...).bb-fuzz: используемый фаззер в PATH (afl-fuzz, go-fuzz, ...).bb-coverage: используемые средства сбора покрытия в PATH (lcov, genhtml, go, ...).bb-reproduce: утилитаtimeout, отладчикgdb.bb-send: python-библиотекаdefectdojo_api.bb-screenshot,bb-report: python-библиотекиJinja2,WeasyPrint,Selenium, приложенияansifilterиpango-viewв PATH, приложениеgeckodriverв PATH и браузер Firefox (необязательно, только для Selenium), шрифтыmono(могут отсутствовать в базовых докер-образах).Примечания:Python-библиотеки устанавливается автоматически при выполнении дальнейших инструкций;в настоящий момент Selenium + geckodriver + Firefoxнеобходимоиспользовать только для отчётов о покрытии, построенных утилитойgo tool cover, для остальных отчётов достаточно использовать WeasyPrint; при этом скриншоты, сделанные с помощью Selenium, выглядят лучше. Недостаток: размер этих пакетов в некоторых дистрибутивах может занять ~700 мегабайт;для просмотра отчётов непосредственно в образе Docker с помощью утилит типа less может потребоваться установка локали с поддержкой UTF-8 и указание переменной LANG.Установка и удаление пакетаУстановить пакет можно локально с помощью pip:git clone https://github.com/gardatech/bugbane
cd bugbane
pip install .[all]Проверить выполнение тестов:pytestДоступна установка только необходимых Python-зависимостей:Группа pip installФаззинг*Заведение багов в Defect DojoОтчёты и скриншотыТестирование BugBaneРазработка BugBane-+----dd++---reporting+-+--test+--+-all++++-dev+++++* Выполнение сборок, фаззинг, работа с семплами, сбор покрытия и воспроизведение багов.Таким образом, можно разделить тестирование и работу с его результатами на разные хосты worker и reporter:pipinstall.# workerpipinstall.[dd,reporting]# reporterРезультат: на хосте worker не требуются зависимости для генерации отчётов, на хосте reporter не требуются окружение для запуска тестируемых приложений и фаззеров.Для удаления использовать следующую команду:pip uninstall bugbaneЗапускРекомендуется использовать BugBane в среде Docker.Подразумевается последовательный запуск инструментов в определённом порядке, например:bb-buildbb-corpus (import)bb-fuzzbb-coveragebb-reproducebb-corpus (export)bb-sendbb-reportПри этом этап №1 является опциональным (сборки могут быть выполнены другими способами), а этапы 7 и 8 могут выполняться в отдельном образе Docker или на отдельной машине.Большинство инструментов BugBane работают с конфигурационным файлом bugbane.json: получают входные переменные, обновляют их значения, добавляют новые переменные и дописывают в существующий файл конфигурации.Пример исходного файла конфигурации, достаточного для последовательного запуска всех инструментов BugBane:{"fuzzing":{"os_name":"Arch Linux","os_version":"Rolling","product_name":"RE2","product_version":"2022-02-01","module_name":"BugBane RE2 Example","application_name":"re2","is_library":true,"is_open_source":true,"language":["C++"],"parse_format":["RegExp"],"tested_source_file":"re2_fuzzer.cc","tested_source_function":"TestOneInput","build_cmd":"./build.sh","build_root":"./build","tested_binary_path":"$BUILD_ROOT/re2_fuzzer","sanitizers":["ASAN","UBSAN"],"builder_type":"AFL++LLVM","fuzzer_type":"AFL++","run_args":null,"run_env":null,"fuzz_cores":16}}bb-buildВыполняет сборку C/C++ приложения с использованием компиляторов фаззера.На вход приложению подаются:Исходный код, подлежащий сборкеФайл с переменными bugbane.jsonВ файле bugbane.json должны быть заданы переменные:builder_type,build_cmd,build_root,sanitizers.Команда, указанная в переменнойbuild_cmd, должна учитывать значения переменных окружения CC, CXX, CFLAGS, CXXFLAGS и при запуске выполнять сборку тестируемого компонента в режиме фаззинг-тестирования. После выполнения одного запуска командыbuild_cmdв папкеbuild_rootдолжна оказаться сборка тестируемого приложения. Переменнаяsanitizersдолжна содержать список санитайзеров, с которыми требуется выполнить сборки. Для каждого санитайзера будет выполнена отдельная сборка.Приложение последовательно выполняет несколько сборок (с различными санитайзерами + для сбора покрытия + дополнительные сборки для фаззинга) и после каждой сборки сохраняет результаты сборки из папкиbuild_rootв папку, указанную аргументом запуска-o. При этом обновляются некоторые переменные в файле bugbane.json (в частности,sanitizers- заполняется названиями санитайзеров, для которых удалось выполнить сборку).Пример скрипта, путь к которому может быть указан в команде сборкиbuild_cmd:#!/bin/bashexportCXX="${CXX:=afl-clang-fast++}"&&mkdir-pbuild&&makeclean&&make-jobj/libre2.a&&$CXX$CXXFLAGS--std=c++11-I.re2/fuzzing/re2_fuzzer.cc/AFLplusplus/libAFLDriver.aobj/libre2.a-lpthread-obuild/re2_fuzzerТаким образом флагами компиляции можно управлять извне и получать сборки с любыми санитайзерами, с инструментацией для сбора покрытия, с отладочной информацией и т.д.Пример запуска:bb-build-i/src-o/fuzzПри этом директория /src должна содержать файл bugbane.json.В результате в пути /fuzz появятся папки с полученными сборками, например: /fuzz/basic, /fuzz/asan, /fuzz/coverage. Также в папке /fuzz сохранится журнал выполнения всех сборок с указанием команд запуска и использованных переменных окружения.Соответствие сборок и папокИмя папкиОписаниеbuilder_typebasicСборка для фаззинга. Это должна быть наиболее производительная сборка: без санитайзеров, без покрытияAFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzergofuzzСборка для фаззинга с использованием dvyukov/go-fuzz (zip-архив). Не поддерживается bb-build, поддерживается остальными утилитами-lafСборка для фаззинга, скомпилированная с переменной окружения AFL_LLVM_LAF_ALLAFL++LLVM, AFL++LLVM-LTOcmplogСборка для фаззинга, скомпилированная с переменной окружения AFL_USE_CMPLOGAFL++LLVM, AFL++LLVM-LTOasanСборка для фаззинга с адресным санитайзером (Address Sanitizer)AFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzerubsanСборка для фаззинга с санитайзером неопределённого поведения (Undefined Behavior Sanitizer)AFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzercfisanСборка для фаззинга с санитайзером целостности потока выполнения (Control Flow Integrity Sanitizer)AFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzertsan *Сборка для фаззинга с санитайзером потоков (Thread Sanitizer)AFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzerlsan *Сборка для фаззинга с санитайзером утечек памяти (Leak Sanitizer). Этот функционал поддерживается адресным санитайзером, но также может использоваться отдельноAFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzermsan *Сборка для фаззинга с санитайзером памяти (Memory Sanitizer)AFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzercoverageСборка для получения информации о покрытииAFL++GCC, AFL++GCC-PLUGIN, AFL++LLVM, AFL++LLVM-LTO, libFuzzer* Работоспособность не тестировалась.Выполнение сборок без инструмента bb-buildВсе сборки рекомендуется выполнять компиляторами фаззера, в том числе сборку для получения информации о покрытии.Все сборки должны выполняться с отладочной информацией, содержащей сведения о строках исходного кода (-gдля gcc,-gили-gline-tables-only- для clang).Все сборки должны выполняться с флагом-fno-omit-frame-pointerдля получения более точных стеков вызовов в случае обнаружения багов или при отладке.Если фаззер поддерживает переменные окружения для включения санитайзеров (AFL_USE_ASAN и т.д.), то использование этих переменных предпочтительнее ручного указания флагов компиляции.Сборки следует размещать в папках под соответствующими названиями. Например, если фаззинг будет запущен из директории /fuzz, то сборка с ASAN должна быть сохранена в папке /fuzz/asan. Сборку, в которой одновременно присутствуют несколько санитайзеров, следует разместить в одном экземпляре в любой одной папке для сборки с санитайзером. То есть сборку с ASAN, UBSAN и CFISAN можно разместить в любой из папок: asan, ubsan, cfisan, lsan, tsan или msan - это не имеет значения.Если сборка coverage выполнялась компиляторами фаззера, то возможно использование сборки coverage для фаззинга в качестве сборки basic (для этого достаточно скопировать папку coverage в basic), но это неэффективно по скорости, а также создаёт дополнительную нагрузку на диск.bb-corpusСинхронизирует тестовые примеры в рабочей директории фаззера с хранилищем.Поддерживает импорт из хранилища в папку фаззера и экспорт из папки фаззера в хранилище.Инструмент не создаёт никаких подключений, не взаимодействует с какими-либо базами данных и не выполняет архивацию, вместо этого он работает с хранилищем как с простой папкой в файловой системе. В свою очередь хранилище может быть примонтированным каталогом Samba, NFS и т.д.Синхронизация происходит в два этапа:Копирование (в случае импорта) или перемещение (в случае экспорта) из папки-источника во временную папку без создания дубликатов по содержимому (проверка sha1).Минимизация семплов из временной папки в конечную папку с использованием инструмента фаззера (afl-cmin, ...).В конфигурационном файле bugbane.json должна быть объявлена переменнаяfuzzer_type.Для минимизации с использованием afl-cmin на диске должны присутствовать сборки тестируемого приложения. Наиболее предпочтительной сборкой для минимизации семплов является сборка в папке laf, т.к. она "различает" больше путей выполнения, но если она не будет обнаружена, для минимизации будут использованы другие сборки.Пример импорта тестовых примеров из хранилища (перед фаззинг-тестированием):bb-corpussuite/fuzzimport-from/storageПример экспорта тестовых примеров в хранилище (после фаззинг-тестирования):bb-corpussuite/fuzzexport-to/storageИмена результирующих файлов будут соответствовать хеш-сумме sha1 их содержимого.bb-fuzzЗапускает фаззинг сборок тестируемого приложения на указанном количестве ядер до наступления указанного условия остановки.bb-fuzz обнаруживает сборки на диске и распределяет их по разным ядрам процессора:сборкам с санитайзерами выделяется по одному ядру;вспомогательные сборки (AFL_LLVM_LAF_ALL, AFL_USE_CMPLOG) назначаются на некоторое процентное соотношение от указанного количества ядер;сборка basic (без санитайзеров) занимает остальные ядра;сборки для определения покрытия исходного кода в фаззинг-тестировании участие не принимают.В конфигурационном файле bugbane.json должны быть определены переменныеfuzzer_type,tested_binary_path,fuzz_cores,src_root,run_argsиrun_env.На диске должны присутствовать сборки приложения, размещённые в папках, соответствующих названию сборки, точно так же, как их размещает инструмент bb-build.Доступные значения переменнойfuzzer_type: AFL++, libFuzzer, go-fuzz.Переменнаяtested_binary_pathсодержит путь к тестируемому приложению относительноbuild_rootи относительно входной папки (где будет осуществлён поиск сборок).Переменнаяsrc_rootне используется напрямую, но без её указания потерпят неудачу утилиты, подлежащие запуску после bb-fuzz.run_args- аргументы запуска тестируемого приложения в режиме фаззинг-тестирования. Переменная может включать последовательность "@@", вместо которой фаззер подставит путь к файлу.run_env* - переменные окружения, которые необходимо установить для запуска тестируемого приложения (LD_PRELOAD и т.д.).* Пока не все инструменты используют эту переменную.Доступные условия остановки:реальная продолжительность фаззинга достигла X секунд (затраченное время независимо от количества ядер / экземпляров фаззера);суммарная продолжительность фаззинга достигла X секунд* (реальная продолжительность, умноженная на количество задействованных в тестировании ядер процессора);новые пути выполнения не обнаруживались в течение последних X секунд среди всех экземпляров фаззера.* Пока нет способа задать это условие.Условие остановки задаётся с помощью переменных окружения:CERT_FUZZ_DURATION=X - наивысший приоритет, если установлены другие переменные; X определяет количество секунд, в течение которых не должны обнаруживаться новые пути выполнения;CERT_FUZZ_LEVEL=X - средний приоритет; X определяет уровень контроля, что в свою очередь определяет время, в течение которого не должны обнаруживаться новые пути выполнения; допустимые значения X: 2, 3, 4.FUZZ_DURATION=X - наименьший приоритет; X определяет реальную продолжительность тестирования.Переменные CERT_FUZZ_* подходят для сертификационных испытаний, FUZZ_* - для использования в CI/CD.Если не объявлена ни одна из указанных переменных, используется FUZZ_DURATION=600.Количество ядер определяется минимальным значением среди перечисленных:Количество доступных в системе ядер.Значение переменнойfuzz_coresв файле конфигурации. Если значение не указано, будет выбрано 8 ядер.Аргумент запуска--max-cpus(значение по умолчанию: 16).Пример запуска:FUZZ_DURATION=1800bb-fuzz--suite/fuzzВ результате выполнения команды будет запущено несколько экземпляров фаззера в сессии tmux. Инструмент bb-fuzz будет периодически печатать статистику работы фаззера, пока не обнаружит наступление условия остановки, в данном случае, пока не накопится время работы 1800 секунд = 30 минут. Затем с использованием команд tmux capture-pane в папку /fuzz/screens будут сохранены дампы панелей tmux с возможно присутствующими ANSI-последовательностями (цвета, выделение текста жирным шрифтом и т.д.). Эти сохранённые дампы используются на слеующих этапах приложениями bb-report или bb-screenshot.Внимание: в настоящий момент после сохранения дампов завершаются ВСЕ процессы фаззера и tmux в пределах операционной системы.Примечание: в настоящий момент не поддерживается использование словарей для фаззинга.bb-coverageСобирает покрытие тестируемого приложения на семплах, сгенерированных фаззером:Запускает тестируемое приложение на семплах в директории синхронизации фаззера *Строит отчёт о покрытии* Для dvyukov/go-fuzz этап пропускается: подразумевается фаззинг с использованием ключа-dumpcover(bb-fuzz использует этот ключ).В конфигурационном файле bugbane.json должны быть объявлены переменныеtested_binary_path,run_args,run_env,coverage_type,fuzzer_type,fuzz_sync_dir,src_root.Переменнаяcoverage_typeзаполняется приложением bb-build и соответствует типу сборки.src_root- путь к исходному коду тестируемого приложения на момент выполнения сборок; не обязан реально существовать в файловой системе: если директория не существует, отчёт о покрытии будет содержать проценты, но не исходный код.Возможные значенияcoverage_typecoverage_typeОписаниеlcovДля целей, собранных компиляторами GCC с флагом--coveragelcov-llvmДля целей, собранных компиляторами LLVM с флагом--coveragego-tool-coverДля целей golangПример запуска:bb-coveragesuite/fuzzРезультат: в папке /fuzz/coverage_report должны появиться файлы отчёта о покрытии, в том числе /fuzz/coverage_report/index.html - главная страница отчёта о покрытии.bb-reproduceВоспроизводит баги и обобщает результаты работы фаззера:Получает общую статистику работы фаззеровМинимизирует падения и зависания путём их воспроизведения (получает информацию о типе ошибки и месте в коде: функция, файл, номер строки)Формирует JSON-файл с перечисленными выше сведениямиВ конфигурационном файле bugbane.json должны быть определены переменныеsrc_root,fuzz_sync_dir,fuzzer_type,reproduce_specs,run_args,run_env. Переменныеfuzz_sync_dirиreproduce_specsдобавляются инструментом bb-fuzz.fuzz_sync_dir- директория синхронизации фаззера; bb-fuzz использует директорию "out".src_root- путь к исходному коду тестируемого приложения на момент выполнения сборок; не обязан реально существовать в файловой системе, используется для более точного определения места падений/зависаний в исходном коде.reproduce_specs- словарь, определяющий тип фаззера, и задающий соответствие между сборками приложения и папками, на которых требуется выполнить воспроизведение:"fuzz_sync_dir":"/fuzz/out","reproduce_specs":{"AFL++":{"/fuzz/basic/re2_fuzzer":"re2_fuzzer2","/fuzz/ubsan/re2_fuzzer":"re2_fuzzer6"}}В данном случае сборка basic будет запущена на семплах/fuzz/out/re2_fuzzer2/{crashes,hangs}/id*, а сборка ubsan - на семплах/fuzz/out/re2_fuzzer6/{crashes,hangs}/id*.При каждом запуске анализируется вывод приложения в терминал, в том числе сообщения об ошибках от санитайзеров. Каждый баг воспроизводится до успешного воспроизведения, но не более N раз. Число N определяется аргументом запуска bb-fuzz--num-reruns, значение по умолчанию: 3. Если при воспроизведении падения не обнаруживается стек вызовов, приложение запускается под отладчиком gdb. Зависания воспроизводятся сразу под отладчиком gdb.Пример запуска:bb-reproduce--hang-timeout3000suite/fuzzВ результате формируется файл /fuzz/bb_results.json, содержащий статистику работы фаззера и сведения об обнаруженных и воспроизведённых багах, в том числе для каждого бага сохраняются: заголовок issue/бага, место возникновения бага в исходном коде, команда запуска с конкретным семплом, stdout+stderr приложения, переменные окружения.bb-sendОтправляет полученные скриптом bb-reproduce данные в формате JSON в систему управления уязвимостями Defect Dojo.Один запуск инструмента соответствует созданию одного теста в нужном engagement. В пределах этого теста создаются экземпляры finding на каждый уникальный баг.Здесь и далее в качестве адреса сервера Defect Dojo используетсяhttps://dojo.local:8080.Приложение bb-send не использует файл конфигурации BugBane. Входные данные берутся из файла bb_results.json.Пример запуска:bb-send --results-file bb_results.json --host https://dojo.local:8080 \
    --user-name ci_fuzz_user --user-id 2 --token TOKEN \
    --engagement 1 --test-type 141Описание некоторых аргументов запуска bb-send:--user-id: id указанного в--user-nameпользователя; можно посмотреть в адресной строке Defect Dojo, выбрав нужного пользователя на страницеhttps://dojo.local:8080/user.--engagement: engagement id; также можно посмотреть в адресной строке в браузере (выбрать нужный engagement на страницеhttps://dojo.local:8080/engagement).--test-type: id вида теста; брать также из адресной строки (выбрать нужный тест на страницеhttps://dojo.local:8080/test_type).--token: ключ API; берётся из Defect Dojo по ссылке:https://dojo.local:8080/api/key-v2(нужно быть авторизованным от имени, указанного в--user-name, ключ нужен из раздела "Your current API key is ....").Если подлинность сертификата сервера Defect Dojo не может быть проверена, то следует добавить аргумент запуска--no-sslи использовать http вместо https.bb-reportСоздаёт отчёт в формате md на основе указанного Jinja2-шаблона. По умолчанию используется шаблон, подобный протоколу сертификационных испытаний.Создаёт скриншоты экранов фаззера (из дампов tmux, сохранённых ранее на этапе фаззинг-тестирования) и главной страницы HTML-отчёта о покрытии кода. Скриншоты сохраняются в папку screenshots и вставляются в отчёт в виде ссылок.В файле конфигурации bugbane.json должны быть объявлены переменныеfuzzer_type,coverage_typeиfuzz_sync_dir.Пример запуска:bb-report--namefuzzing_re2suite/fuzzЗапуск с использованием Selenum:bb-report--html-screenerselenium--namefuzzing_re2suite/fuzzРезультат: в папке /fuzz появится папка screenshots и файл с отчётом fuzzing_re2.md.bb-screenshotУтилита для ручного создания скриншотов. Скриншоты создаются так же, как в приложении bb-report, но пользователь может указать имена входного и выходного файлов.Примеры запуска:bb-screenshot-Spango-itmux_dump.txt-otmux_screenshot.png
bb-screenshot-Sweasyprint-iindex.html-ocoverage.png
bb-screenshot-Sselenium-iindex.html-ocoverage2.pngРазвитиеПланы по улучшению BugBane:Поддержка работы со словарямиПоддержка тестирования разных целей в пределах одной сборкиПоддержка других фаззеровДобавление других утилитГенерация отчётов в других форматах и по другим шаблонамДля разработчиковУстановка в режиме editable в виртуальное окружение:python -m venv .venv
. .venv/bin/activate
pip install -e .[dev]Запуск тестов:pytestЗапуск тестов в среде tox (при этом собирается покрытие кода тестами):toxБлагодарностиСпасибо всем участникам проекта!Отдельные благодарности:Илья Уразбахтин: идеи, консультации, менторство.