SmartMesh合约重大漏洞是怎么回事?

SmartMesh(SMT)是以太坊上发行的ERC20代币。4月25日,SMT的智能合约爆出重大漏洞,导致被黑客“创造”出天量的SMT代币,这些SMT代币被充到交易所进行大规模抛售。

@松果

SmartMesh(SMT)是以太坊上发行的ERC20代币。昨天(2018.4.25),SMT的智能合约爆出重大漏洞,导致被黑客“创造”出天量的SMT代币,这些SMT代币被充到交易所进行大规模抛售。

目前,相关交易所已停止所有SMT交易对的交易,但已对SmartMesh项目方和SMT代币持有者造成重大损失。

而且,有此漏的代币还不止SMT,其它的还有BEC、MESH、UGToken、SMART、MTC、FirstCoin、GG Token、CNY Token等。

那么,这到底是怎样的一个漏洞呢?

整型溢出问题

在分析SMT的源码前,先要了解计算机编程领域常见的一个问题:整型溢出(Integer Overflow)

在编程语言中,会把数据划分成不同的类型。

其中,用int表示整数,用uint(unsigned integer)表示无符号的整数,也就是通常说所的“正整数”。

这次的SMT漏洞就是uint溢出导致的。

正整数在计算机中是怎么表示的?

计算机和人不同,就算是1、2、3这种最简单的数字它也看不懂,计算机能看懂的只有“0101000101010100101011000”这种二进制数。

这是因为早期的电子元件只有“通电”、“断电”两种状态,正好可以用“1”和“0”表示。

因此,我们使用的十进制数字,要让计算机能看懂,要先转换为二进制。

在计算机中,每一位二进制数字,称作一个“比特(bit)”。(比特币的“比特”也是这么来的)

下面,用4个比特来表示十进制数:

可以看到,4个比特只能表示16个十进制数字,即0到15。

那如果我想用4个比特表示16怎么办?

答案是不行,除非你增加比特的位数,否则强行让15+1会得到 0 !这就是SmartMesh合约漏洞的原因。

这是为什么呢?

因为装不下了,4个二进制数所有的排列组合用完了也只能表示16个数字,没有第17个数的位置了!

这种机制,类似于一个可以循环的轮子,就像这样:

uint分类

4位比特只能表示16个数字,要表示更多的数字,就需要增加“比特”的位数。

于是出现了8位、16位、32位、64位、128位、256位等,可以想象,256个比特已经可以表示很大的天文数字了。

为了便于管理,出现了一个单位叫“字节(byte)”。

编程中使用的uint(正整数),根据byte数不同,被分成不同的类别,用来表示不同范围的正整数。

可以看到,uint256能表示的十进制数是非常大的,1.15乘以10的77次方!

这个数字有多大?用来表示整个宇宙的物质总和的数量都远远足够了。但就是这么大的数字,黑客让它溢出了,这估计是很多代币发行者都没有想到的。

SMT合约源码的漏洞

在etherscan.io上,可以看到SMT的源码,网址在这里:

问题代码出在第206行,转移代币时会被调用:

这里的判断条件是:

如果这个if条件成立,就终断执行,否则就继续执行之后的代码(转移代币)

balances[_from] 是发送者的代币余额;

_feeSmt 是手续费;

_value 是发送的代币数量。

_feeSmt和_value两个参数都是uint256类型,如果传入两个非常大的数字,让它们的和溢出变为0,这个if条件就不成立,就会直接走到下面转移代币的代码中去。

即这几行代码会被执行:

可以看到,

接收者balances[_to]增加了_value数量的代币;

发送者balances[msg.sender]增加了_feeSmt数量的代币;

并且触发了Transfer事件,结果会被写入区块链。

黑客调用transferProxy函数的交易记录可以从这里看到:

可以看到,黑客给这个函数的_value和_feeSmt参数传入了非常大的数字,导致它们的和溢出,后果我们都知道了。

而代币发送者和接收者都是黑客自己的地址,于是SMT代币的分配变成了这样:

有人会问,uint256溢出需要10^77,这里才10^58,怎么会溢出呢?

这是因为,ERC20代币还有一个默认18位的精度,因此实际转出数量是10^(58+18) =10^76个,再加上 10^76手续费,最终溢出。

如何预防溢出问题?

作为对比,我们看一下EOS的合约代码是怎么写的。

https://etherscan.io/address/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0#code

在104行的位置,定义了DSMath合约,这是一组安全的数学运算函数。

用add()、sub()、mul()、div()取代了“+、-、*、/”数学运算符,在函数内部做了assert(断言)处理,可以防止溢出。

在之前的文章:跟“如花”学以太坊智能合约编程 05中,也介绍了使用OpenZeppelin的SafeMath库来防止溢出和下溢。

SafeMath库的源码如下:

结语

其实整型溢出问题,是编程非常初级的问题,各种解决方案也很完善。

但这么多项目合约源码爆出这种问题,只能说数字货币的圈子还是太浮躁了,就连合约代码都是你抄我的,我抄你的。

大家都很急,急着发币,急着圈钱。

但在这个行业里,代码即法律,代码即金钱。

希望各项目方,通过这次事件,认真对待自己的合约,做好代码审计。区块链和数字货币行业的未来还是会越来越好的。

以上仅代表嘉宾个人观点

不代表币乎社区(bihu.com)

不构成任何投资建议

炒币有风险,入市需谨慎

币乎精选 | 从Steemit社区,看EOS公链解决的到底是不是伪需求

币乎精选 | EOS权力之争,“温州帮”携40亿杀入

币乎精选 | 加密革命之路

币乎社区

bihu.com

好文有好报

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180427A1JK9N00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券