美文网首页
合约安全:整数溢出漏洞(0.8.0版本已修复)

合约安全:整数溢出漏洞(0.8.0版本已修复)

作者: 梁帆 | 来源:发表于2022-11-28 12:11 被阅读0次

一、漏洞

比如uint8类型,总共有8位,能够表示00000000~11111111的数值,换算成十进制,就是0~255的数值范围。这时候一旦结果为256时,由于总共只有8位,所以第9位1无法显示,只剩下00000000,即想要的256,其实得到了0。

比如下面这个TimeLock合约:

pragma solidity ^0.4.18;

contract TimeLock {
    mapping(address => uint) public balances;
    mapping(address => uint) public lockTime;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
        lockTime[msg.sender] = now + 1 weeks;
    }

    function increaseLockTime(uint _secondsToIncrease) public {
        lockTime[msg.sender] += _secondsToIncrease;
    }

    function withdraw() public {
        require(balances[msg.sender] > 0);
        require(now > lockTime[msg.sender]);
        uint transferValue = balances[msg.sender];
        balances[msg.sender] = 0;
        msg.sender.transfer(transferValue);
    }
}

其中的increaseLockTime函数中,由于可以自己输入一个自由的时间戳增量,所以会带来整数溢出的危险。试想一下,如果输入的_secondsToIncrease和原有的lockTime[msg.sender]相加,由于溢出,最后使得lockTime[msg.sender]的值成为一个很小的值,这样在withdraw函数中,就可以顺利通过

require(now > lockTime[msg.sender]);

这一行,使得deposit进去的ETH可以提前被取出。

二、预防手段

首先,在0.8.0版本,在语言层面已经解决了这个问题:一旦发生整数溢出,transaction会直接被revert。
0.8.0版本之前,需要用到一个open zeppelin的SafeMath库。

三、真实案例

2018年4月22日,黑客对BEC智能合约发起攻击,凭空取出:

57,896,044,618,658,100,000,000,000,000,000,000,000,000,000,000,000,000,000,000.792003956564819968个BEC代币并在市场上进行抛售,BEC随即急剧贬值,价值几乎为0,该市场瞬间土崩瓦解。

合约版本是^0.4.16,小于 0.8 版本,也没有使用 SafeMath 库,因此存在整数溢出问题。

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;
  }

黑客传入了一个极大的值(这里为2**255),通过乘法向上溢出,使得 amount(要转的总币数)溢出后变为一个很小的数字或者0(这里变成0),从而绕过 balances[msg.sender] >= amount 的检查代码,使得巨大 _value 数额的恶意转账得以成功。

实际攻击的恶意转账记录:

etherscan截图 https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f

相关文章

网友评论

      本文标题:合约安全:整数溢出漏洞(0.8.0版本已修复)

      本文链接:https://www.haomeiwen.com/subject/lqvlfdtx.html