ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

H2O 项目 picotls 的 LibFuzzer 模糊测试实战指南:构建 fuzz 目标、管理语料库与驱动源码解析

H2O 项目 picotls 的 LibFuzzer 模糊测试实战指南:构建 fuzz 目标、管理语料库与驱动源码解析 后端网络【免费下载链接】h2oH2O - the optimized HTTP/1, HTTP/2, HTTP/3 server项目地址https://gitcode.com/gh_mirrors/h2/h2o点击查看免费下载H2O 将 TLS 实现 picotls 以依赖形式内置于deps/picotls并在该子项目中提供了完整的 LibFuzzer 模糊测试基础设施三个可独立构建的 fuzz 目标fuzz-client-hello、fuzz-server-hello、fuzz-asn1、种子语料库以及语料库合并规范。本文以 deps/picotls/fuzz/README.md 为主线结合 CMakeLists.txt 与三个驱动源码讲解如何构建并运行这些 fuzz 目标、如何管理种子语料库、如何提交能覆盖新路径的种子文件并深入剖析 fuzz 驱动与PTLS_FUZZ_HANDSHAKE宏的底层实现原理。读完本文你将能够独立在本地复现 picotls 的模糊测试流程并理解其确定性假密码学设计如何让 TLS 握手模糊测试既快速又可复现。一、目录定位fuzz 目录里有什么deps/picotls/fuzz目录包含 picotls 的模糊测试代码与测试数据核心内容是三个用 LLVM LibFuzzer 编写的 fuzz 驱动fuzz target以及若干种子语料库deps/picotls/fuzz/ ├── fuzz-client-hello.c # 服务端握手 fuzz 驱动解析 ClientHello ├── fuzz-server-hello.c # 客户端握手 fuzz 驱动解析 ServerHello ├── fuzz-asn1.c # ASN.1 解析 / 证书、私钥加载 fuzz 驱动 ├── fuzz-client-hello-corpus/ # fuzz-client-hello 的种子语料库 ├── fuzz-server-hello-corpus/ # fuzz-server-hello 的种子语料库384 个种子文件 └── README.md # 本文主线的说明文档LibFuzzer 是 LLVM 提供的进程内覆盖率引导coverage-guided模糊测试引擎它把被测试代码编译进同一个进程反复调用驱动入口LLVMFuzzerTestOneInput并根据覆盖率反馈持续变异输入数据。picotls 的三个驱动都以该函数为统一入口因此可以直接对接 LibFuzzer、OSS-Fuzz 等基础设施。二、构建 fuzz 目标2.1 前置条件构建 fuzz 目标有两个硬性要求编译器必须是 LLVM Clang且版本支持 LibFuzzer即-fsanitizefuzzer可用。README 明确说明you must have a version of LLVM Clang installed that supports LibFuzzer in order for this build to succeed。需要 picotls 自身及其依赖OpenSSL、AEGIS 等能够正常配置。2.2 构建命令在 picotls 的构建目录中执行cmake -DBUILD_FUZZERON .. make-DBUILD_FUZZERON会同时完成两件事对库代码进行插桩instrument使 LibFuzzer 能收集覆盖率反馈构建出三个 fuzz 目标可执行文件fuzz-asn1、fuzz-server-hello、fuzz-client-hello。2.3 CMake 内部逻辑BUILD_FUZZER 分支CMakeLists.txt 中IF (BUILD_FUZZER)分支的完整逻辑如下编译器检查IF (NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang)时直接FATAL_ERROR从构建系统层面杜绝了非 Clang 编译器导致插桩失败的问题。目标定义通过ADD_EXECUTABLE依次定义fuzz-asn1、fuzz-server-hello、fuzz-client-hello三个可执行目标其中fuzz-asn1还带-D_GNU_SOURCE编译宏。关键宏注入ADD_DEFINITIONS(-DPTLS_FUZZ_HANDSHAKE1)全库启用PTLS_FUZZ_HANDSHAKE其作用见下文 2.5 节。OSS-Fuzz 兼容分支当定义了OSS_FUZZ变量时链接FuzzingEngineOSS-Fuzz 提供的 libFuzzer 封装库并将三个目标的LINKER_LANGUAGE强制设为CXX以兼容 OSS-Fuzz 的构建环境否则走本地路径给库代码加-fsanitizefuzzer-no-link给三个 fuzz 目标加-fsanitizefuzzer的编译与链接标志。链接依赖三个目标均链接picotls-core、picotls-openssl、OpenSSL crypto 库与 AEGIS 库其中fuzz-asn1额外链接picotls-minicrypto因为它的证书/私钥加载路径走的是 mini-crypto 实现。仓库还提供了辅助脚本 misc/build_libFuzzer.sh当本地缺少libFuzzer.a时它会从 Chromium 的 LLVM Fuzzer 仓库拉取指定 commit 并调用Fuzzer/build.sh完成编译适用于使用较老 LLVM 版本、需要单独链接静态 libFuzzer 的场景。2.4 关于 README 中示例命令的一个细节README 中提交语料库的示例写作$ ./fuzz-client-hello-merge1 ./fuzz/fuzz-client-hello-corpus ./fuzz/my-new-seeds即./fuzz-client-hello-merge1这是 README 的原样文本实际执行时应在可执行文件名与-merge参数之间保留空格即./fuzz-client-hello -merge1 ./fuzz/fuzz-client-hello-corpus ./fuzz/my-new-seeds-merge1是 LibFuzzer 的语料库合并模式语义是读取右侧新种子目录中的文件仅保留那些为现有语料库新增覆盖率的输入其余丢弃从而在扩充路径覆盖的同时控制语料库体积。2.5 PTLS_FUZZ_HANDSHAKE为模糊测试关闭真实密码学这是 picotls 模糊测试体系中最核心的设计。在 include/picotls.h 中#ifndef PTLS_FUZZ_HANDSHAKE #define PTLS_FUZZ_HANDSHAKE 0 #endif默认关闭当通过-DPTLS_FUZZ_HANDSHAKE1打开后lib/picotls.c 中的真实 AEAD 加解密实现会被替换为确定性假实现#if PTLS_FUZZ_HANDSHAKE static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen, uint8_t content_type) { memcpy(output, input, inlen); memcpy(output inlen, content_type, 1); return inlen 1 16; } static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen) { if (inlen 16) { return PTLS_ALERT_BAD_RECORD_MAC; } memcpy(output, input, inlen - 16); *outlen inlen - 16; /* removing the 16 bytes of tag */ return 0; }假加密不做任何密码运算仅做内存拷贝并模拟 16 字节标签假解密仅检查最小长度后截掉标签。其收益是双重的确定性握手过程不依赖随机数、密钥调度等不确定状态同一输入必然产生同一输出便于复现崩溃与回归测试速度跳过昂贵的密码学运算让 fuzz 引擎把 CPU 预算集中在握手状态机、报文解析、内存管理等真正容易出现内存安全问题越界、UAF、空指针的代码路径上。三、三个 fuzz 目标逐个拆解3.1 fuzz-client-hello站在服务端解析 ClientHellofuzz-client-hello.c 模拟的是TLS 服务端把 fuzz 输入当作客户端发来的 ClientHello及其后的握手消息喂给 picotls 解析。关键流程配置密钥交换与密码套件key_exchanges[0] ptls_openssl_secp256r1密码套件为ptls_openssl_aes128gcmsha256与ptls_openssl_aes256gcmsha384注释明确说明第二个套件用于 PSK ticket。注入确定性随机源deterministic_random_bytes把所有随机字节填 0配合 2.5 节的假 AEAD保证握手全程可复现。伪造 session ticket 加密回调encrypt_ticket_cb_fake使用内置的fake_ticket字节数组代替真实 ticket 加密并设置ticket_lifetime UINT_MAX、max_early_data_size 8192从而覆盖带 early data 与 PSK 恢复的握手路径。执行握手解析ptls_handshake(tls_server, server_response, data, consumed, hsprop)让服务端消费 fuzz 输入若返回 0 且输入还有剩余字节则用ptls_receive继续解析后续记录覆盖握手完成后的应用数据/告警报文路径。这一目标主要覆盖ClientHello 解析、密码套件协商、密钥交换消息处理、session ticket 恢复与 early data 逻辑。3.2 fuzz-server-hello站在客户端解析 ServerHellofuzz-server-hello.c 模拟的是TLS 客户端流程与前者镜像对称配置客户端上下文ptls_new(ctx_client, 0)注释// 0: client密码套件为ptls_openssl_aes128gcmsha256随机源同样替换为确定性实现。先构造一个真实 ClientHello调用ptls_handshake(tls_client, client_encbuf, NULL, 0, hsprop)生成客户端第一段握手消息清空缓冲区后把 fuzz 输入当作服务端响应ServerHello 及其后消息交给ptls_handshake解析同样地若有余量再走ptls_receive。fake_ticket_cb作为save_ticket回调直接返回 0模拟保存 ticket 但不持久化让会话恢复路径可被无副作用地反复触发。该目标覆盖ServerHello 解析、服务端密钥交换/证书消息处理、客户端对服务端握手的校验逻辑。3.3 fuzz-asn1ASN.1 解析与证书/私钥加载fuzz-asn1.c 的输入被按字节喂养给一个自定义的feeder结构前两个字节决定测试缓冲区大小bytes_max随后按模式分发到四条路径ret 0调用ptls_asn1_validation(bytes, bytes_max, ctx)声明于 include/picotls/asn1.h对整段输入做 ASN.1 结构合法性校验ret 1以输入字节选取偏移byte_index与期望类型expected_type调用ptls_asn1_get_expected_type_and_length覆盖 ASN.1 TLV 长度解析与类型匹配逻辑ret 2把输入写入临时文件后调用ptls_load_certificates(ctx, fname)声明于 include/picotls.h测试 PEM/DER 证书链加载加载成功后释放证书列表ret 3同样写临时文件调用ptls_minicrypto_load_private_key(ctx, fname)声明于 include/picotls/minicrypto.h测试私钥解析。每条路径结束都会释放临时文件与缓冲区确保 fuzz 进程内无资源泄漏累积。四、种子语料库组织、运行与提交4.1 corpus 的组织方式README 规定种子语料库存放在fuzz目录下、以对应 fuzz 目标可执行文件名命名的子目录中。仓库中已存在的示例fuzz-client-hello-corpus/↔ 目标fuzz-client-hellofuzz-server-hello-corpus/↔ 目标fuzz-server-hello每个文件即为一个种子输入无扩展名内容是原始握手/ASN.1 字节流。并不是所有目标都有种子库README 用 some fuzz targets 说明这一点fuzz-asn1 属于未提供内置种子库的目标可从空目录开始配合-merge逐步积累。4.2 运行 fuzzerREADME 给出的基础运行命令./fuzz-client-hello fuzz/fuzz-client-hello-corpus即以fuzz-client-hello-corpus为初始语料库启动模糊测试。README 同时指出You will likely want to tailor fuzzer options to your execution environment——建议根据执行环境定制参数。常用选项包括-max_lenN限制输入最大字节数picotls 握手报文通常几百字节即可覆盖绝大多数解析路径过大的上限会浪费变异预算-jobsN/-workersN并行运行多个 fuzz 进程-timeoutN/-rss_limit_mbN控制单次执行超时与内存上限防止死循环或内存暴涨-print_final_stats1结束时的统计汇总。4.3 提交新种子文件-merge 是硬性要求README 明确欢迎能exercise new paths覆盖新路径的种子文件但在提交前必须通过-merge验证其确实为现有语料库新增了覆盖率$ ./fuzz-client-hello -merge1 ./fuzz/fuzz-client-hello-corpus ./fuzz/my-new-seedsmerge 完成后可再用 LibFuzzer 的-minimize_corpus或-merge结合 minimization 流程对语料库做最小化剔除冗余输入。README 建议查阅 LibFuzzer 官方文档了解语料库最小化的更多细节。五、相关资源h2o 仓库中的其他模糊测试设施picotls 的这套 fuzz 体系服务于 H2O 的 TLS 层而 H2O 本身也在更高层次提供了模糊测试在fuzz/目录下有 h2o 的 HTTP/1、HTTP/2、HTTP/3 与 URL 解析 fuzz 驱动如 driver.cc、driver_h3.cc并有对应的回归测试 t/99fuzzer-http1.t、t/99fuzzer-http2.t、t/99fuzzer-http3.t、t/99fuzzer-url.t这些测试使用fuzz/http1-corpus/、fuzz/http2-corpus/、fuzz/http3-corpus/、fuzz/url-corpus/等大型语料库其中 http2-corpus 含 1272 个、http3-corpus 含 591 个种子文件。两者的分工是picotls 的 fuzz 目标专注 TLS 协议层ClientHello/ServerHello/ASN.1h2o 的 fuzz 驱动专注 HTTP 协议层配合使用可覆盖从 TLS 握手到 HTTP 报文处理的整条链路。六、总结picotls 的模糊测试体系可以概括为三句话一条命令构建cmake -DBUILD_FUZZERON配合 Clang/LibFuzzer产出fuzz-client-hello、fuzz-server-hello、fuzz-asn1三个目标一个宏保证确定性PTLS_FUZZ_HANDSHAKE1用假 AEAD 替换真实密码学让握手模糊测试可复现、极快速源码见 lib/picotls.c一套语料库规范种子按目标名存放于fuzz/target-corpus/新增种子必须用-merge1验证并增量扩充覆盖率。这套设计同时兼容 OSS-FuzzOSS_FUZZ分支直接链接FuzzingEngine意味着同样的代码既能本地运行也能无缝接入 Google 的持续模糊测试平台。对于希望为 picotls 或 h2o 贡献安全性的开发者从扩展这三个 fuzz 目标、补充覆盖新握手路径的种子文件开始是最直接也最受项目欢迎的切入点。赞分享后端网络【免费下载链接】h2oH2O - the optimized HTTP/1, HTTP/2, HTTP/3 server项目地址https://gitcode.com/gh_mirrors/h2/h2o点击查看免费下载相关推荐Wasmtime 的 cargo fuzz 模糊测试实战指南fuzz 目标、语料库与 Bug 复现Wasmtime 的 cargo fuzz 模糊测试实战指南fuzz 目标、语料库与 Bug 复现 本指南以 Wasmtime 仓库中的 fuzz/READM语言运行时JIT编译编译器TiKV 模糊测试Fuzzing实战指南fuzz CLI 与 libFuzzer / Honggfuzz / AFL 测试目标全解析TiKV 模糊测试Fuzzing实战指南fuzz CLI 与 libFuzzer / Honggfuzz / AFL 测试目标全解析 导读 本文围绕 Ti数据库KV存储分布式数据库云原生Mbed TLS Fuzz Targets 实战指南基于 libFuzzer 与 oss-fuzz 的库级模糊测试Mbed TLS Fuzz Targets 实战指南基于 libFuzzer 与 oss fuzz 的库级模糊测试 导读 本文聚焦 Flipper Zero嵌入式物联网固件硬件开发渗透测试上一篇CSWin Transformer为何被称为终极通用视觉Transformer骨干网络CVPR 2022微软官方仓库完整指南下一篇联想笔记本BIOS解锁终极指南一键开启隐藏性能选项的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表