您的位置:首页 > 财经 >

只是短短的一行代码就蒸发了¥6,447,277,680 人民币

2018-04-23 14:04:02 来源: 金色财经

今天有人在群里说,Beauty Chain 美蜜 代码里面有bug,已经有人利用该bug获得了 57,896,044,618,658,100,000,000,000,000,000,000,000,

今天有人在群里说,Beauty Chain 美蜜 代码里面有bug,已经有人利用该bug获得了 57,896,044,618,658,100,000,000,000,000,000,000,000,000,000,000,000,000,000,000.792003956564819968 个 BEC

那笔操作记录是

0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f

Beauty Chain 美蜜 代码

下面我来带大家看看,黑客是如何实现的!

我们可以看到执行的方法是 batchTransfer

那这个方法是干嘛的呢?(给指定的几个地址,发送相同数量的代币)

整体逻辑是

你传几个地址给我(receivers),然后再传给我你要给每个人多少代币(value)

然后你要发送的总金额 = 发送的人数* 发送的金额

然后 要求你当前的余额大于 发送的总金额

然后扣掉你发送的总金额

然后 给receivers 里面的每个人发送 指定的金额(value)

从逻辑上看,这边是没有任何问题的,你想给别人发送代币,那么你本身的余额一定要大于发送的总金额的!

但是这段代码却犯了一个很傻的错!

代码解释

代码解释

这个方法会传入两个参数

_receivers

_value

_receivers 的值是个列表,里面有两个地址

0x0e823ffe018727585eaf5bc769fa80472f76c3d7

0xb4d30cac5124b46c2df0cf3e3e1be05f42119033

_value 的值是 8000000000000000000000000000000000000000000000000000000000000000

我们再查看代码(如下图)

代码

我们一行一行的来解释

uint cnt = _receivers.length;

是获取 _receivers 里面有几个地址,我们从上面可以看到 参数里面只有两个地址,所以 cnt=2,也就是 给两个地址发送代币

uint256 amount = uint256(cnt) * _value;

uint256

首先 uint256(cnt) 是把cnt 转成了 uint256类型

那么,什么是uint256类型?或者说uint256类型的取值范围是多少...

uintx 类型的取值范围是 0 到 2的x次方 -1

也就是 假如是 uint8的话

则 uint8的取值范围是 0 到 2的8次方 -1

也就是 0 到255

那么uint256 的取值范围是

0 - 2的256次方-1 也就是

0 到115792089237316195423570985008687907853269984665640564039457584007913129639935

python 算 2的256次方是多少?

代码

那么假如说 设置的值超过了 取值范围怎么办?这种情况称为 溢出

举个例子来说明

因为uint256的取值太大了,所以用uint8来 举例。。。

从上面我们已经知道了 uint8 最小是0,最大是255

那么当我 255 + 1 的时候,结果是啥呢?结果会变成0

那么当我 255 + 2 的时候,结果是啥呢?结果会变成1

那么当我 0 - 1 的时候,结果是啥呢?结果会变成255

那么当我 0 - 2 的时候,结果是啥呢?结果会变成254

那么 我们回到上面的代码中,

amount = uint256(cnt) * _value

则 amount = 2* _value

但是此时 _value 是16进制的,我们把他转成 10进制

(python 16进制转10进制)

代码

可以看到 _value = 57896044618658097711785492504343953926634992332820282019728792003956564819968

那么 amount = _value*2 = 115792089237316195423570985008687907853269984665640564039457584007913129639936

可以在查看上面看到 uint256取值范围最大为 115792089237316195423570985008687907853269984665640564039457584007913129639935

此时,amout已经超过了最大值,溢出 则 amount = 0

下一行代码 require(cnt > 0 && cnt <= 20); require 语句是表示该语句一定要是正确的,也就是 cnt 必须大于0 且 小于等于20

我们的cnt等于2,通过!

require(_value > 0 && balances[msg.sender] >= amount);

这句要求 value 大于0,我们的value是大于0 的 且,当前用户拥有的代币余额大于等于 amount,因为amount等于0,所以 就算你一个代币没有,也是满足的!

balances[msg.sender] = balances[msg.sender].sub(amount);

这句是当前用户的余额 - amount

当前amount 是0,所以当前用户代币的余额没有变动

for (uint i = 0; i < cnt; i++) {

balances[_receivers[i]] = balances[_receivers[i]].add(_value);

Transfer(msg.sender, _receivers[i], _value);

}

这句是遍历 _receivers中的地址, 对每个地址做以下操作

balances[_receivers[i]] = balances[_receivers[i]].add(_value); _receivers中的地址 的余额 = 原本余额+value

所以 _receivers 中地址的余额,则加了

57896044618658097711785492504343953926634992332820282019728792003956564819968 个代币!!!

Transfer(msg.sender, _receivers[i], _value); } 这句则只是把赠送代币的记录存下来!!!

总结

就一个简单的溢出漏洞,导致BEC代币的市值接近归0

那么,开发者有没有考虑到溢出问题呢?

其实他考虑了,

