名称: ruzzy 类型: fuzzer 描述: > Ruzzy是Trail of Bits开发的一个覆盖引导的Ruby模糊测试工具。 用于模糊测试纯Ruby代码和Ruby C扩展。
Ruzzy
Ruzzy是一个基于libFuzzer的覆盖引导模糊测试工具,用于Ruby。它支持模糊测试纯Ruby代码和Ruby C扩展,并带有检测内存损坏和未定义行为的消毒剂支持。
何时使用
Ruzzy是目前唯一生产就绪的覆盖引导Ruby模糊测试工具。
选择Ruzzy当:
- 模糊测试Ruby应用程序或库
- 测试Ruby C扩展的内存安全问题
- 您需要Ruby代码的覆盖引导模糊测试
- 处理具有本地扩展的Ruby gems
快速开始
设置环境:
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
使用包含的玩具示例测试:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby -e 'require "ruzzy"; Ruzzy.dummy'
这应该快速找到一个崩溃,证明Ruzzy正常工作。
安装
平台支持
Ruzzy支持Linux x86-64和AArch64/ARM64。对于macOS或Windows,使用Dockerfile或开发环境。
先决条件
- Linux x86-64或AArch64/ARM64
- 最近版本的clang(测试回溯到14.0.0,推荐最新版本)
- 带有gem安装的Ruby
安装命令
使用clang编译器标志安装Ruzzy:
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
gem install ruzzy
环境变量解释:
MAKE: 覆盖make以尊重后续环境变量CC,CXX,LDSHARED,LDSHAREDXX: 确保使用正确的clang二进制文件以获得最新功能
故障排除安装
如果安装失败,启用调试输出:
RUZZY_DEBUG=1 gem install --verbose ruzzy
验证
通过运行玩具示例验证安装(见快速开始部分)。
编写测试工具
模糊测试纯Ruby代码
纯Ruby模糊测试需要两个脚本,由于Ruby解释器实现细节。
跟踪脚本 (test_tracer.rb):
# frozen_string_literal: true
require 'ruzzy'
Ruzzy.trace('test_harness.rb')
测试工具脚本 (test_harness.rb):
# frozen_string_literal: true
require 'ruzzy'
def fuzzing_target(input)
# 您的代码在此模糊测试
if input.length == 4
if input[0] == 'F'
if input[1] == 'U'
if input[2] == 'Z'
if input[3] == 'Z'
raise
end
end
end
end
end
end
test_one_input = lambda do |data|
fuzzing_target(data)
return 0
end
Ruzzy.fuzz(test_one_input)
运行:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby test_tracer.rb
模糊测试Ruby C扩展
C扩展可以用单个测试工具文件模糊测试,无需跟踪器。
msgpack的示例测试工具 (fuzz_msgpack.rb):
# frozen_string_literal: true
require 'msgpack'
require 'ruzzy'
test_one_input = lambda do |data|
begin
MessagePack.unpack(data)
rescue Exception
# 我们寻找内存损坏,而不是Ruby异常
end
return 0
end
Ruzzy.fuzz(test_one_input)
运行:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby fuzz_msgpack.rb
测试工具规则
| 做 | 不做 |
|---|---|
| 如果测试C扩展,捕获Ruby异常 | 让Ruby异常崩溃模糊测试器 |
| 从test_one_input lambda返回0 | 返回其他值 |
| 保持测试工具确定性 | 使用随机性或基于时间的逻辑 |
| 对纯Ruby使用跟踪脚本 | 对纯Ruby代码跳过跟踪器 |
另见: 对于详细的测试工具编写技术、处理复杂输入的策略和高级策略,参见fuzz-harness-writing技术技能。
编译
使用消毒剂安装Gems
当安装Ruby gems以进行模糊测试时,使用消毒剂标志编译:
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
CFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
CXXFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
gem install <gem-name>
构建标志
| 标志 | 目的 |
|---|---|
-fsanitize=address,fuzzer-no-link |
启用AddressSanitizer和模糊测试器仪器 |
-fno-omit-frame-pointer |
提高堆栈跟踪质量 |
-fno-common |
更好的消毒剂兼容性 |
-fPIC |
共享库的位置无关代码 |
-g |
包含调试符号 |
运行活动
环境设置
运行任何模糊测试活动前,设置ASAN_OPTIONS:
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
选项解释:
allocator_may_return_null=1: 跳过常见的低影响分配失败(DoS)detect_leaks=0: Ruby解释器泄漏数据,暂时忽略这些use_sigaltstack=0: Ruby推荐在使用ASan时禁用sigaltstack
基本运行
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb
注意: LD_PRELOAD 是消毒剂注入所必需的。与 ASAN_OPTIONS 不同,不要导出它,因为它可能干扰其他程序。
使用语料库
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb /path/to/corpus
传递libFuzzer选项
所有libFuzzer选项可以作为参数传递:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb /path/to/corpus -max_len=1024 -timeout=10
参见libFuzzer选项获取完整参考。
复制崩溃
通过传递崩溃文件重新运行崩溃案例:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb ./crash-253420c1158bc6382093d409ce2e9cff5806e980
解释输出
| 输出 | 含义 |
|---|---|
INFO: Running with entropic power schedule |
模糊测试活动开始 |
ERROR: AddressSanitizer: heap-use-after-free |
检测到内存损坏 |
SUMMARY: libFuzzer: fuzz target exited |
发生Ruby异常 |
artifact_prefix='./'; Test unit written to ./crash-* |
崩溃输入保存 |
Base64: ... |
崩溃输入的Base64编码 |
消毒剂集成
AddressSanitizer (ASan)
Ruzzy包含一个预编译的AddressSanitizer库:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby harness.rb
使用ASan检测:
- 堆缓冲区溢出
- 栈缓冲区溢出
- 使用后释放
- 双重释放
- 内存泄漏(在Ruzzy中默认禁用)
UndefinedBehaviorSanitizer (UBSan)
Ruzzy还包含UBSan:
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::UBSAN_PATH') \
ruby harness.rb
使用UBSan检测:
- 有符号整数溢出
- 空指针解引用
- 未对齐内存访问
- 除以零
常见消毒剂问题
| 问题 | 解决方案 |
|---|---|
| Ruby解释器泄漏警告 | 使用 ASAN_OPTIONS=detect_leaks=0 |
| Sigaltstack冲突 | 使用 ASAN_OPTIONS=use_sigaltstack=0 |
| 分配失败垃圾邮件 | 使用 ASAN_OPTIONS=allocator_may_return_null=1 |
| LD_PRELOAD干扰工具 | 不要导出它;在ruby命令中内联设置 |
另见: 对于详细的消毒剂配置、常见问题和高级标志,参见address-sanitizer和undefined-behavior-sanitizer技术技能。
真实世界示例
示例: msgpack-ruby
模糊测试msgpack MessagePack解析器以检测内存损坏。
使用消毒剂安装:
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
CFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
CXXFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
gem install msgpack
测试工具 (fuzz_msgpack.rb):
# frozen_string_literal: true
require 'msgpack'
require 'ruzzy'
test_one_input = lambda do |data|
begin
MessagePack.unpack(data)
rescue Exception
# 我们寻找内存损坏,而不是Ruby异常
end
return 0
end
Ruzzy.fuzz(test_one_input)
运行:
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby fuzz_msgpack.rb
示例: 纯Ruby目标
模糊测试纯Ruby代码与自定义解析器。
跟踪器 (test_tracer.rb):
# frozen_string_literal: true
require 'ruzzy'
Ruzzy.trace('test_harness.rb')
测试工具 (test_harness.rb):
# frozen_string_literal: true
require 'ruzzy'
require_relative 'my_parser'
test_one_input = lambda do |data|
begin
MyParser.parse(data)
rescue StandardError
# 来自畸形输入的预期异常
end
return 0
end
Ruzzy.fuzz(test_one_input)
运行:
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
ruby test_tracer.rb
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 安装失败 | 错误的clang版本或路径 | 验证clang路径,使用clang 14.0.0+ |
cannot open shared object file |
LD_PRELOAD未设置 | 在ruby命令中内联设置LD_PRELOAD |
| 模糊测试器立即退出 | 缺少语料库目录 | 创建语料库目录或作为参数传递 |
| 无覆盖进度 | 纯Ruby需要跟踪器 | 对纯Ruby代码使用跟踪脚本 |
| 泄漏检测垃圾邮件 | Ruby解释器泄漏 | 设置 ASAN_OPTIONS=detect_leaks=0 |
| 安装调试需要 | 编译错误 | 使用 RUZZY_DEBUG=1 gem install --verbose ruzzy |
相关技能
技术技能
| 技能 | 使用案例 |
|---|---|
| fuzz-harness-writing | 编写有效测试工具的详细指导 |
| address-sanitizer | 模糊测试期间的内存错误检测 |
| undefined-behavior-sanitizer | 检测C扩展中的未定义行为 |
| libfuzzer | 理解libFuzzer选项(Ruzzy基于libFuzzer) |
相关模糊测试器
| 技能 | 何时考虑 |
|---|---|
| libfuzzer | 当直接在C/C++中模糊测试Ruby C扩展代码时 |
| aflpp | 通过仪器化Ruby解释器模糊测试Ruby的替代方法 |
资源
关键外部资源
介绍Ruzzy,一个覆盖引导Ruby模糊测试器 Trail of Bits官方博客文章宣布Ruzzy,涵盖动机、架构和初步结果。
Ruzzy GitHub仓库 源代码、额外示例和开发说明。
libFuzzer文档 由于Ruzzy基于libFuzzer,理解libFuzzer选项和行为是有价值的。
模糊测试Ruby C扩展 模糊测试C扩展的详细指南,包括编译标志和示例。
模糊测试纯Ruby代码 模糊测试纯Ruby代码所需的跟踪器模式的详细指南。