1.uint256溢出漏洞(以BEC为例)
BEC token的代码可在etherscan中查看
出问题的代码为batchTransfer方法:
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;
}
该方法的目的是批量从请求发送者的账号里各转移_value个ether至_receivers中,于是该方法的执行需要如下前提条件
该方法的调用者的账号中的余额 >_receivers.length * _value
batchTransfer 方法乍一看没什么问题,但作者忘了一个重要的问题,uint256(32个字节)虽然能表示很大的整形,但总归是有限的,会溢出
如果调用者发现这个漏洞,精巧的构造batchTransfer的参数_receivers, _val 使得
_receivers.length * _val = amount 溢出,并且 amount < 调用账号的余额
那么_receivers账号中每个就将凭空产生_val的余额
2.权限检测漏洞(以EDU为例)
edu token的代码可在etherscan中查看
如果上面的代码漏还稍微具有隐蔽性,那么EDU智能合约中的漏洞就low得令人发指。
请看漏洞代码:
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
/// same as above
require(_to != 0x0);
require(balances[_from] >= _value);
require(balances[_to] + _value > balances[_to]);
uint previousBalances = balances[_from] + balances[_to];
balances[_from] -= _value;
balances[_to] += _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
assert(balances[_from] + balances[_to] == previousBalances);
return true;
}
如果不管智能合约的其他部分,单看这个方法,你会莫名其妙,因为该方法的目的是允许任何人从任意账户中转移一定金额到任意目标账号中,请注意主谓宾:
- 任何人
- 任意账号
- 任意目标账号
你在EDU中的资产在这个方法面前是完全没有任何安全性可言的。
其实该方法的本来的目的是授权给一定用户,允许他转移自己的资产,授权的代码是
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
授权是放在allowed中,但transferFrom却忘记了对授权的检测,导致你在EDU中的资产完全没有设防,完整的transferFrom应该如下
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
/// same as above
require(_to != 0x0);
require(balances[_from] >= _value);
require(balances[_to] + _value > balances[_to]);
//少了最关键的这一行:
require(allowed[_from][msg.sender] >= _value);
uint previousBalances = balances[_from] + balances[_to];
balances[_from] -= _value;
balances[_to] += _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
assert(balances[_from] + balances[_to] == previousBalances);
return true;
}
3. 以太坊代币“假充值”漏洞
https://mp.weixin.qq.com/s/-neSbS38cVsnHgvbO2NFLA
以太坊代币交易回执中 status 字段是 0×1(true) 还是 0×0(false),取决于交易事务执行过程中是否抛出了异常(比如使用了 require/assert/revert/throw 等机制)。当用户调用代币合约的 transfer 函数进行转账时,如果 transfer 函数正常运行未抛出异常,该交易的 status 即是 0×1(true)。
如果想了解智能合约更多的漏洞,请看
如何保护你的智能合约:6个Solidity漏洞以及如何避开它们一
如何保护你的智能合约:6个Solidity漏洞以及如何避开它们二
网友评论