Ниже приведён пример фаззинг-тестирования Java-приложения Keycloak — программного продукта с открытым исходным кодом, представляющего собой сервер идентификации и
управления доступом.
Для взаимодействия с Keycloak по умолчанию используется TCP-порт 8080.
Перед выполнением фаззинга необходимо подготовить:
- фаззинг-цель (Keycloak);
- сценарий работы Keycloak;
- начальный корпус вх. данных.
- Установка зависимостей для запуска
Keycloakна хосте.
$ sudo apt install -y openjdk-17-jdk- Создание докер-образа
keycloak-buildдля сборкиKeycloak.
$ docker build -f docker/Dockerfile-build -t keycloak-build docker/- Сборка
Keycloak.
$ docker run --rm -v $PWD:$PWD -w $PWD -ti keycloak-build /bin/bash
$ git clone https://github.com/keycloak/keycloak.git
$ cd keycloak
$ git checkout 26.0.7
$ mvn -pl quarkus/dist -am -DskipTests clean install
$ exit
$ cp keycloak/quarkus/dist/target/keycloak-26.0.7.tar.gz docker
$ cd docker
$ tar xf keycloak-26.0.7.tar.gz
$ cd ..Keycloak запускается в JVM с помощью скрипта ./docker/keycloak-26.0.7/bin/kc.sh, который автоматически формирует и передает параметры Java.
Чтобы вывести эти параметры, в скрипте запуска kc.sh была изменена последняя строка на следующую:
eval exec echo "'$JAVA'" $JAVA_RUN_OPTS
Далее необходимо запустить:
$ ./docker/keycloak-26.0.7/bin/kc.sh start-devПолученные JVM-параметры были добавлены в конфигурационный файл config.json.
В этом файле необходимо заменить пути в полях -Dkc.home.dir и -Djboss.server.config.dir на актуальные.
Начальные образцы входных данных (HTTP-запросы в директории in) для целей фаззинга Keycloak были получены путём запуска в режиме разработчика с возможностью просмотра трафика.
Будет рассмотрено 2 режима фаззинга:
- (тестовый) фаззинг на хосте в однопоточном режиме;
- многопоточный фаззинг в докер-режиме.
Фаззинг выполняется с помощью Crusher, который реализует:
- возможность фаззинга сетевых приложений;
- инструментацию Java байт-кода;
- возможность параллельного изолированного фаззинга в докер-контейнерах
Скрипт fuzz-1.sh используется для проверки запуска фаззера в 1 поток:
$ ./fuzz-1.sh /path/to/crusherгде:
-i- директория с начальным корпусом входных данных;-o- директория для результатов;-t- таймаут на запуск приложения (в мс);--auto-stop-target-server- автоматическое завершение процесса анализируемого сервера после того, как он закрывает сокет передачи данных;-T NetworkTCP- передача входных данных по TCP;--port 8080- порт, использующийся для передачи пакетов;--ip 127.0.0.1- IP-адрес для подключения;--delay- время ожидания (в мс) на установление соединения с сервером;-I javajacoco- тип инструментации Java без форк-сервера;-- ./keycloak-26.0.7/lib/quarkus-run.jar- путь к исполняемому JAR-файлу;io.quarkus.bootstrap.runner.QuarkusEntryPoint- путь к основному классу, содержащему метод main() — точку входа в приложение;--profile=dev- запуск в режиме разработчика (опция keycloak);start-dev- аргумент запуска Keycloak в dev-режиме (как в скрипте kc.sh).
Примечание: при запуске фаззинг-тестирования в однопоточном режиме HTML-отчёт о покрытии не формируется.
В данном примере будет наблюдаться рост покрытия. В директории ./out/queue будут находиться файлы с входными данными, которые приводят к нормальному завершению и росту покрытия.
При многопоточном фаззинге во избежание конфликтов за TCP-порт 8080 запуски различных процессов Keycloak необходимо изолировать.
Для этого воспользуемся докер-режимом крашера - см. "Фаззинг в докер-контейнерах" в документации (crusher/README.pdf).
Менеджер фаззеров (fuzz_manager) создаёт отдельный контейнер для каждого фазз-процесса, который, в свою очередь, запускает Keycloak.
- Настройки лицензии.
Для правильной работы лицензии в докер-контейнерах необходимо в файле docker/license/hasplm.ini указать ip-адрес сервера с сетевым hasp-ключом (красной или черной флешкой) в следующей строке:
serveraddr = X.X.X.X
Если hasp-ключ подключен к машине, где производится запуск, то hasplm.ini не нужно использовать. В таком случае нужно внести соответствующие изменения и в Dockerfile.
- Сборка докер-образа
keycloak-fuzz.
$ ./docker/docker_build.shЗапуск многопоточного фаззинга:
$ ./fuzz.sh /path/to/crusher/где:
--wait-next-instance- временной интервал (мс) между запусками процессов fuzz и eat;--tcp-recv-response- получение ответа от анализируемого TCP-сервера;--docker- Docker-образ;--java-jacoco-trace- формирование отчета об покрытии;--no-affinity- отключение привязки процессов к свободным ядрам;--max-file-size- максимальный размер файла в начальном корпусе входных данных.
Остальные опции см. выше.
Примечание: при запуске необходимо указывать полные пути.
Для просмотра UI фаззера в другом терминале выполните следующую команду:
sudo -E /path/to/crusher/bin_x86-64/ui -o outгде:
-o <path>- путь доoutдиректории, которая хранит в себе результаты работы фаззера
См. также "Пользовательский интерфейс" в документации.
При недостаточных значениях опций --delay и --timeout фаззинг-тестирование может завершиться с ошибкой. В таких случаях в логах будут записаны сообщения о соответствующих ошибках.
Следует проверить следующие логи:
- При однопоточном фаззинге:
out/log_fuzzer
- При многопоточном фаззинге
out/FUZZ-IspFuzzerManager-*/log_fuzzer
При обнаружении таких ошибок следует увеличить значения соответствующих опций в скриптах (fuzz-1.sh и fuzz.sh) и повторно запустить тестирование.
Сохраняются в out/EAT_OUT в следующих поддиректориях (в зависимости от статуса завершения таргета):
queue/- нормальное завершение;crashes/- аварийное завершение (в случае Java - необработанное исключение);hangs/- зависание (таргет продолжает работу после истечения отведённого таймаута).
Для каждого инпута также сохраняется набор отчётов - out/EAT_OUT/results/, в частности - отчёт о покрытии.
$ sudo chown -R $USER:$USER out/
$ firefox out/.tmp/JavaJacocoTrace/report/index.htmlОтчет содержит информацию о покрытии по инструкциям и ветвлениям (branches) на уровне JVM.