一行代码蒸发了¥6,447,277,680 人民币

文章讲述了BEC(美链)因智能合约中的溢出漏洞导致大量代币被非法获取,从而几乎使市值归零的事件。问题源于一个简单的计算错误,未使用安全的数学库进行乘法运算,导致金额超过uint256的最大值并回绕到零。作者提出了避免此类问题的建议,包括使用安全的计算库、代码测试和审查。此外,文章还提及了网络安全学习路径,强调了基础和持续学习的重要性。
摘要由CSDN通过智能技术生成

一行代码蒸发了¥6,447,277,680 人民币!

现在进入你还是先行者,最后观望者进场才是韭菜。

美图董事长蔡文胜曾在三点钟群,高调的说出了这句话,随即被大众疯传。

在他发表完言论没多久,2月美链(BEC)上交易所会暴涨4000%,后又暴跌。尽管他多次否认,聪明的网友早已扒出,他与BEC千丝万缕的关系。

庄家坐庄操控币价,美图的股价随之暴涨,蔡文胜顺利完成了他的韭菜收割大计。

但在币圈,割人者,人恒割之。

随着BEC智能合约的漏洞的爆出,被黑客利用,瞬间套现抛售大额BEC,6亿在瞬间归零。

而这一切,竟然是因为一个简单至极的程序Bug。

背景

今天有人在群里说,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

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

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

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

整体逻辑是

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

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

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

然后扣掉你发送的总金额

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

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

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

代码解释

这个方法会传入两个参数

1._receivers 2._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 的时候,结果是啥呢?结果会变成255

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

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 是为了计算安全 而写的一个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

所以程序会报错!

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

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

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

那么他俩有啥区别呢?

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

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

总结

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

我个人看法是

1.只要涉及到计算,一定要用safeMath 2.代码一定要测试! 3.代码一定要review! 4.必要时,要请专门做代码审计的公司来 测试代码

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

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

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

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

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

网络安全入门学习路线

其实入门网络安全要学的东西不算多,也就是网络基础+操作系统+中间件+数据库,四个流程下来就差不多了。

1.网络安全法和了解电脑基础

其中包括操作系统Windows基础和Linux基础,标记语言HTML基础和代码JS基础,以及网络基础、数据库基础和虚拟机使用等...

别被这些看上去很多的东西给吓到了,其实都是很简单的基础知识,同学们看完基本上都能掌握。计算机专业的同学都应该接触了解过,这部分可以直接略过。没学过的同学也不要慌,可以去B站搜索相关视频,你搜关键词网络安全工程师会出现很多相关的视频教程,我粗略的看了一下,排名第一的视频就讲的很详细。 当然你也可以看下面这个视频教程仅展示部分截图 学到http和https抓包后能读懂它在说什么就行。

2.网络基础和编程语言

3.入手Web安全

web是对外开放的,自然成了的重点关照对象,有事没事就来入侵一波,你说不管能行吗! 想学好Web安全,咱首先得先弄清web是怎么搭建的,知道它的构造才能精准打击。所以web前端和web后端的知识多少要了解点,然后再学点python,起码得看懂部分代码吧。

最后网站开发知识多少也要了解点,不过别紧张,只是学习基础知识。

等你用几周的时间学完这些,基本上算是具备了入门合格渗透工程师的资格,记得上述的重点要重点关注哦! 再就是,要正式进入web安全领域,得学会web渗透,OWASP TOP 10等常见Web漏洞原理与利用方式需要掌握,像SQL注入/XSS跨站脚本攻击/Webshell木马编写/命令执行等。

这个过程并不枯燥,一边打怪刷级一边成长岂不美哉,每个攻击手段都能让你玩得不亦乐乎,而且总有更猥琐的方法等着你去实践。

学完web渗透还不算完,还得掌握相关系统层面漏洞,像ms17-010永恒之蓝等各种微软ms漏洞,所以要学习后渗透。可能到这里大家已经不知所云了,不过不要紧,等你学会了web渗透再来看会发现很简单。

其实学会了这几步,你就正式从新手小白晋升为入门学员了,真的不算难,你上你也行。

4.安全体系

不过我们这个水平也就算个渗透测试工程师,也就只能做个基础的安全服务,而这个领域还有很多业务,像攻防演练、等保测评、风险评估等,我们的能力根本不够看。

所以想要成为一名合格的网络工程师,想要拿到安全公司的offer,还得再掌握更多的网络安全知识,能力再更上一层楼才行。即便以后进入企业,也需要学习很多新知识,不充实自己的技能就会被淘汰。

从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。

尾言

因为入门学习阶段知识点比较杂,所以我讲得比较笼统,最后联合CSDN整理了一套【282G】网络安全从入门到精通资料包,需要的小伙伴可以点击链接领取哦! 网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值