
dtls-fuzzer — это фаззер состояний протокола для реализаций DTLS-серверов.
dtls-fuzzer — это Java-инструмент, который выполняет протокольный state fuzzing DTLS-серверов. Более конкретно, он поддерживает следующие функции:
dtls-fuzzer использует TLS-Attacker для генерации/разбора сообщений DTLS, а также для поддержания состояния. Для этого TLS-Attacker был расширен поддержкой DTLS. dtls-fuzzer опирается на версию 3.0b TLS-Attacker, версию, реализующую улучшение DTLS.
Артефакт содержит:
Наиболее важные папки в корневом каталоге dtls-fuzzer:
'experiments/results' содержит результаты экспериментов, которые являются основным результатом работы. В частности,
Выходные папки именуются на основе конфигурации эксперимента, то есть:
В качестве примера, имя папки 'jsse-12_rsa_cert_none_rwalk_incl' указывает на эксперимент с реализацией JSSE 12 DTLS, использующий алфавит, включающий входные данные для выполнения квитирования RSA, аутентификация клиентского сертификата отключена, алгоритм тестирования — random walk, и повторные передачи включены.
Выходная папка содержит:
Оценщик может проверить (например), что результаты экспериментов в 'included' соответствуют тем, что показаны в Таблице 4, или что конфигурации, протестированные в Таблице 2, также присутствуют в 'all_ciphers'. Обратите внимание, что модели, представленные в статье, являются результатом значительного сокращения/обрезки, тогда как модели, появляющиеся в выходных папках, не изменены.
Для оценки dtls-fuzzer необходимо выполнить следующие шаги:
За этим разделом оценки следует руководство по использованию dtls-fuzzer, в котором представлены его основные варианты использования.
dtls-fuzzer тестировался на Ubuntu 18.04 и Debian 9 дистрибутивах Linux. Он должен работать на любом современном дистрибутиве Linux. Поддержка других платформ не тестировалась. Это руководство предполагает использование дистрибутива на основе Debian (с 'apt-get').
Требуется виртуальная машина (VM) с Java 8 JDK (Java Development Kit). Версия, используемая для запуска экспериментов: 1.8.0_222, хотя более поздние версии Java 8 также должны работать. Обратите внимание, что инструмент не собирается на Java 9 или более поздних версиях. Мы также полагаемся на maven (утилита 'mvn') для управления зависимостями/развертывания.
Мы рекомендуем использовать достаточно мощную машину, иначе чувствительные временные параметры, такие как время ожидания ответа, могут стать слишком низкими, что приведет к другим результатам, отличным от полученных в статье. Хуже того, это может привести к сбою экспериментов по обучению. Исходные эксперименты проводились на многоядерном сервере, однако мы ожидаем (хотя тщательно не тестировали), что обучение должно быть возможно на настольном компьютере с процессором i7. Обучение также возможно на более слабых системах, если соответствующим образом настроить временные параметры. Наконец, для визуализации моделей .dot путем экспорта в .pdf требуется установка библиотеки graphviz. Предполагается, что утилита 'dot', предоставляемая graphviz, находится в системном PATH.
Таким образом, рекомендуемые предварительные требования:
dtls-fuzzer требует Java 8 JDK (Java Development Kit). Если Java не установлена, устанавливаем реализацию OpenJDK (через 'apt-get' на Ubuntu), и можем пропустить остальную часть этого подраздела.
> sudo apt-get install openjdk-8-jdk
Если версия Java установлена, мы можем проверить, какая это версия, запустив:
> java -version
Код версии должен начинаться с 1.8 (например, 1.8.0_242), а виртуальная машина должна быть "Server VM" (что указывает на то, что установлен полный JDK, а не только среда выполнения). Если это так, то с Java мы закончили. Если нет, мы можем проверить, установлена ли Java 8 JDK на нашей платформе, но в данный момент не выбрана, перечислив установленные Java VM через:
> update-java-alternatives --list
Если Java 8 JDK появляется, мы можем использовать ту же команду, чтобы настроить Java 8 JDK как реализацию Java по умолчанию.
> sudo update-java-alternatives --set java-1.8.0-openjdk-amd64
В противном случае необходимо выполнить полную установку, как показано в начале. К сожалению, 'update-java-alternatives' иногда не срабатывает, о чем свидетельствуют сообщения об "ошибках". Если такой случай возникает, мы можем использовать 'update-alternatives' для интерактивной настройки того, какая Java VM выбрана для 'java' (интерпретатор) и 'javac' (компилятор).
> sudo update-alternatives --config java
> sudo update-alternatives --config javac
Установив Java 8, приступаем к установке других зависимостей: maven, graphviz плюс некоторые общие зависимости SUT. Затем клонируем репозиторий dtls-fuzzer в выбранную папку, переключаясь на ветку артефакта. В завершение делаем эту папку текущим каталогом.
> sudo apt-get install maven graphviz autotools-dev automake libtool
> git clone -b usenix20-artifact https://github.com/assist-project/dtls-fuzzer.git ~/dtls-fuzzer
> cd ~/dtls-fuzzer
Сначала запускаем скрипт 'prepare.sh', который устанавливает библиотеки, от которых зависит dtls-fuzzer, а именно два локальных .jar и TLS-Attacker 3.0b. Затем устанавливаем сам инструмент. Полученные команды в POSIX-системе будут:
> bash prepare.sh
> mvn clean install
После выполнения этих шагов должна быть создана директория 'target', содержащая 'dtls-fuzzer.jar'. Это наша исполняемая библиотека. С этого момента предполагается, что команды выполняются из корневого каталога dtls-fuzzer.
Предположим, мы хотим сгенерировать модель для OpenSSL 1.1.1b, используя только PSK (предварительные общие ключи). Быстрый запуск dtls-fuzzer выглядит следующим образом.
Сначала настраиваем SUT, что автоматически делается скриптом 'setup_sut.sh'.
> bash setup_sut.sh openssl-1.1.1b
Затем выбираем файл аргументов из папки 'args/openssl-1.1.1b'. Замечаем, что есть несколько файлов аргументов на выбор, а именно:
learn_openssl-1.1.1b_all_cert_none_rwalk_incl
learn_openssl-1.1.1b_all_cert_nreq_rwalk_incl
learn_openssl-1.1.1b_all_cert_req_rwalk_incl
learn_openssl-1.1.1b_psk_rwalk_incl
Интересующий нас файл аргументов — 'learn_openssl-1.1.1b_psk_rwalk_incl', так как его имя указывает на PSK. Таким образом, мы выбираем его и запускаем фаззер на нем. Дополнительно ограничиваем количество тестов до 200, чтобы сократить время обучения. Наконец, для OpenSSL необходимо установить LD_LIBRARY_PATH в каталог реализации ('suts/openssl-1.1.1b/'). Перед запуском обучения мы, возможно, захотим выполнить простой тест, чтобы проверить, что наша настройка работает. Хорошим тестом является просто завершение квитирования. Мы передаем файл аргументов вместе с соответствующим тестом из 'examples/tests' в качестве параметра. Получаем:
> LD_LIBRARY_PATH=suts/openssl-1.1.1b/ java -jar target/dtls-fuzzer.jar @args/openssl-1.1.1b/learn_openssl-1.1.1b_psk_rwalk_incl -test examples/tests/psk
Если все прошло хорошо, сервер должен вывести "This is a hello message" — сообщение, которое мы отправляем после завершения квитирования. Зная, что настройка работает, мы можем начать обучение, выполнив:
> LD_LIBRARY_PATH=suts/openssl-1.1.1b/ java -jar target/dtls-fuzzer.jar @args/openssl-1.1.1b/learn_openssl-1.1.1b_psk_rwalk_incl -queries 200
Замечаем, что для эксперимента была создана выходная директория 'output/openssl-1.1.1b_psk_rwalk_incl/'. Мы можем выполнить 'ls' этой директории, чтобы проверить текущий статус эксперимента (количество сгенерированных гипотез...).
> ls output/openssl-1.1.1b_psk_rwalk_incl/
Если все идет хорошо, через 20-30 минут выходная директория должна содержать файл 'learnedModel.dot'. Мы можем визуализировать файл с помощью утилиты 'dot' из graphviz, экспортировав его в .pdf и открыв .pdf с помощью любимого просмотрщика .pdf.
> dot -Tpdf output/openssl-1.1.1b_psk_rwalk_incl/learnedModel.dot > output/openssl-1.1.1b_psk_rwalk_incl/learnedModel.pdf
> evince output/openssl-1.1.1b_psk_rwalk_incl/learnedModel.pdf
Наконец, мы можем использовать 'trim_model.sh' для создания улучшенной/более короткой версии модели. Это можно сделать следующим образом:
> bash trim_model.sh --output output/openssl-1.1.1b_psk_rwalk_incl/nicerLearnedModel.dot output/openssl-1.1.1b_psk_rwalk_incl/learnedModel.dot
> dot -Tpdf output/openssl-1.1.1b_psk_rwalk_incl/nicerLearnedModel.dot > output/openssl-1.1.1b_psk_rwalk_incl/nicerLearnedModel.pdf
> evince output/openssl-1.1.1b_psk_rwalk_incl/nicerLearnedModel.pdf
Теперь мы можем определить соответствие системы, проверив модель на соответствие спецификации...
При выполнении 'ls' выходной директории мы можем найти 'error.msg'. Это признак того, что эксперимент завершился неудачей и обучение было прервано. В таких случаях отображение содержимого показывает причину сбоя.
> cat output/openssl-1.1.1b_psk_rwalk_incl/error.msg
Обратите внимание, что проверка соответствия все еще может быть выполнена на последней сгенерированной гипотезе, при условии, что потенциальные находки проверены на системе (как и должно быть в любом случае).
Мы предоставляем скрипт для настройки SUT. Этот скрипт загружает исходные файлы, устанавливает некоторые зависимости (jvm) и собирает SUT. Чтобы просмотреть SUT, для которых предоставлена автоматическая настройка, выполните:
> bash setup_sut.sh
Чтобы настроить, например, реализацию tinydtls от Contiki-NG, выполните:
> bash setup_sut.sh ctinydtls
Скрипт создаст две папки в корневом каталоге dtls-fuzzer.
К сожалению, автоматизация настройки SUT является сложным процессом, поэтому мы используем следующие обходные пути. Для Java SUT (JSSE, Scandium) мы не собираем реализации, вместо этого используем скомпилированные .jar из каталога 'experiments/suts'. Обратите внимание, что исходный код этих Java SUT (серверных приложений) находится в открытом доступе онлайн, см. Scandium и JSSE, что также верно для PionDTLS. Автоматическая установка зависимостей может запросить доступ 'sudo'. Это происходит для GnuTLS, который полагается на внешние библиотеки, такие как nettle, и для Eclipse TinyDTLS, который полагается на autoconf. Наконец, мы не предоставляем автоматическую настройку/файлы аргументов для NSS и PionDTLS из-за сложности настройки этих систем.
Если в процессе настройки что-то перестало работать, удаление папки 'suts' (или папки 'suts/SUT', относящейся к конкретному SUT) и повторный запуск скрипта настройки может решить проблему. Кроме того, в случае сбоя сборки исходный код реализации все равно должен быть загружен в каталог 'suts'. Обходной путь — собрать реализацию вручную. Пока реализация собрана, наша настройка должна работать.
Мы приводим неполное дерево зависимостей различных SUT. Зависимости, выделенные курсивом, — это те, которые 'setup_sut.sh' пытается установить с использованием доступа 'sudo'.
Теперь мы готовы обучать конфигурацию SUT. Файлы аргументов для различных конфигураций SUT предоставлены в каталоге 'args', расположенном в домашнем каталоге dtls-fuzzer. Каждое имя файла аргументов описывает настройку эксперимента (SUT, алфавит, аутентификация), как описано именами выходных папок в 'experiments/results/'. Чтобы начать обучение для SUT с использованием файла аргументов, выполните:
> java -jar target/dtls-fuzzer.jar @args/sut_name/arg_file
Выходная папка будет сохранена в сгенерированном каталоге 'output'.
По сравнению с экспериментами в статье, мы увеличили тайм-аут ответа для нескольких SUT в качестве адаптации к менее мощному оборудованию. Чтобы сократить время обучения, мы предлагаем уменьшить границу тестов алгоритма random walk с 20000 до 5000. Это можно сделать следующим образом:
> java -jar target/dtls-fuzzer.jar @args/sut_name/arg_file -queries 5000
Это переопределит настройку границы в файле аргументов. За исключением GnuTLS, PionDTLS и JSSE, мы ожидаем, что обучение даст те же модели при этой более низкой границе.
Время может стать проблемой, вызывая недетерминизм, за которым следует резкое завершение с информативным файлом 'error.msg'. В таких случаях есть два параметра, которые можно настроить:
Эти параметры можно настроить, переопределив (скорее всего, увеличив) соответствующие настройки в файле аргументов:
> java -jar target/dtls-fuzzer.jar @args/sut_name/arg_file -timeout new_response_timeout -runWait new_start_timeout
Чтобы избежать проблем, связанных с временем, мы рекомендуем запускать эксперименты на достаточно мощной машине. Основной причиной недетерминизма является то, что SUT слишком долго запускается или генерирует ответ. Эта вероятность уменьшается с увеличением вычислительной мощности.
Возможно, мы захотим автоматически завершать эксперименты через определенный период, особенно эксперименты, которые не ожидается, что они когда-либо завершатся. Установка этого периода возможна через параметр time limit, которому присваивается максимальная продолжительность, разрешенная для эксперимента. Эта продолжительность указывается в формате ISO 8601. Чтобы ограничить время выполнения эксперимента 60 минутами, мы выполнили бы:
> java -jar target/dtls-fuzzer.jar @args/sut_name/arg_file -timeLimit "PT60M"
Можно запускать несколько экспериментов одновременно при условии, что серверы настроены прослушивать разные порты. Мы можем запускать каждый эксперимент в отдельном терминале. В качестве альтернативы мы можем запускать эксперименты в одном терминале, используя утилиту 'disown':
> java -jar target/dtls-fuzzer.jar @args/ctinydtls/learn_ctinydtls_psk_rwalk 1>/dev/null 2>&1 & disown
Однако запуск более нескольких (>2) создает дополнительную нагрузку на машину. Это также увеличивает вероятность сбоя обучения из-за случайной коллизии портов. В большинстве конфигураций серверы настроены прослушивать какой-то жестко заданный порт через localhost, конфигурации, предоставленные в 'args', используют различные жестко заданные порты. Для конфигураций JSSE и Scandium настройка отличается. При каждом тесте SUT запускает сервер, прослушивающий динамически выбираемый порт, и сообщает порт через TCP-сокеты dtls-fuzzer. Это имеет преимущество в том, что уведомляет dtls-fuzzer, когда сервер готов принимать пакеты (без этого dtls-fuzzer пришлось бы слепо ждать произвольное время запуска сервера). Недостатком является то, что выделенный порт может совпадать с жестко заданным портом другого эксперимента, когда поток сервера недавно был остановлен, а новый поток еще не запущен (это означает, что жестко заданный порт может быть использован при динамическом выделении). Чтобы избежать такой формы коллизии, мы рекомендуем запускать эксперименты Scandium и JSSE отдельно от всех остальных.
Мы рекомендуем следующие конфигурации, для которых автоматическая сборка надежна, обучение выполняется быстрее или были найдены интересные ошибки. Убедитесь, что вы настроили SUT перед выполнением указанной команды. Вы заметите, что мы по возможности сосредотачиваемся на конфигурациях PSK. Это связано с тем, что PSK с небольшими паролями требует значительно меньше времени обработки, чем любой другой механизм шифрования.### OpenSSL 1.1.1b Можно опробовать любую конфигурацию openssl-1.1.1b (например, 'args/openssl-1.1.1b/learn_openssl-1.1.1b_all_cert_req_rwalk_incl'). Эксперименты завершаются быстро (менее дня), проверяя все алгоритмы обмена ключами. Команда для конфигурации, требующей сертификат клиента, с использованием всех алгоритмов обмена ключами (PSK, RSA, ECDH, DH):
> LD_LIBRARY_PATH=suts/openssl-1.1.1b/ java -jar target/dtls-fuzzer.jar @args/openssl-1.1.1b/learn_openssl-1.1.1b_all_cert_req_rwalk_incl -queries 5000
Обратите внимание: при обучении OpenSSL необходимо указать переменную LD_LIBRARY_PATH на каталог установки.
Любую конфигурацию mbedtls-2.16.1 можно использовать по тем же причинам, что и OpenSSL. Эксперименты занимают больше времени из-за более медленной работы SUT. Команда для конфигурации с отключенной аутентификацией сертификата клиента с использованием всех алгоритмов обмена ключами:
> java -jar target/dtls-fuzzer.jar @args/mbedtls-2.16.1/learn_mbedtls_all_cert_none_rwalk_incl -queries 5000
Сокращенная версия модели, полученной для этой конфигурации, приведена в приложении. Мы можем использовать низкую границу тестов 2000, так как входной алфавит мал, что упрощает тестирование.
> java -jar target/dtls-fuzzer.jar @args/ctinydtls/learn_ctinydtls_psk_rwalk -queries 2000
Для WolfSSL мы предоставляем конфигурацию PSK, для которой обучение должно завершиться относительно быстро.
> java -jar target/dtls-fuzzer.jar @args/wolfssl-4.0.0/learn_wolfssl-4.0.0_psk_rwalk -queries 2000
Более новая версия GnuTLS, которую мы анализировали, дала компактные модели. К сожалению, включение аутентификации клиента привело к резкому увеличению количества необходимых тестов. Мы предлагаем конфигурацию, в которой она отключена, чтобы сократить время обучения:
> java -jar target/dtls-fuzzer.jar @args/gnutls-3.6.7/learn_gnutls-3.6.7_all_cert_none_rwalk_incl -queries 2000
Сокращенная версия модели, полученной для этой конфигурации, приведена в статье. Модель выявляет важные ошибки, но, к сожалению, эксперимент длительный. Эксперимент не следует запускать параллельно с экспериментами, не связанными со Scandium или JSSE. Команда:
> java -jar target/dtls-fuzzer.jar @args/scandium-2.0.0/learn_scandium-2.0.0_psk_rwalk -queries 2000
Сокращенная версия модели, полученной для этой конфигурации, приведена в статье. Модель выявляет важные ошибки. Эксперимент не следует запускать параллельно с экспериментами, не связанными со Scandium или JSSE. Обратите внимание: обучение JSSE не завершается/не сходится, строя гипотезы со все большим числом состояний. Поэтому мы настроили эксперименты JSSE на автоматическое завершение через один день (два дня в статье). Команда для обмена ключами RSA:
> java -jar target/dtls-fuzzer.jar @args/jsse-12/learn_jsse-12_rsa_cert_req_rwalk_incl
Вместо трудоемкого обучения мы можем просто проверить, можно ли завершить рукопожатие в этой конфигурации без отправки каких-либо сертификатов. Это можно сделать, выполнив:
> java -jar target/dtls-fuzzer.jar @args/jsse-12/learn_jsse-12_rsa_cert_req_rwalk_incl -test examples/tests/rsa
После завершения обучения в выходном каталоге анализируются:
Изученную модель в формате .dot можно визуализировать с помощью библиотеки graphviz, преобразовав в .pdf:
> dot -Tpdf learnedModel.dot > learnedModel.pdf
К сожалению, по мере роста моделей сгенерированные этим методом .pdf-файлы становятся всё труднее читать. Поэтому мы разработали/использовали/импортировали скрипты для сокращения, доступные через 'trim_model.sh'. Скрипт выводит информацию об использовании при запуске:
> bash trim_model.sh
Мы рекомендуем использовать скрипт в его базовой форме:
> bash trim_model.sh learnedModel.dot
Скрипт:
Шаг (5) требует установки пользовательской библиотеки mypydot для Python 3, находящейся в 'experiments\scripts'. Все остальные шаги используют обычный 'sed' и библиотеку Java dot-trimmer. .JAR-файл этой библиотеки включен в 'experiments\scripts'.
Выполните:
> java -jar target/dtls-fuzzer.jar -help
Количество опций может быть огромным. Для обучения реализации DTLS-сервера достаточно указать несколько опций, а именно: "-connect ip_address:port" – адрес, на котором прослушивает работающий DTLS-сервер. Все остальные опции устанавливаются в значения по умолчанию, включая алфавит.
Для запуска одиночного обучения существующей, скажем, локальной реализации сервера, прослушивающего порт 20000, выполните:
> java -jar target/dtls-fuzzer.jar -connect localhost:20000
Скорее всего, возникнут проблемы с таким типом обучения. Обучение требует, чтобы вы могли сбрасывать сервер после каждого теста. Некоторые серверы сохраняют состояние от одного теста к другому. Это может привести к недетерминизму во время обучения, поэтому лучший подход – запускать новый поток сервера для каждого теста с помощью предоставленной команды. Поток сервера завершается после выполнения теста, что обеспечивает правильный сброс. Пример для OpenSSL:
> java -jar target/dtls-fuzzer.jar -connect localhost:20000 -cmd "openssl s_server -accept 20000 -dtls1_2"
При таком количестве параметров команды могут стать очень длинными. dtls-fuzzer использует JCommander для анализа аргументов, который также может читать параметры из файла. Перейдите в 'experiments/args' для примеров аргументов. Чтобы передать файл аргументов в dtls-fuzzer, укажите его в качестве параметра с префиксом "@". Вы также можете добавлять другие явные аргументы к командам (они перезаписывают указанные в файле аргументов):
> java -jar target/dtls-fuzzer.jar @arg_file ...параметры_перезаписи...
Для запуска пакета обучающих запусков можно использовать скрипт 'launcher.py' в 'experiments/scripts'. При наличии каталога с файлами аргументов инструмент запустит процесс обучения для каждого файла аргументов.
> python3 experiments/scripts/launcher.py --jar target/dtls-fuzzer.jar --args args_folder
Перед проведением экспериментов по обучению полезно проверить, что аргументы установлены правильно, особенно параметры тайминга. Для этого dtls-fuzzer может выполнить пользовательский набор тестов (коллекцию тестов) на SUT и предоставить сводку выходных данных. Эту функцию также можно использовать при диагностике неудачных экспериментов по обучению, т.е. для выяснения того, что пошло не так.
Для запуска набора тестов на сервере с использованием алфавита по умолчанию можно выполнить:
> java -jar target/dtls-fuzzer.jar -connect localhost:20000 -test test_file
Примеры тестовых файлов находятся в 'examples/tests'. Тестовый файл содержит список входных данных, разделенных переводами строк. Тесты разделяются пустыми строками. Конец каждого теста – либо конец файла, либо пустая строка. "#" используется для комментирования строки.
Если у вас есть модель/спецификация, вы также можете запустить набор тестов и сравнить вывод с выводом из спецификации.
> java -jar target/dtls-fuzzer.jar -connect localhost:20000 -test test_file -specification model
Количество запусков тестов настраивается параметром '-times', по умолчанию равным 1. Установка большого значения помогает выявить недетерминизм в конфигурациях обучения, сравнивая вывод каждого теста.
> java -jar target/dtls-fuzzer.jar -connect localhost:20000 -test test_file -times 10
Наконец, если у вас есть файл аргументов для эксперимента по обучению, вы можете использовать их для запуска тестов на соответствующем SUT, просто добавив необходимые аргументы теста:
> java -jar target/dtls-fuzzer.jar @learning_arg_file -test test_file