十亿次空值 - Trail of Bits博客
以太坊ABI解析器的隐藏挑战
在以太坊强大的区块链技术背后,隐藏着一个区块链开发者面临的较少人知的挑战:编写健壮的以太坊ABI(应用二进制接口)解析器的复杂性。以太坊ABI对区块链基础设施至关重要,它实现了智能合约与外部应用间的无缝交互。数据类型的复杂性以及精确编码解码的需求使得ABI解析充满挑战。规范或实现中的模糊性可能导致危及用户的漏洞。
漏洞发现:零大小类型(ZST)的威胁
零大小类型(ZST)是在磁盘上存储占用零(或最小)字节,但在内存中表示时需要显著更多空间的数据类型。以太坊ABI允许ZST存在,这可能导致拒绝服务(DoS)攻击:通过迫使应用程序分配大量内存来处理极小的磁盘或网络表示。
考虑以下示例:当解析器遇到ZST数组时会发生什么?它应该尝试解析数组声称包含的尽可能多的ZST。由于每个数组元素占用零字节,定义极大的ZST数组变得轻而易举。
具体示例如下:一个20字节的有效载荷将反序列化为包含数字2、1和3的数组;而另一个8字节的有效载荷将反序列化为2^32个ZST元素(如空元组或空数组)。如果每个ZST在解析后占用零字节内存,这不会成为问题。但实际上,每个元素通常需要少量但非零的内存来存储,导致表示整个数组时需要巨大内存分配,从而引发拒绝服务攻击。
漏洞影响范围
我们发现在多个主流库中存在此漏洞:
- eth_abi (Python): v4.2.0之前版本存在漏洞
- ethabi (Rust): v18.0.0版本
- ethers-rs (Rust): v2.0.10版本
- alloy-rs: 0.4.2版本
- ethereumjs-abi: 0.6.8版本
概念验证(PoC)
我们定义有效载荷为:
|
|
该有效载荷包含两个32字节块,描述了一个序列化的ZST数组。第一个块定义数组元素的偏移量,第二个块定义数组长度。
各库PoC示例
Python (eth_abi):
|
|
Rust (ethabi):
|
|
ethers-rs:
|
|
漏洞发现过程
这个漏洞类型的测试想法来源于在borsh-rs库中发现的问题。该Rust库尝试在恒定时间内解析ZST数组,这导致了未定义行为。在另一次审计中,自定义ABI解析器在解析ZST时也存在DoS向量。鉴于这两个问题不太可能是巧合,我们调查了其他ABI解析库中的此类漏洞。
利用可能性
该漏洞是否可利用取决于受影响库的使用方式。在上述示例中,演示目标是CLI工具。目前尚未找到在主网部署触发此漏洞的智能合约的方法,主要是因为Solidity和Vyper的最新版本禁止ZST。
然而,使用上述任何库的应用程序都可能存在漏洞。例如Etherscan(解析不受信任的ABI声明)以及任何从合约获取和解码数据的链下软件,如果允许用户指定ABI类型,都可能受到此漏洞影响。
协调披露时间线
- 2023年6月30日: 首次联系各库维护者
- 2023年8月2日: 为eth_abi(Python)创建GitHub私有安全公告
- 2023年8月31日: eth_abi(Python)发布修复,未公开提及DoS向量
防护建议
建议开发者对解码器进行模糊测试,因为解码器中的漏洞通常很容易通过模糊测试发现。在Trail of Bits的审计中,我们采用模糊测试来识别漏洞,并教育客户如何进行自己的模糊测试。
本文详细披露了以太坊ABI解析器中的关键漏洞,强调了规范设计和实现加固的重要性,为区块链开发者提供了重要的安全警示。