Source: https://www.usenix.org/conference/osdi21/presentation/yang
Fluffy: Finding Consensus Bugs in Ethereum via Multi-transaction Differential Fuzzing
区块链 BIT-NetLab
Yang Y, Kim T, Chun B G. Finding Consensus Bugs in Ethereum via Multi-transaction Differential Fuzzing[C]//15th USENIX Symposium on Operating Systems Design and Implementation (OSDI 21). USENIX Association, 2021: 349-365.
以太坊是仅次于比特币的全球第二大区块链平台。在以太坊网络中,去中心化的客户端节点必须根据 EVM 规范对交易达成一致的区块链状态。然而,一类被称为”共识 Bug”的实现缺陷,会让不同客户端产生不一致的状态视图,进而可被攻击者利用来分裂网络甚至盗取资产。发表于 OSDI 2021 的这篇论文提出了 Fluffy——一个基于多交易差分模糊测试的工具,成功在最主流的 Geth 客户端中发现了两个可在以太坊主网上被利用的高危 Bug。其中一个 Bug 在被报告四个月后真实触发,导致了自 2016 年 DAO 事件以来以太坊最严重的硬分叉。
一、研究背景:共识 Bug 为何极其罕见又极其危险?
以太坊主网约 98% 的节点运行两种客户端:Go 语言的 Geth(约 80%)和 Rust 语言的 OpenEthereum(约 18%)。它们各自独立实现 EVM 规范,而实现差异可能导致同一笔交易在两个客户端上产出不同的区块链状态,这就是共识 Bug。
自 2014 年以太坊上线至 2019 年,在这两个客户端中总共只发现了 13 个共识 Bug,其中仅 6 个可在主网上被利用。如此稀少,是因为以太坊社区将防范共识 Bug 列为最高优先级,持续投入大量资源进行审计、测试和模糊测试。但一旦被攻击者利用,后果极其严重:可以触发网络分裂使交易作废,也可以从智能合约中窃取 ETH。
已有的以太坊模糊测试工具(EVMLab、EVM libFuzzer、EVMFuzzer)都采用”区块链状态模型”:每次测试仅生成一个初始状态和一笔交易。这种单交易模型只能覆盖有限的客户端内部程序状态,无法探索多笔交易交互后才会出现的深层状态。

二、核心创新:从单交易到多交易的模型跃迁
Fluffy 的关键洞察在于:即使两个客户端从相同的区块链状态出发,如果它们通过不同的交易序列到达该状态,其内部程序变量可能完全不同。例如,某账户区块链层面余额为 0 ETH,但程序层面可能处于”余额为 0、未删除”或”余额为 3、已标记删除”等截然不同的内部状态。
基于此观察,Fluffy 提出了”客户端程序状态模型”:不再直接生成区块链状态,而是通过执行多笔有依赖关系的交易序列,间接构造出各种中间程序状态。再将同一组测试用例分别在 Geth 和 OpenEthereum 上执行,比对输出的区块链状态——如果不一致,则发现了共识 Bug。
这种差分模糊测试(Differential Fuzzing)的思路类似于 N-版本编程中的交叉验证,但 Fluffy 是第一个将其与多交易测试结合的以太坊模糊测试工具。
三、系统设计:语义感知的变异策略
Fluffy 的变异策略包含三个维度:
上下文变异(Context Mutation):通过随机添加、删除、克隆、复制交易和区块,改变目标交易的执行上下文。这是 Fluffy 独有的能力——已有工具都不具备对交易上下文的变异。
字节码变异(Bytecode Mutation):将合约创建交易的数据字段拆分为构造函数(constructor)和返回代码(code-to-return)两部分,分别独立变异。这种结构化处理避免了将数据字段作为整体变异时,构造函数极易提前终止而无法返回有效字节码的问题。
参数变异(Parameter Mutation):随机修改交易的 gas 限制、转账金额、接收地址,以及区块的版本号(对应不同的 EVM 硬分叉升级)等参数。
为提升效率,Fluffy 还采用了进程内模糊测试(避免每次迭代都启动新进程)、”激活地址”机制(将 20 字节地址空间收窄到预编译合约和已创建合约地址)、跳过交易签名验证等优化手段。
四、发现的两个高危 Bug
Fluffy 在 Geth 中发现了两个可在主网上被利用的共识 Bug。
第一个是”浅拷贝 Bug”。Geth 中预编译合约 DataCopy 的实现使用了浅拷贝而非深拷贝。攻击者可以先通过 DataCopy 复制一段数据,然后修改原始内存区域,由于指针共享,复制的数据也会被篡改。这使得 Geth 的执行结果偏离了 EVM 规范。该 Bug 虽然概念上简单,但在实际 Go 代码中追踪指针在多个文件和函数间的传递非常困难,代码审查和静态分析工具都未能发现它。
第二个是”销毁后转账 Bug”。当合约 A 被 SELFDESTRUCT 销毁后,Geth 仅标记其账户对象为”已删除”而非真正销毁。如果同一笔交易中先销毁 A 再向 A 转账 2 ETH,Geth 会将这 2 ETH 挂在被标记删除的账户上。下一笔交易再向 A 转 1 ETH 时,Geth 错误地将旧余额 2 ETH 累加到新账户,最终显示 3 ETH,而规范要求的正确结果是 1 ETH。这个 Bug 必须通过至少两笔交易才能触发,单交易模糊测试工具根本无法发现。

五、真实世界的冲击:以太坊主网硬分叉
研究团队通过以太坊漏洞赏金计划将两个 Bug 私下报告给 Geth 开发者。开发者确认可利用性后在新版本中静默修复。然而,并非所有主网节点都及时升级。
2020 年 11 月 11 日,浅拷贝 Bug 在主网上被真实触发。运行旧版 Geth(v1.9.7 至 v1.9.16)的节点产生了硬分叉,其中包括 Infura——最大的以太坊基础设施服务商。Infura 宕机导致 Metamask、MakerDAO、Uniswap、Compound 等主流去中心化应用同时下线,全球多家交易所(包括币安)暂停了 ETH 交易。约 30 个区块在分叉链上丢失,涉及约 860 万美元的 ETH 转账。区块链社区将此事件视为自 2016 年 DAO 攻击以来最严重的挑战。
六、实验评估
论文在 12 核 Intel Xeon 机器上对 Fluffy 进行了全面评估:
- 在 12 小时模糊测试中,Fluffy 成功发现了 11 个已知共识 Bug 中的 10 个,唯一未能发现的是一个最初通过人工审计才发现的 Bug
- Fluffy 覆盖了 5,809 条代码路径,是 EVMLab(2,185 条)的 2.7 倍
- Fluffy 处理了超过 3.5 亿笔交易和 3,800 万次迭代,分别是 EVMLab 的 510 倍和 55 倍
- CPU 利用率分析显示,Fluffy 将 89.9% 的 CPU 时间用于执行客户端核心逻辑,而 EVMLab 仅有 18.2%
七、总结与展望
Fluffy 证明了多交易差分模糊测试能够有效探索以太坊客户端的深层程序状态,发现单交易方法无法触及的共识 Bug。这一思路可推广至 Cardano、EOS、Tezos 等其他智能合约区块链。论文同时也揭示了去中心化系统中漏洞披露的困境:静默修复无法保证所有节点及时升级,最终酿成了主网硬分叉。这次事件推动了 Geth 团队建立公开的漏洞透明披露政策,也为整个区块链社区的安全治理提供了深刻教训。
论文链接:https://www.usenix.org/conference/osdi21/presentation/yang
开源代码:https://github.com/snuspl/fluffy