使用Slither避免智能合约“僵局”漏洞

本文详细分析了Edgeware智能合约中的DoS漏洞“Gridlock”,探讨了严格相等性检查的危险性,并介绍了Slither工具的轻量级污点分析检测机制如何帮助开发者预防此类漏洞。

避免智能合约“僵局”:使用Slither

2019年7月1日,以太坊上部署的Edgeware智能合约公开报告了一个拒绝服务(DoS)漏洞,被称为“Gridlock”。该合约可能处理了价值高达9亿美元的以太币。Edgeware随后确认并修复了这个“致命错误”。

当我们听说Gridlock漏洞后,我们使用Slither对存在漏洞和已修复的Edgeware合约进行了分析。其公开的危险严格相等性检测器正确识别了漏洞合约中的危险断言,并显示修复后的合约中不存在此漏洞。

严格相等性与DoS漏洞

Gridlock漏洞在以下代码片段中被识别。发现后,维护人员确认了该错误,并部署了解决该问题的第二个版本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function lock(Term term, bytes calldata edgewareAddr, bool isValidator)
    external
    payable
    didStart
    didNotEnd
{
    uint256 eth = msg.value;
    address owner = msg.sender;
    uint256 unlockTime = unlockTimeForTerm(term);
    // 创建ETH锁合约
    Lock lockAddr = (new Lock).value(eth)(owner, unlockTime);
    // 确保锁合约拥有所有ETH,否则失败
    assert(address(lockAddr).balance == msg.value); // BUG
    emit Locked(owner, eth, lockAddr, term, edgewareAddr, isValidator, now);
}

具体来说,此漏洞的来源是断言,该断言在新创建的Lock合约的余额与发送到此合约的msg.value之间执行严格相等性检查。

从合约开发者的角度来看,这个断言应该成立。新的Lock合约刚刚在前一行创建。此外,它被记入的以太币值等于作为当前交易一部分发送的msg.value。

然而,这假设新创建的Lock合约地址在创建之前将具有零以太币余额。这是不正确的。以太币可以在合约在这些地址实例化之前发送到合约地址。这是可能的,因为以太坊地址生成基于确定性随机数。

DoS攻击包括预计算下一个Lock合约地址并向该地址发送一些Wei。这迫使lock()函数在所有未来交易中的断言处失败,使合约陷入“僵局”。

修复方法是将断言替换为以下内容,其中严格相等“==”被“>=”替换,考虑了在新创建的Lock合约地址已存在的以太币。

1
assert(address(lockAddr).balance >= msg.value);

避免使用严格相等性来确定账户是否有足够的以太币或代币是Solidity中一种广为人知的防御性编程技术。

Slither的危险严格相等性检测器

Slither自2019年1月14日发布的0.5.0版本以来,就有一个公开的危险严格相等性检测器针对此漏洞。我们将此检测器的结果分类为中等影响和高置信度,因为严格相等性在合约操作的基本逻辑中几乎总是被误用。此检查的结果值得仔细审查!

在Lockdrop.sol合约上运行Slither立即识别出易受攻击的断言:

1
2
3
4
5
6
$ slither --detect incorrect-equality Lockdrop.sol
INFO:Detectors:
Lockdrop.lock(Lockdrop.Term,bytes,bool) (Lockdrop.sol#53-67) uses a dangerous strict equality:
        - assert(bool)(address(lockAddr).balance == msg.value)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities
INFO:Slither:Lockdrop.sol analyzed (2 contracts), 1 result(s) found

该检测器使用轻量级污点分析实现,其中污点源是带有msg.value、now、block.number、block.timestamp的程序构造,以及ERC代币balanceOf()函数调用的结果。污点接收器是使用严格相等比较的表达式,即“==”。该分析在Slither的中间语言表示SlithIR上工作,并跟踪污点值在赋值和函数调用中的传播。当污点接收器对污点源有数据依赖时,会生成警报。

简单的文本搜索可能已经捕获此漏洞,但语法正则表达式会产生大量误报警报或完全错过它。这是因为此漏洞模式可以以多种方式表现,包括跨函数调用和变量赋值。硬编码这样的正则表达式具有挑战性。其他安全工具缺乏针对此漏洞的检测器,或产生大量误报。SlithIR支持的轻量级语义污点分析大大提高了此检测器的准确性并减少了误报。

在Lockdrop合约的情况下,Slither的危险严格相等性检测器生成此类警报,因为msg.value和地址余额在断言中的严格相等比较中被使用。这是严格相等性漏洞的教科书示例,Slither轻松捕获。我们还验证了此警报在最近修复的代码中不存在。

Crytic.io正确识别Edgeware智能合约中的“Gridlock”

除了此检测器外,Slither还有35个以上的检测器,可捕获许多Solidity智能合约漏洞。它们与我们的持续保证系统crytic.io中的30个额外专有检测器一起工作(可以认为是“以太坊的Travis-CI”)。所以,请尝试使用Slither。我们很乐意听取您的体验,并欢迎反馈。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计