BEC合约整数溢出漏洞还原与分析
一、币圈一秒,人间一年
有道是币圈一日,人间一年。这个说法又得升级了,叫币圈一秒,人间一年。
前不久,币圈又出大事啦。BEC智能合约被爆出整数溢出漏洞,导致黑客能无限印币,在一次交易中,也就那么几秒钟的事情,黑客就“无中生有”地给两个账户转了天文数字般的BEC币,而原账户一分BEC币都没损失。大家来围观下这笔交易:
https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f

围观归围观,所谓外行看热闹、内行看门道。
看到这么大笔BEC币,想必大家已经流着口水。。。
别馋,虽然大熊不能教大家在现实中黑一把,但搭个测试网络,还原下漏洞,过过瘾也是可以的,顺便还能学习下智能合约部署、整数溢出漏洞原理。知识是无价的,说不定以后能靠这些知识大赚一笔。好吧,言归正传:
那么,什么是整数溢出呢?
二、整数溢出浅析
首先,什么是溢出?
往空杯里面加水,加到满之后,继续加水,水就会流出来,这就是溢出。然而需要注意的是:尽管溢出,但杯里面依然是满水的状态。
然后,什么是整数溢出?
跟满杯加水溢出的情况不同,整数溢出像水轮车。往里面加水,加满之后,继续加水,水轮车将会失衡并转动,此时水将会被全部倒出来,并重新盛水。这就跟整数溢出类似:当变量累加到超过自身容量范围时,将会溢出,归零。而黑客也是利用了这一点,把运算前的数值作为转账金额,把运算后溢出的0作为检测条件,从而实现绕过。

这次的BEC整数溢出漏洞就出在batchTransfer函数(功能是批量转账)里面,具体来说就是以下这行:
uint256 amount = uint256(cnt) * _value;

简单分析一下:比如张三给李四、王五转账,数额为2^255(2的255次方),那么cnt=2,_value=2^255,而将cnt和_value两者相乘,结果为2^256,刚好超过uint256的范围,溢出之后amount的结果为0,接下来所有条件都能通过,最终结果是张三账户减去0个BEC币,而李四、王五的账户增加2^255个BEC币。

三、环境搭建、漏洞还原与分析(无意中又发现了一个漏洞)
这个漏洞虽然不起眼,逻辑也很简单,但是直接带来的后果相当于无限印BEC币啊,这还了得,下面就开始搭建测试网络,也给自己印几批天文数字BEC币过过瘾。
由于区块链环境搭建、以太坊和BEC智能合约部署过程较长,且并不是本文重点,所以简单阐述,若有兴趣可到本人微信公众号《进击的大熊》,回复“BEC”可见。
(一)环境搭建
geth客户端安装,用来运行以太坊节点、创建和管理账户、发送交易、挖矿、部署智能合约。
具体环境是:Win10 64位、geth-windows-amd64-1.8.6
(二)漏洞还原
1、准备创世块文件、初始化创世块,并启动区块链节点
2、创建3个用户,并分别命名为zhangsan、lisi、wangwu
3、主节点zhangsan挖矿,得到一些ETH,后面部署智能合约等操作都需要ETH。
4、复制BEC智能合约源码,用在线Remix工具编译,并部署到虚拟网络,下图可以看到已经部署成功。

5、溢出转账
这里无意中又发现了一个漏洞,不过不是BEC合约,而是console,先前我一直这样来转账:(绿色数值就是2^255)
>bectoken.batchTransfer([lisi,wangwu],57896044618658097711785492504343953926634992332820282019728792003956564819968,{from:zhangsan})
然而失败了若干次,由于数值较大,每次都怀疑是不是复制错了,为了解决这个问题,还试过:
>bectoken.batchTransfer([lisi,wangwu],Math.pow(2,255),{from:zhangsan})
但还是失败,李四和王五的账号依旧为0.
后来用Remix调试才发现,原来一直不能成功绕过检测(require(_value > 0 && balances[msg.sender] >= amount); ),因为amount根本不为0,张三的账户上也没有那么多BEC币。进一步发现,传进去的_value也不是2^255,而是诡异的0x80000000000000016c889……
怪不得不能通过 balances[msg.sender] >= amount

那么,换个思路吧,直接把_value的值在源代码设为2^255则可实现绕过,并成功实现转账,最少证明了理论是可行,大方向没错。

