批量扫描智能合约中的整数溢出问题

最近币圈接连出了两个大漏洞,BEC和SMT这两种智能合约存在整数溢出问题,导致可以凭空造币。目前已经出了几篇分析的文章,我也在这里蹭一波热点,说一些其他的东西。

在说其他的之前,还是先提一遍原理。

根据Solidity 的文档,我们可以看到,在这个语言里的整数分为int(有符号)/uint(无符号)两种。变量步长为8,支持从uint8/int8到uint256/int256。uint和int默认代表uint256和int256。对整数的运算是会有溢出问题的。其他更详细的原理分析可以参考这篇文章

首先看 BEC 的代码。完整代码点这里。更加详细的分析点这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BEC
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value;
require(cnt > 0 && cnt <= 20);
require(_value > 0 && balances[msg.sender] >= amount);

balances[msg.sender] = balances[msg.sender].sub(amount);
for (uint i = 0; i < cnt; i++) {
balances[_receivers[i]] = balances[_receivers[i]].add(_value);
Transfer(msg.sender, _receivers[i], _value);
}
return true;
}

这个函数接收两个参数,一个是address的数组,一个是uint256。控制输入地址的数量cnt,填写一个大的_value,使uint256 amount = uint256(cnt) * _value;计算溢出为比较小的值,能过balances[msg.sender] >= amount判断,即可触发bug。当然,这里的问题被归结为没有使用SafeMath做乘法。

再看 SMT 的代码。完整代码点这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeUgt,
uint8 _v,bytes32 _r, bytes32 _s) returns (bool){

if(balances[_from] < _feeUgt + _value) throw;

uint256 nonce = nonces[_from];
bytes32 h = sha3(_from,_to,_value,_feeUgt,nonce);
if(_from != ecrecover(h,_v,_r,_s)) throw;

if(balances[_to] + _value < balances[_to]
|| balances[msg.sender] + _feeUgt < balances[msg.sender]) throw;
balances[_to] += _value;
Transfer(_from, _to, _value);

balances[msg.sender] += _feeUgt;
Transfer(_from, msg.sender, _feeUgt);

balances[_from] -= _value + _feeUgt;
nonces[_from] = nonce + 1;
return true;
}

这个函数问题出现在_value_feeUgt这两个变量上。需要使balances[_from] < _feeUgt + _valuebalances[_to] + _value < balances[_to]balances[msg.sender] + _feeUgt < balances[msg.sender])不成立即可。通过溢出_feeUgt + _value很容易做到。

现在问题来了,我知道了漏洞的原理,可世界上有那么多智能合约,应该怎么批量扫描这类漏洞呢。
我们重新看一遍上面两个漏洞。漏洞的原因是整数溢出,漏洞的特征是函数有两个以上可以控制的参与运算和判断的数字。如果只能传入一个数字参与运算的话,一般来说是无法触发整数溢出的。BEC 的问题函数除了_value之外还有个隐藏参数是_receivers.length。SMT 就是明显的传入了两个参数_value_feeUgt。因此要批量扫描这类问题,只需要找到接收数组或者多个参数的transfer函数即可。

在4月25日下午我们写了爬虫,爬取了https://etherscan.io/tokens上列出的开源的知名智能合约代码。并从火币网爬取了正在交易的 ERC20 代币,下载其源码。我们在下载到的368个智能合约代码中批量查找以上特征,没有发现受到影响的代币。

在4月25日傍晚看到微信公众号上有文章称发现多个ERC20智能合约遭受proxyOverflow漏洞影响。我们发现,该文章中列出的智能合约并不是知名的智能合约,有一部分和一些知名智能合约重名(通过智能合约的地址就能区分),有些甚至已经很久不活跃了。这些智能合约的特点是都使用了transferProxy这个有漏洞的函数。

当然,智能合约的问题不止是整数溢出问题,智能合约一旦出问题就不能修补,直接死亡。不过目前来看,智能合约代码量少,互相借鉴程度高,只要抄袭了热门项目的代码,出现问题的概率还是很低的。

参考文献
http://solidity.readthedocs.io
https://www.anquanke.com/post/id/106382
https://paper.seebug.org/582/
https://mp.weixin.qq.com/s/L0gn5zXPXKcdmzygpoM_Rg

分享到 评论