Фаззеры безопасности
Если вы делаете что-то важное с точки зрения безопасности с помощью C и C ++, вы, должно быть, слышали об American Fuzzy Lop, самом известном из фаззеров. Он использует заданный набор данных для подпитки вашей программы, немного изменяет его и видит, какие мутации приводят к другому пути кода. Учитывая, что в вашей программе скомпилированы утверждения, или если вы используете AddressSanitizer (-fsanitize=address
), эти мутации, как правило, быстро приводят к обнаружению уязвимого места в вашем коде.
Что ж, проект LLVM разрабатывает свою собственную, немного менее функциональную, но предположительно более быструю систему фаззинга, LibFuzzer. Предпосылка та же: вы компилируете свой код с -fsanitizer=fuzzer
, и он генерирует исполняемый файл, который, учитывая корпус, многократно вызывает вашу функцию int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
, каким-то образом приводя ее к сбою. Кроме того, он делает это быстрее, поскольку, в отличие от AFL, выполняет фаззинг входных данных в рамках одного процесса. Это может пригодиться, если вы пишете всевозможные парсеры, которые не тратят много времени на обработку для завершения одного цикла обработки входных данных.
Проект LibFuzzer развивается довольно быстро, поэтому старые инструкции не работают. Например, ожидается, что поддержка среды выполнения для LibFuzzer будет упакована как libFuzzer.a, libLLVMFuzzer.a, libcompiler-rt.a и некоторые другие имена, в зависимости от версии clang.
Компиляция нового clang + libFuzzer с нуля
Наиболее разумным способом использования фаззера LLVM является создание проекта LLVM / Clang и его поддержки во время выполнения с нуля. Ниже приведена автоматическая процедура, которая работала для меня на последней версии macOS Sierra на момент публикации.
- Ожидается, что Homebrew установлен, так как ему необходимо установить или обновить cmake и ninja.
- Он использует ниндзя для управления параллельной компиляцией, потому что он строится быстрее и обеспечивает несколько лучшую индикацию прогресса.
Вот сценарий для создания недавнего лязга с поддержкой фаззера:
#!/usr/bin/env bash set -x set -e INSTALL_PREFIX=/usr/local/clang-devel mkdir -p clang-src cd clang-src function brew_install() { local pkgname=$1 echo Ensuring $pkgname is installed test -f .has-$pkgname && return brew install $pkgname || brew update $pkgname touch .has-$pkgname } brew_install ninja # Faster make replacement brew_install cmake CLONE_DEPTH="--depth 1" echo Cloning LLVM/Clang sources test ! -d llvm && git clone ${CLONE_DEPTH} http://llvm.org/git/llvm.git test ! -d llvm/tools/clang && \ (cd llvm/tools && git clone ${CLONE_DEPTH} http://llvm.org/git/clang.git) test ! -d llvm/projects/libcxx && \ (cd llvm/projects && git clone ${CLONE_DEPTH} http://llvm.org/git/libcxx.git) test ! -d compiler-rt && \ (git clone ${CLONE_DEPTH} http://llvm.org/git/compiler-rt.git) mkdir -p build cd build echo Configuring LLVM/CLang for ninja cmake -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \ -DLLVM_TARGETS_TO_BUILD=X86 \ --build ../llvm echo Building LLVM/Clang ninja cd ../compiler-rt # Suppress x86_64h arch sed -i '' -e 's/set(X86_64 x86_64 x86_64h)/set(X86_64 x86_64)/' cmake/config-ix.cmake sed -i '' -e 's/set(X86_64 x86_64 x86_64h)/set(X86_64 x86_64)/' cmake/builtin-config-ix.cmake sed -i '' -e 's/set(DARWIN_osx_ARCHS i386 x86_64 x86_64h)/set(DARWIN_osx_ARCHS i386 x86_64)/' cmake/builtin-config-ix.cmake HOST_TARGET=$(../build/bin/llvm-config --host-target) echo Configuring compiler-rt which contains fuzzer runtime. PATH=../build/bin:$PATH cmake -G Ninja \ -DLLVM_CONFIG_PATH=../build/bin/llvm-config \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER_TARGET=${HOST_TARGET} \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_FLAGS=-I$(pwd)/../build/include \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_INSTALL_PREFIX=/usr/local/clang-devel/lib/clang/6.0.0 \ -DCOMPILER_RT_ENABLE_IOS=OFF \ -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ --build . # Some weird recursive dependency sed -i '' -E 's/CUSTOM_COMMAND incl.*\.h$/CUSTOM_COMMAND/' build.ninja echo Building compiler-rt ninja cd .. echo "Install clang into ${INSTALL_PREFIX}? Enter \"yes\":" if read install && test "$install" == yes; then (cd build && sudo ninja install) (cd compiler-rt && sudo ninja install) else echo "Not installing. Execute:" echo " (cd clang-src/build && ninja install)" echo " (cd clang-src/compiler-rt && ninja install)" echo "to install into ${INSTALL_PREFIX}" fi
Компиляция и завершение установки в /usr/local/bin/clang-devel
может занять пару часов. Убедитесь, что он у вас на пути, если вы хотите использовать его сразу:
t='export PATH=/usr/local/clang-devel/bin:$PATH' eval "$t" echo "$t" >> ~/.bash_profile
Пример фаззинга
Теперь давайте представим короткий, но работающий пример неработающей программы для тестирования нашего недавно скомпилированного инструментария фаззинга на:
#include <string.h> int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { /* Whatever broken logic you want to execute on Data... */ return Data && strlen((const char *)Data) == Size; }
Скомпилируйте и запустите это прямо до сбоя:
/usr/local/clang-devel/bin/clang -o fuzz fuzz.c \ -fsanitize=undefined,address,fuzzer ./fuzz
Где undefined
и address
- это формы дезинфекции инструментов (чтобы выяснить, если что-то не так, именно тогда, когда это происходит, а не когда происходит сбой). И fuzzer
здесь главный гость, сама аппаратура фаззера. Вы можете удалить один или оба undefined
и address
дезинфицирующих средств или даже заменить их на memory
(который обычно несовместим с address
дезинфицирующим средством).
Результирующий сбой будет выглядеть примерно так:
Неурегулированные проблемы
Хотя этот рецепт, похоже, работает, я все равно считаю его довольно неприятным решением:
- Бинарный файл
clang-6.0
отлично работает с-fsanitize=fuzzer
, тогда какclang
иclang++
, несмотря на то, что они являются символическими ссылками, не поддерживают эту опцию. Они просто терпят неудачу, как если бы эта опция игнорировалась. Я пока не мог понять, почему. Полный путь работает для/usr/local/clang-devel/bin/clang
, но не для…clang++
. - Полезное дезинфицирующее средство памяти (
-fsanitize=memory
) не работает с этим рецептом. - Мне не удалось превзойти библиотеку
compiler-rt
, чтобы скомпилировать ее обычным способом, когда ожидается, что она будет находиться в каталогеclang-src/llvm/projects
. Поэтому мне пришлось переместить его и скомпилировать автономно.
Скажите, есть ли у вас более короткий способ построить его, или вы можете помочь с самым разочаровывающим первым элементом.