使用Slither避免智能合约“死锁”漏洞

本文详细分析了Edgeware智能合约中的Gridlock拒绝服务漏洞,探讨Slither静态分析工具如何通过危险严格相等检测器识别此类漏洞,并介绍其轻量级污点分析技术原理。

避免智能合约“死锁”:使用Slither工具

2019年7月1日,以太坊上部署的Edgeware智能合约中公开报告了一个名为"Gridlock"的拒绝服务(DoS)漏洞。该合约可能处理了价值高达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); // 漏洞点
    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 设计