3. 安全通信基础

模糊测试,圈里人常叫它 Fuzzing。说白了,就是给目标程序灌一堆「乱七八糟」的数据,看它会不会崩溃、挂掉、或者干出什么出格的事。我入行那会儿,Fuzzing 还是个挺「土」的活——写个脚本随机改几个字节就开跑。现在不一样了,工具链成熟得很,但核心思想没变:用异常输入触发异常行为。

你想想看,一个协议解析器,正常报文它肯定处理得妥妥当当。但你要是给它一个长度字段写死 0xFFFF、实际数据只有 10 个字节的畸形包呢?很多漏洞就是这么挖出来的。

3.1 模糊测试的核心原理

Fuzzing 的流程其实就三步:

  1. 生成测试用例——构造畸形或半畸形的输入数据
  2. 执行目标程序——把数据喂给被测对象
  3. 监控异常——看程序有没有崩溃、断言失败、内存泄漏

我在项目里遇到过最典型的场景:一个私有协议网关,厂商说「绝对稳定」。我拿 Boofuzz 跑了不到两小时,就崩了三次。原因?一个整数字段没做边界检查,传个负数直接写穿堆栈。嗯,这就是 Fuzzing 的价值——你永远不知道哪个「正常」的逻辑藏着「不正常」的漏洞。

核心要点:Fuzzing 不追求「全覆盖」,它追求「高概率触发异常」。覆盖率是手段,不是目的。

3.2 三种测试类型:黑盒、白盒、灰盒

这三种类型,区别在于你对目标内部结构的了解程度。我按自己的理解给你捋一捋:

类型 对源码的依赖 覆盖率 典型场景
黑盒 Fuzzing 完全不需要 低(靠运气) 闭源协议、二进制固件
白盒 Fuzzing 需要源码 + 插桩 高(路径导向) 开源库、自研协议栈
灰盒 Fuzzing 部分需要(如覆盖率反馈) 中高(反馈驱动) 大部分实战场景

黑盒:我最开始做 Fuzzing 就是黑盒。拿个串口抓包工具,把正常报文抓下来,然后随机改字节。效率低,但胜在通用——你不需要知道目标里面长什么样。

白盒:有源码的情况下,白盒 Fuzzing 威力巨大。AFL 就是典型代表,它通过插桩获取每条路径的覆盖信息,然后「聪明地」选择能探索新路径的输入。我曾经用 AFL 测过一个开源 TLS 库,三天挖出 7 个崩溃,其中两个是 CVE 级别的。

灰盒:这是我现在最常用的方式。没有完整源码,但可以通过二进制插桩(比如 Intel PT)拿到覆盖率反馈。说白了,就是「黑盒的操作 + 白盒的反馈」。效率介于两者之间,但适用面最广。

我的建议:如果你刚开始学 Fuzzing,从灰盒入手。工具选 AFL 或 Boofuzz,上手快,效果也明显。

3.3 协议 Fuzzer 设计思路

设计一个协议 Fuzzer,核心就两条路:基于变异和基于生成。我分别说说。

3.3.1 基于变异(Mutation-Based)

拿一个「种子」文件,然后对它做各种「破坏」:

  • 翻转比特(bit flip)
  • 替换字节(byte substitution)
  • 插入/删除数据块(block insert/delete)
  • 修改长度字段(length field tampering)

我曾经遇到一个坑:一个协议报文里有 CRC 校验。我改了数据载荷,但没更新 CRC,结果目标直接丢弃了所有变异报文。后来我加了个「智能变异」——改完数据后自动重算校验字段。嗯,这招很管用。

# 一个简单的比特翻转示例(Python 伪代码)
def bit_flip(data, pos):
    byte_idx = pos // 8
    bit_idx = pos % 8
    data[byte_idx] ^= (1 << bit_idx)
    return data

3.3.2 基于生成(Generation-Based)

这种方式不依赖种子,而是根据协议规范「从零构造」报文。你需要先定义协议模板:

  • 字段类型(整型、字符串、枚举)
  • 字段长度(固定、可变、依赖其他字段)
  • 字段约束(取值范围、校验规则)

Peach Fuzzer 就是基于生成的典型代表。它的 Pit 文件(XML 格式)定义了完整的协议结构。我当年测一个工控协议 Modbus TCP,就是用 Peach 写的模板,把功能码、寄存器地址、数据长度全部参数化,然后让工具自动组合。

注意:基于生成的方式,模板写得好不好直接决定测试质量。我见过有人把模板写得太「宽松」,结果生成的报文全是无效包,目标程序直接忽略。模板要「紧」一点,让每个字段都在合法边界附近试探。

3.4 常用框架对比

市面上 Fuzzing 框架很多,但真正经得起实战考验的,我重点说三个:AFL、Boofuzz、Peach Fuzzer。

特性 AFL Boofuzz Peach Fuzzer
类型 灰盒(插桩反馈) 黑盒/灰盒 黑盒(基于生成)
适用对象 本地二进制程序 网络协议、文件格式 复杂协议、工控/车联网
上手难度 中(需要编译插桩) 低(Python 脚本) 高(Pit 模板学习曲线陡)
效率 极高(路径导向) 中(变异策略较简单) 中高(模板精细则效率高)
社区活跃度 非常活跃 活跃 一般(商业版为主)

AFL(American Fuzzy Lop):我个人的「白月光」。它用遗传算法 + 覆盖率反馈,自动探索程序路径。你只需要提供种子输入和编译插桩后的二进制,剩下的交给它。我曾经用 AFL 测一个 JSON 解析库,从 10 个种子开始,24 小时后覆盖了 85% 的代码路径。

Boofuzz:Sulley 的继承者,纯 Python 实现。我最喜欢它的「监控-恢复」机制——目标崩了,它能自动重启并继续测试。适合长时间跑协议 Fuzzing。我有个项目跑了整整一周,中间崩了 200 多次,Boofuzz 全自动处理了。

Peach Fuzzer:商业级工具,社区版功能受限。它的 Pit 模板非常强大,支持复杂的协议状态机。我测过一些车联网协议(比如 CAN 总线上的诊断协议),Peach 的「状态感知」能力是其他工具比不了的。但说实话,学习成本也高——我第一次写 Pit 模板花了整整两天。

选型建议:测本地程序选 AFL,测网络协议选 Boofuzz,测复杂工业/车联网协议选 Peach。别贪多,先把一个工具用透。

3.5 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作 Fuzzing 入门的「地图」:

模糊测试(Fuzzing)知识体系 核心原理 ① 生成测试用例 ② 执行目标程序 ③ 监控异常 三种测试类型 黑盒(无源码) 白盒(有源码+插桩) 灰盒(反馈驱动) Fuzzer 设计思路 基于变异(Mutation-Based) 基于生成(Generation-Based) 常用框架:AFL | Boofuzz | Peach Fuzzer

从这张图你能看到,Fuzzing 的「根」是核心原理,然后分支出测试类型、设计思路和常用框架。我个人建议你按这个顺序学:先理解原理,再选一个类型(灰盒优先),然后掌握一种设计思路(基于变异上手快),最后选一个框架深入实践。

一句话总结:Fuzzing 就是用「聪明的乱搞」来找漏洞。工具是手段,思路才是核心。别被框架绑架,多想想「这个协议最脆弱的字段在哪」。