dtls-fuzzer 是一个Java工具,用于对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握手的输入,客户端证书认证被禁用,测试算法为随机游走,且包含重传。
一个输出文件夹包含:
评估者可以检查(例如)'included' 中的实验结果是否与表4中显示的一致,或者表2中测试的配置是否也出现在 'all_ciphers' 中。 请注意,论文中出现的模型经过了大量修剪/精简,而输出文件夹中出现的模型是未经修改的。
为了评估 dtls-fuzzer,需要执行以下步骤:
本评估部分之后是一份关于使用 dtls-fuzzer 的指南,介绍其主要用例。
dtls-fuzzer 已在 Ubuntu 18.04 和 Debian 9 Linux 发行版上测试过。 它应该能在任何最新的Linux发行版上工作。 对其他平台的支持尚未测试。 本指南假设使用的是基于Debian的发行版(具有 'apt-get')。
需要Java 8 JDK(Java开发工具包)虚拟机(VM)。 运行实验使用的版本是1.8.0_222,但后续的Java 8版本也应该可以工作。 请注意,该工具无法在Java 9或更高版本上构建。 我们还依赖maven('mvn' 工具)进行依赖管理/部署。
我们建议使用性能足够强大的机器,否则敏感的时序参数(如响应等待时间)可能变得过低,导致与论文中不同的输出。 更糟糕的是,它们可能导致学习实验失败。 原始实验是在一个多核服务器上运行的,但预计(尽管未进行充分测试)在配备i7处理器的桌面上也应该能够进行学习。 如果在较弱的系统上进行学习,需要相应地调整时序参数。 最后,通过将 .dot 模型导出为 .pdf 进行可视化需要安装 graphviz库。 假设 graphviz 提供的 'dot' 工具位于系统PATH中。
简而言之,建议的先决条件包括:
dtls-fuzzer 需要Java 8 JDK(Java开发工具包)。 如果未安装Java,我们安装OpenJDK实现(在Ubuntu上通过 'apt-get'),并可以跳过本小节的其余部分。
> 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' 有时不成功,表现为 "error" 消息。 如果出现这种情况,可以使用 'update-alternatives' 以交互方式配置由 'java'(解释器)和 'javac'(编译器)选择的Java VM。
> 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 的根目录运行的。
假设我们想仅使用PSK(预共享密钥)为OpenSSL 1.1.1b生成一个模型。 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' 文件。 我们可以使用 graphviz 的 'dot' 工具将该文件可视化,通过导出为 .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
例如,要设置Contiki-NG的tinydtls实现,运行:
> bash setup_sut.sh ctinydtls
该脚本将在 dtls-fuzzer 的根目录中生成两个文件夹。
不幸的是,自动化SUT设置是一个复杂的过程,因此我们采取以下捷径。 对于Java SUT(JSSE、Scandium),我们不构建实现,而是使用来自 'experiments/suts' 目录的已编译 .jar 文件。 请注意,这些Java SUT(服务器应用程序)的源代码可在线公开获取,请参见 Scandium 和 JSSE,PionDTLS 也是如此。 自动安装依赖项可能会提示 'sudo' 权限。 对于依赖外部库(如nettle)的GnuTLS和依赖autoconf的Eclipse TinyDTLS,会发生这种情况。 最后,由于NSS和PionDTLS的设置非常复杂,我们不提供自动设置/参数文件。
如果设置过程中的某些步骤停止工作,删除 'suts' 文件夹(或特定于SUT的 'suts/SUT' 文件夹)并重新运行设置脚本可能会解决问题。 另外,如果构建失败,实现的源代码仍应已下载到 'suts' 目录。 一种解决方法是手动构建实现。 只要实现构建成功,我们的设置就应该能工作。
以下我们给出各个SUT所依赖的不完整依赖树。 斜体的是 'setup_sut.sh' 尝试使用 'sudo' 权限安装的依赖项。
我们现在准备学习一个SUT配置。 在 dtls-fuzzer 主目录的 'args' 目录中提供了各种SUT配置的参数文件。 每个参数文件名描述了实验设置(SUT、字母表、认证),如 'experiments/results/' 中输出文件夹名称所描述的那样。 要使用参数文件开始学习某个SUT,运行:
> java -jar target/dtls-fuzzer.jar @args/sut_name/arg_file
输出文件夹将存储在生成的 'output' 目录中。
与论文中的实验相比,我们增加了一些SUT的响应超时,以适应性能较弱的硬件。 为了缩短学习时间,我们建议将随机游走算法的测试上限从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启动或生成响应耗时过长。 随着计算能力的提高,这种可能性会降低。
我们可能希望在一定时间后自动终止实验,特别是那些预计永远不会终止的实验。 可以通过时间限制参数设置这个时间段,该参数指定实验允许运行的最大持续时间。 该持续时间以 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)实验会给机器带来额外负担。 并且由于意外端口冲突,学习失败的可能性也会增加。 在大多数配置中,服务器被配置为在本地主机上监听某个硬编码端口,'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
学习完成后,输出目录中要分析的内容包括:
可以通过 graphviz 库将 .dot 学习模型转换为 .pdf 进行可视化:
> dot -Tpdf learnedModel.dot > learnedModel.pdf
不幸的是,随着模型规模增大,使用此方法生成的 .pdf 越来越难以阅读。 因此我们开发/使用/导入了修剪脚本,通过 'trim_model.sh' 访问。 该脚本通过运行以下命令提供使用信息:
> bash trim_model.sh
我们建议使用脚本的最基本形式,即:
> bash trim_model.sh learnedModel.dot
该脚本:
步骤(5)需要安装位于 'experiments\scripts' 中的自定义 mypydot Python 3 库。 所有其他步骤使用纯 'sed' 加上 dot-trimmer Java 库。 该库的 .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 ...overwriting params...
要启动一批学习运行,可以使用 'experiments/scripts' 中的 'launcher.py' 脚本。 假设有一个包含参数文件的目录,该工具将为每个参数文件启动一个学习进程。
> 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