Технология фаззинга Python3 скриптами
При фаззинге Python3 используются скрипты на Python в качестве входного корпуса, которые потом обрабатывает интерпретатор. Цель состоит в том, чтобы определить, как интерпретатор реагирует на разнообразные конструкции языка.
Это требует наличия инструментации интерпретатора, чтобы можно было отслеживать покрытие кода и эффективность тестирования. Для этого в данной статье будут использоваться такие инструменты, как afl++
и afl-cov
. Также следует упомянуть, что все нижеописанные действия будут проводиться в контейнере Podman.
Настройка контейнерного окружения для фаззинга
Перед началом фаззинга Python3 нужно настроить контейнерное окружение. Подготовленный Dockerfile из репозитория [1] позволяет создать окружение, полностью готовое для запуска фаззинга и сбора покрытия кода. Он основан на базе alt:p10
. Сначала в нем настраиваются параметры UID и GID. Далее устанавливаются базовые утилиты, такие как bash
. Затем выполняется установка инструментов для сборки и анализа: clang
, llvm
, gcc
, ninja
, а также пакетов для Python (python3-dev
, setuptools
, pip
). Эти инструменты необходимы для компиляции Python3 и интеграции с AFL для фаззинга. Дополнительно устанавливаются утилиты для сбора покрытия кода (lcov
, gcovr
) и Rust, необходимый для поддержки некоторых возможностей AFL++.
После установки всех зависимостей создается изолированный пользователь. Далее устанавливаются дополнительные инструменты, такие как capstone
и libxdc
, для поддержки продвинутых режимов фаззинга. Затем выполняется установка и настройка afl++
и afl-cov
. Контейнер настраивается для использования afl-clang-lto
, чтобы поддерживать фаззинг на уровне сборки Python3. В конце Dockerfile переключается обратно на непривилегированного пользователя и запускает контейнер в режиме оболочки, создавая готовую к использованию изолированную среду для тестирования.
Для начала работы следует склонировать репозиторий с подготовленным Dockerfile и перейти в его директорию:
git clone https://git.altlinux.org/people/zeff/public/python3-fuzzing.git
cd ./python3-fuzzing
После этого, перейдите к сборке контейнера:
podman build -t cpython-fuzzer .
Запуск фаззинга Python3
Процесс запуска фаззинга Python3 состоит из четырёх основных этапов: сборка Python3 с AFL, сборка Python3 с покрытием (AFL-cov), запуск фаззинга, сбор покрытия кода.
Сборка Python3 с инструментацией AFL
Для сборки Python3 с инструментами фаззинга AFL, необходимо выполнить сборку внутри контейнера. Запустите контейнер с образом cpython-fuzzer:
podman run -it --rm --name build-afl \
-v "$(pwd)/artifacts:/artifacts" \
-v "$(pwd):/host:ro" \
--userns=keep-id:uid=500,gid=500 \
cpython-fuzzer bash
Установите переменные окружения для компиляторов AFL:
export CC=afl-clang-lto \
CXX=afl-clang-lto++ \
LD=afl-ld-lto \
CFLAGS="-fPIE" \
CXXFLAGS="-fPIE" \
AFL_USE_ASAN=1 \
AFL_USE_UBSAN=1 \
ASAN_OPTIONS="detect_leaks=0:abort_on_error=1:symbolize=0"
Затем склонируйте исходный код Python3 и приготовьте его к сборке:
git clone --branch 3.9.20-alt1 --depth 1 git://git.altlinux.org/gears/p/python3.git python3.9.20-alt1
cd python3.9.20-alt1
git checkout 3.9.20-alt1
sed -i 's&--enable-ipv6&--enable-ipv6 ax_cv_c_float_words_bigendian=no&' python3.spec
gear --commit --rpmbuild -- rpmbuild --buildroot=/home/user/fuzz_target -bp
Перейдите в директорию сборки и настройте параметры компиляции:
cd /home/user/RPM/BUILD/
rm -rf /artifacts/python3-3.9.20-alt1
mv python3-3.9.20 /artifacts/python3-3.9.20-alt1
cd /artifacts/python3-3.9.20-alt1
mkdir build && cd build
../configure ax_cv_c_float_words_bigendian=no \
--enable-ipv6 \
--with-dbmliborder=gdbm:ndbm:bdb \
--with-system-expat \
--with-system-ffi \
--enable-loadable-sqlite-extensions \
--with-lto \
--with-ssl-default-suites=openssl \
--without-ensurepip
Запустите сборку:
make -j$(nproc) 2>&1 | tee /artifacts/python3-3.9.20-alt1/build.log
exit
После завершения сборки Python3 будет готов для запуска фаззинга с использованием AFL.
Сборка Python3 с инструментацией для покрытия (AFL-cov)
Для анализа покрытия кода при фаззинге необходимо собрать Python3 с дополнительной инструментализацией для покрытия. Для этого сначала запустите контейнер:
podman run -it --rm --name build-cov \
-v "$(pwd)/artifacts:/artifacts" \
-v "$(pwd):/host:ro" \
--userns=keep-id:uid=500,gid=500 \
cpython-fuzzer bash
Внутри контейнера настройте переменные окружения для сборки с поддержкой покрытия:
export CC=clang-15 \
CXX=clang++-15 \
LD=clang-15 \
CFLAGS="-fprofile-arcs -ftest-coverage -fPIE" \
CXXFLAGS="-fprofile-arcs -ftest-coverage -fPIE" \
LDFLAGS="-fprofile-arcs -ftest-coverage -fPIE" \
ALTWRAP_LLVM_VERSION=15.0
Склонируйте исходный код и приготовьте его к сборке:
git clone --branch 3.9.20-alt1 --depth 1 git://git.altlinux.org/gears/p/python3.git python3.9.20-alt1
cd python3.9.20-alt1
git checkout 3.9.20-alt1
sed -i 's&--enable-ipv6&--enable-ipv6 ax_cv_c_float_words_bigendian=no&' python3.spec
gear --commit --rpmbuild -- rpmbuild --buildroot=/home/user/cov_target -bp
Перенестие собранные файлы и выполните настройку:
mv /home/user/RPM/BUILD/python3-3.9.20 /artifacts/python3-3.9.20-alt1-cov
cd /artifacts/python3-3.9.20-alt1-cov
./configure ax_cv_c_float_words_bigendian=no \
--enable-ipv6 \
--with-dbmliborder=gdbm:ndbm:bdb \
--with-system-expat \
--with-system-ffi \
--enable-loadable-sqlite-extensions \
--with-lto \
--with-ssl-default-suites=openssl \
--without-ensurepip
Запустите сборку:
make -j$(nproc) 2>&1 | tee /artifacts/python3-3.9.20-alt1-cov/build.log
exit
Запуск фаззинга
Для запуска фаззинга на собранной версии Python3, выполните следующие шаги. Сначала запустите контейнер:
podman run -it --rm --name run-fuzzing \
-v "$(pwd)/artifacts:/artifacts" \
-v "$(pwd):/host:ro" \
--userns=keep-id:uid=500,gid=500 \
cpython-fuzzer bash
Далее следует подготовить входной корпус, но остановимся здесь поподробнее. В процессе фаззинга Python3, входной корпус представляет собой набор скриптов на Python, которые используются в качестве тестовых данных. Желательно, чтобы входной корпус был разнообразным и содержал как простые, так и сложные конструкции Python, чтобы максимально покрыть возможные пути выполнения.
В качестве входного корпуса в данной статье будет использоваться заранее подготовленный репозиторий [2], содержащий набор тестовых Python-скриптов:
git clone https://github.com/OMGfox/python_input_corpuses
mv python_input_corpuses/input /home/user/indir
rm -rf python_input_corpuses
Настройте окружение для AFL:
export AFL_AUTORESUME=1 AFL_SKIP_CPUFREQ=1 AFL_TRY_AFFINITY=1
mkdir -p /artifacts/outdir
И запустите фаззинг:
afl-fuzz -i /home/user/indir -o /artifacts/outdir \
-m 1000 -t 1000+ \
-M master -- /artifacts/python3-3.9.20-alt1/build/python
Эта команда запускает фаззинг с помощью afl-fuzz
, используя входные данные из директории /home/user/indir
и сохраняя результаты в /artifacts/outdir
. Параметр -m 1000
устанавливает лимит памяти в 1000 МБ (его можно удалить, если ограничение не требуется), а -t 1000+
задаёт максимальное время выполнения одного теста в миллисекундах. Опция -M
указывает, что данный процесс будет работать в режиме мастера. Также возможна опция -S
, которая указывает, что данный процесс будет работать в slave-режиме. После --
указывается путь к исполняемому файлу.
Сбор покрытия кода
Для оценки покрытия кода запустите контейнер:
podman run -it --rm --name run-coverage \
-v "$(pwd)/artifacts:/artifacts" \
-v "$(pwd):/host:ro" \
--userns=keep-id:uid=500,gid=500 \
cpython-fuzzer bash
Внутри контейнера создайте директорию для отчетов покрытия:
cd ~
mkdir /artifacts/coverage
Запустите afl-cov для анализа покрытия:
afl-cov --overwrite --clang --cover-corpus -v -d /artifacts/coverage \
--coverage-cmd "/artifacts/python3-3.9.20-alt1-cov/python @@" \
--code-dir "/artifacts/python3-3.9.20-alt1-cov"
Эта команда запускает afl-cov
для анализа покрытия кода, достигнутого во время фаззинга. Параметр --overwrite
позволяет перезаписать старые отчёты. Флаг --clang
указывает использовать компилятор Clang для точного анализа покрытия. Опция --cover-corpus
заставляет afl-cov
проверять весь сгенерированный корпус. Параметр -v
включает подробный вывод, а -d /artifacts/coverage
указывает, куда сохранять результаты анализа. Опция --coverage-cmd "/artifacts/python3-3.9.20-alt1-cov/python @@"
указывает команду для запуска исполняемого файла с каждым тестовым случаем, где @@
заменяется на файл из корпуса. Наконец, --code-dir
указывает на директорию с исходным кодом для сопоставления сгенерированного покрытия, что позволяет увидеть, какие участки кода были задействованы во время фаззинга.