代码

可以看如上截图

除了amount的计算外, 其他的给用户转钱 都用了safeMath 的方法(sub,add)

那么 为啥就偏偏这一句没有用safeMath的方法呢。。。

这就要用写代码的人了。。。

啥是safeMath

啥是safeMath

safeMath 是为了计算安全 而写的一个library

我们看看他干了啥?为啥能保证计算安全.

function mul(uint256 a, uint256 b) internal constant returns (uint256) {

uint256 c = a * b;

assert(a == 0 || c / a == b);

return c;

}

如上面的乘法. 他在计算后,用assert 验证了下结果是否正确!

如果在上面计算 amount的时候,用了 mul的话, 则 c / a == b 也就是 验证 amount / cnt == _value

这句会执行报错的,因为 0 / cnt 不等于 _value

所以程序会报错!

也就不会发生溢出了...

那么 还有一个小问题,这里的 assert 好 require 好像是干的同一件事

都是为了验证 某条语句是否正确!

那么他俩有啥区别呢?

用了assert的话,则程序的gas limit 会消耗完毕

而require的话,则只是消耗掉当前执行的gas

总结

那么 我们如何避免这种问题呢?

我个人看法是

只要涉及到计算,一定要用safeMath

代码一定要测试!

代码一定要review!

必要时,要请专门做代码审计的公司来 测试代码

这件事后需要如何处理呢?

目前,该方法已经暂停了(还好可以暂停)所以看过文章的朋友 不要去测试了...

Rlp7WCZJRE53Huqsa6JeKt2CPoc0zDjqKV4EzIDr.jpeg

不过已经发生了的事情咋办呢?

我能想到的是,快照在漏洞之前,所有用户的余额情况

然后发行新的token,给之前的用户 发送等额的代币...

关键词:

精选 导读

募资55亿港元万物云启动招股 预计9月29日登陆港交所主板

万科9月19日早间公告,万物云当日启动招股,预计发行价介乎每股47 1港元至52 7港元,预计9月29日登陆港交所主板。按发行1 167亿股计算,万

发布时间: 2022-09-20 10:39
管理   2022-09-20

公募基金二季度持股情况曝光 隐形重仓股多为高端制造业

随着半年报披露收官,公募基金二季度持股情况曝光。截至今年二季度末,公募基金全市场基金总数为9794只,资产净值为269454 75亿元,同比上

发布时间: 2022-09-02 10:45
资讯   2022-09-02

又有上市公司宣布变卖房产 上市公司粉饰财报动作不断

再有上市公司宣布变卖房产。四川长虹25日称,拟以1 66亿元的转让底价挂牌出售31套房产。今年以来,A股公司出售房产不断。根据记者不完全统

发布时间: 2022-08-26 09:44
资讯   2022-08-26

16天12连板大港股份回复深交所关注函 股份继续冲高

回复交易所关注函后,大港股份继续冲高。8月11日大港股份高开,随后震荡走高,接近收盘时触及涨停,报20 2元 股。值得一提的是,在7月21日

发布时间: 2022-08-12 09:56
资讯   2022-08-12

万家基金再添第二大股东 中泰证券拟受让11%基金股权

7月13日,中泰证券发布公告,拟受让齐河众鑫投资有限公司(以下简称齐河众鑫)所持有的万家基金11%的股权,交易双方共同确定本次交易的标的资

发布时间: 2022-07-14 09:39
管理   2022-07-14

央行连续7日每天30亿元逆回购 对债市影响如何?

央行12日再次开展了30亿元逆回购操作,中标利率2 10%。这已是央行连续7日每天仅进行30亿元的逆回购缩量投放,创下去年1月以来的最低操作规

发布时间: 2022-07-13 09:38
资讯   2022-07-13

美元指数创近20年新高 黄金期货创出逾9个月新低

由于对美联储激进加息的担忧,美元指数11日大涨近1%创出近20年新高。受此影响,欧美股市、大宗商品均走弱,而黄金期货创出逾9个月新低。美

发布时间: 2022-07-13 09:36
资讯   2022-07-13

美股三大股指全线下跌 纳斯达克跌幅创下记录以来最大跌幅

今年上半年,美股持续回落。数据显示,道琼斯指数上半年下跌15 3%,纳斯达克综合指数下跌29 5%,标普500指数下跌20 6%。其中,纳斯达克连续

发布时间: 2022-07-04 09:51
推荐   2022-07-04

融资客热情回升 两市融资余额月内增加超344亿元

近期A股走强,沪指6月以来上涨4%,融资客热情明显回升。数据显示,截至6月16日,两市融资余额1 479万亿元,月内增加344 67亿元,最近一个半

发布时间: 2022-06-20 09:41
资讯   2022-06-20

4个交易日净买入超百亿元 北向资金持续流入A股市场

北向资金净流入态势延续。继6月15日净买入133 59亿元后,北向资金6月16日净买入44 52亿元。自5月27日至今,除6月13日以外,北向资金累计净

发布时间: 2022-06-17 09:37
推荐   2022-06-17