EDU&BAI任意转走账户Token事件回顾

事件背景
5月23日晚间,EDU(EduCoin)被爆出现合约漏洞,多达数十亿代币被盗。EduCoin是建立在区块链和智能合约技术基础上,面向在线教育内容分享和服务的去中心化教育平台,而EDU是基于以太坊ERC-20协议发行的项目代币,总量150亿。
在曝出合约漏洞之前,EDU的交易K线就已出现了大量抛售的现象。从5月20日午夜开始,大量EDU被人抛售,而持续的抛售带来的则是市场的进一步恐慌,EDU的价格持续走低,直到EDU的交易对被迫关停,期间累计售出的EDU超过20亿。
黑客之所以非常容易得窃取了代币,正是因为EDU的智能合约,存在一个非常大的安全漏洞。
漏洞概述
针对EDU&BAI智能合约出现的问题,BUGX团队做了一些简单分析,仅供参考:
在 transferFrom 函数中,未校验 `allowed[_from][msg.sender] >= _value` 并且函数内 `allowed[_from][msg.sender] -= _value`; 没有使用 SafeMath,导致无法抛出异常并回滚交易。目前发现有大量洗劫行为,攻击者不需要私钥即可转走你账户里所有的 EDU,并且由于合约没有 Pause 设计,导致无法止损。
本漏洞目前被发现在EDU及BAI合约中。
合约转账情况:
EDU: https://etherscan.io/token/0xa0872ee815b8dd0f6937386fd77134720d953581>
BAI:https://etherscan.io/token/0x14d9779b6585f3a7d4f768383b3cb030705dad2e>
漏洞原理
合约中漏洞代码如下:
// 批准转账上限(批准目标可以代我转账的上限)
    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }
 // 代我转账的流程
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        /// same as above
        require(_to != 0x0); // 检测转账对象非空地址
        require(balances[_from] >= _value); // 检测被转账者余额大于_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;
    }
可以看出,这个流程并没做 allowed[ _from][msg.sender] 和 _value 的判断,比如函数开始应该判断:
require(allowed[ _from][msg.sender] >= _value);
如果 allowed[ _from][msg.sender] 不存在,那么值是 0,判断缺失,也就等于之前的 approve 函数形同虚设。然后,这还出现了个有趣的溢出:
allowed[ _from][msg.sender] -= _value;
当 allowed[ _from][msg.sender] 不存在,那么值是 0,减去 _value(大于 0 时),就溢出了(溢出并不会导致中断回滚)。这就是为什么如果用了 SafeMath 就会没问题,因为 SafeMath 会抛出错误,直接中断回滚 transferFrom 函数。
整体这样看下来,EDU 和 BAI 等合约的 transferFrom 盗币事件最核心的问题是权限问题,溢出在这仅仅是个小插曲而已。
本地复现
使用Remix即可简单复现。
1 部署合约
EDU:https://etherscan.io/address/0xa0872ee815b8dd0f6937386fd77134720d953581#code>
BAI:https://etherscan.io/address/0x14d9779b6585f3a7d4f768383b3cb030705dad2e#code>
2 执行攻击
被转账者(使用合约创建者地址即可):0xca35b7d915458ef540ade6068dfe2f44e8fa733c
攻击者:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
transferFrom里输入内容并执行:”0xca35b7d915458ef540ade6068dfe2f44e8fa733c”,”0x14723a09acff6d2a60dcdf7aa4aff308fddc160c”,”3″
可以看到攻击者余额增加了,变为3。