——相信关心币圈的人应该都对此事有所了解,SMT的合约漏洞被黑客攻击并疯狂抛售,导致当天币价断崖式下跌,SMT也因此休克并中断交易很长一段时间。因为本人当时也持有一点SMT(没看他们代码之前买的......还是太年轻),所以对此事颇感兴趣,后面就抽空分析了一下SmartMesh的合约代码(下面会贴上代码详解).......果不其然,又是溢出,是溢出,溢出.....
再吐槽一下以太坊团队:这种内存问题,请在底层处理好啊魂淡.!
Solidity:怪我咯,还不是你们被其他语言惯的,这锅我不背....
——好了,不开玩笑了,下面我们来回顾下事情经过:
先贴出当天的交易hash:0x0775e55c402281e8ff24cf37d6f2079bf2a768cf7254593287b5f8a0f621fb83
我们可以看到:
我们从Contract合约地址点进去看看当时详细的合约代码0x55f93985431fc9304077687a35a1ba103dc1e081
为了方便查看,我贴到IDE上,全局搜一下日志方法Transfer(),直接定位关键函数。
这是两个标准的转账方法,第一个是直接转账,第二个是代理人转账,逻辑都是先检测发送人金额是否大于转账金额,以及收款人余额加上转账金额后是否会发生溢出,通过之后,给发送人减钱,给收款人打钱,记录日志,返回true。这么看是没毛病啊。
继续找!
这是合约里的另一个转账方法,我们姑且不考虑它是来干嘛用的,先一行一行来分析代码。
206行:判断转账人的余额是不是大于另外两个数(_value和_feeSmt)的和,如果大于则通过,继续执行。通俗来讲,就是判断
你有没有足够的钱给别人转账,钱不够就直接回退报错,很好理解是吧!好,我们继续!
........停,打住!我们定位到方法参数里的变量声明,两个参数都是uint256,所以这个数的取值范围可以非常大,大到
什么程度呢,0~(2^256)-1这么大!
但是问题来了,如果我们给 uint256 的变量赋值((2^256)-1)+1呢??
没错,会发生溢出,结果就是值从最大数回到初始值的0;
这就是问题的关键所在,不信的可以试试,我们继续看代码。
208
...
214行:这里直接给(_to)这个地址增加_value余额
217行:这里给(msg.sender)这个地址增加_feeSmt余额
——到这里其实就够了,我们顺藤摸瓜,找到当时那笔交易的数据记录:
如果合约用户正常操作,输入错误数据(低于总发行量的交易数额)的话,完全可以在第206行就被拦截到。但如果黑客利用漏洞恶意操作,输入两个非常大的数_value和_feeSmt,让它们加起来的和刚好等于2^256,就会导致uint的溢出,结果回到0值,从而顺利骗过206行的判断逻辑,而此时,方法里的参数(_value和_feeSmt)在memory中的值还是输入时那两个非常大的数,到214行和217行的时候,就顺利转走了巨额SMT,而且在黑客的操作中,_to和msg.sender是相等的,意味黑客会接收两笔巨额SMT......
是不是细思极恐,我们钱包里大多数所谓的币(合约Token),就是这种 不完善的语言 + 一时粗心的程序猿 组合而成的智能合约,而且往往几百行代码承载着的是几十亿的价值,所以韭菜们还是谨慎点比较好......
本文没有诋毁SmartMesh团队的意思,而且他们做的已经够可以了(相较于某些项目来说),出事之后第一时间反应,并冻结交易,把用户的损失降到最低(虽然币价现在还没回去....),最终被黑客套现的千万RMB也是由基金会来买单。
最后祝各位韭菜平安度过2018年。