这种情况下,我想只能是console传递数值的时候出现问题了,但找了很多资料没能解决,最后在Github上发帖咨询Geth团队,才知道原来也是个溢出漏洞,真是“洞中洞”啊。
发帖没过多久就收到了回复,感谢这位小哥!
大概意思是:所用的控制台基于JavaScript,而JavaScipt使用float-s来表示数字,直接传递2^255这样大的数值,将会产生溢出,从而使数值发生变化。解决方法是,需要使用一个大数字库,这样才能使用任意精度的整数。

因此我改变打法,用web3.toBigNumber来表示2^255,最终也实现了还原,成功给李四、王五账户上转了天文数字般的BEC币。
var value = web3.toBigNumber(’57896044618658097711785492504343953926634992332820282019728792003956564819968′);


另外,可能眼尖的朋友会留意到,为什么zhangsan账户上也有一笔BEC,那是因为智能合约上规定,创造者会拥有一些初始的Token,请见怪不怪。

(三)安全代码的作用
已经证明了整数溢出的危害性,那么该如何解决这个问题呢?其实BEC智能合约里面已经回答了这个问题,可以发现除了漏洞那行代码之外,其它运算都用了安全运算库里面的函数,那么试试吧。
更改BEC智能合约代码,把*改成库里面的mul函数,再重复上面的过程。
可以看到,这时已不能绕过检测,转账失败。


分析下这个Mul函数,代入具体数值说明一下:
假如:a = 2,b = 2^255
那么:c = a * b = 0
但: c / a = 0 != b
因此也就绕不过安全运算函数了。

四、整数溢出的原因
水轮车溢出之后归零,并重新开始盛水,是因为自身失衡所致。
那整数溢出的原理又是什么呢,当uint256的参数为2^256时,为什么会等于0,而不是继续等于2^256-1。
下面尝试用C++程序实验来解释:

从下图实验结果可以发现,当int参数赋给unsigned short参数之后,unsigned short只取后16的数值,而前面的数值一概不理,所以就会出现当输入为65536,赋给输出值后,产生溢出,只保留最后的0000,最终输出值为零。
另外,当输入为65537,-1时,按照溢出的规则,则输出也就自然为1、65535。

用OD调试这个C++程序,以进一步说明:
单步调试来到这里输入数值前,可以从堆栈发现输入值将会保存到 地址为0x004FF918的内存空间中。
于是输入65535,可以发现0x FFFF(65535)已经被存储到 0x004FF918的内存空间中。
接下来,程序会将该值用movzx指令赋给ecx寄存器,以实现C++中的output=input。

继续单步调试,可以看到ecx寄存器的寄存器值为0x FFFF,也就是说,当输入为65535,输出也为65535。

再来一次,这次输入65536,可以发现0x 0001 0000已经被存储到 地址为0x00CFF8CC的内存空间中。
接下来,程序会将该值用movzx指令赋给ecx寄存器。

单步调试,继续运行程序,可以看到ecx寄存器的寄存器值为0x 0000。也就是说当输入为65536时,输出为0。
所以关键在于:MOVZX指令。
这是汇编语言数据传送指令MOV的变体,无符号扩展并传送。
简单来说,movzx将源操作数取出,置于目的操作数,而目的操作数其余位用0填充。于是就出现了溢出时,只留下低位的16位数值。
五、写在最后
BEC智能合约也不是小众产品,但都能出现如此明显且致命的漏洞,可见币圈安全问题真的不会少。
想必大家都想着产品早日上线,早日盈利,所以基本的代码审计、系统测试这类基本的安全检查都能省则省,或者简单带过,带来的后果可想而知。这次的安全事件给区块链开发者和黑客们都提了醒,开发者得加大精力考虑代码安全,而黑客们则是将注意力从持币者身上转移到源码审计。
信息安全问题,归根结底是人与人之间的利益博弈,只要利益存在,攻与防、矛与盾就会一直持续。
希望这次的事件,能引起大家对信息安全的重视。
时刻牢记安全无小事,防微杜渐是关键!
BEC合约整数溢出漏洞还原与分析的更多相关文章
- CVE-2013-2551:Internet Explore VML COALineDashStyleArray 整数溢出漏洞简单调试分析
0x01 2013 Pwn2Own 黑客大赛 在 Pwn2Own 的黑客大赛上,来自法国的 VUPEN 安全团队再一次利用 0day 漏洞攻破 Windows8 环境下的 IE10 浏览器,这一次问题 ...
- Solidity合约中的整数溢出漏洞事件
事件 2018年4月23日 BEC 一夜被偷64亿 2018年4月25日 SMT 再爆类似漏洞,火币Pro和OKEx相继暂停了SMT交易 2018年4月25日 BEC.SMT现重大漏洞,这8个智能合约 ...
- CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天
CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天
- Nginx整数溢出漏洞 Writeup
Nginx整数溢出漏洞 CVE-2017-7529 题目URL:http://www.whalwl.site:8040/ 漏洞描述 在 Nginx 的 range filter 中存在整数溢出漏洞,可 ...
- Linux Kernel 整数溢出漏洞
漏洞名称: Linux Kernel 整数溢出漏洞 CNNVD编号: CNNVD-201311-062 发布时间: 2013-11-07 更新时间: 2013-11-07 危害等级: 漏洞类型: ...
- CVE-2012-0774:Adobe Reader TrueType 字体整数溢出漏洞调试分析
0x01 TrueType 字体 TTF 字体是 Apple 和 Microsoft 两家公司共同推出的字体格式,现在已经广泛的运用于 Windows 操作系统,其中 PDF 文档也可以嵌入 TTF ...
- gdk-pixbuf tiff_image_parse函数整数溢出漏洞
受影响系统:gdk-pixbuf gdk-pixbuf 2.36.6描述:CVE(CAN) ID: CVE-2017-2870 gdk-pixbuf是一个用于以各种格式加载图像和像素缓冲处理的库. 使 ...
- 美链BEC合约漏洞技术分析
这两天币圈链圈被美链BEC智能合约的漏洞导致代币价值几乎归零的事件刷遍朋友圈.这篇文章就来分析下BEC智能合约的漏洞 漏洞攻击交易 我们先来还原下攻击交易,这个交易可以在这个链接查询到. 我截图给大家 ...
- CVE2016-8863libupnp缓冲区溢出漏洞原理分析及Poc
1.libupnp问题分析: (1)问题简述: 根据客户给出的报告,通过设备安装的libupnp软件版本来判断,存在缓冲区溢出漏洞:CVE-2016-8863. (2)漏洞原理分析: 该漏洞发生在up ...
随机推荐
- Python中令人迷惑的4个引用
第一个:执行时机的差异 1. array = [1, 8, 15] g = (x for x in array if array.count(x) > 0) array = [2, 8, 22] ...
- PyQt5设置图片格式及动画
1.缩放图片'''使用QImage.Scale(width,height)方法可以来设置图片'''from PyQt5.QtCore import *from PyQt5.QtGui import * ...
- 吴裕雄 python 神经网络——TensorFlow训练神经网络:全模型
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data INPUT_NODE = 784 ...
- thymeleaf模板引擎简介
一:thymeleaf 学习笔记---http://www.blogjava.net/bjwulin/articles/395185.html thymeleaf是一个支持html原型的自然引擎,它在 ...
- 在Linux CentOS 下安装JDK 1.8
一首先去官网下载linux 版本JDK1.8 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-21331 ...
- 洛谷 P2925 [USACO08DEC]干草出售Hay For Sale
嗯... 题目链接:https://www.luogu.org/problemnew/show/P2925 这是一道简单的01背包问题,但是按照正常的01背包来做会TLE一个点,所以要加一个特判(见代 ...
- Hive的学习之路(理论篇)
一.Hive介绍 Apache官网给出的logo,一半是Hadoop大象的头,一半是蜜蜂的身体,也是寓意着它是基于Hadoop,哈哈,纯属个人理解,进入正题. Hive是基于Hadoop的一个数据仓库 ...
- Linux vim中方向键变成字母的问题
使用Ubuntu Desktop 18.04 时 发现 vim 在编辑模式的时候,方向键变成了字母ABCD. 原因: Ubuntu预装的是vim tiny版本,安装vim full版本即可解决. 1. ...
- 使用IDEA导入一个Maven风格的SSM项目
转自: 方法一: (我用的这种,导入的方法 File->New->Project from existing sources)(同理,important也是一样的) https://how ...
- 语言国际化:中文ASC码互转
https://javawind.net/tools/native2ascii.jsp 1.首先找到了上面的链接,也就是下图,输入中文就可立即得出ASCII码 2.看到上图第一条,找到了JDK/bin ...