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智能合约也不是小众产品,但都能出现如此明显且致命的漏洞,可见币圈安全问题真的不会少。

想必大家都想着产品早日上线,早日盈利,所以基本的代码审计、系统测试这类基本的安全检查都能省则省,或者简单带过,带来的后果可想而知。这次的安全事件给区块链开发者和黑客们都提了醒,开发者得加大精力考虑代码安全,而黑客们则是将注意力从持币者身上转移到源码审计。

信息安全问题,归根结底是人与人之间的利益博弈,只要利益存在,攻与防、矛与盾就会一直持续。

希望这次的事件,能引起大家对信息安全的重视。

时刻牢记安全无小事,防微杜渐是关键!

原文地址:https://www.cnblogs.com/0daybug/p/12311126.html

时间: 02-15

BEC合约整数溢出漏洞还原与分析的相关文章

整数溢出漏洞小结

有无符号数是CPU架构决定的,是硬件特性直接反映到汇编指令中.C语言忠实的展现了汇编的特性. 无符号数比较: ja.jae.jb.jbe.je或jne 小于.小于等于.等于.不等于.大于或大于等于: 有符号数比较: 则使用jl.jle.je.jne.jg.jge指令 小于.小于等于.等于.不等于.大于或大于等于: 无符号:十六进制表示 有符号:补码表示 无符号与有符号转换: 基本原则:保证底层的位模式保持不变 导致的问题:有符号数赋给无符号数之后,会从-1变成4294967295(导致溢出) (

缓冲区溢出分析第11课:整数溢出的原理

<缓冲区溢出分析>这一系列的内容是我为"i春秋"(www.ichunqiu.com)所录制的同名视频课程的讲稿汇总.每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解.而本系列的内容也是从零开始,来给大家由浅入深地进行缓冲区溢出漏洞的讲解.整个课程是理论与实践相结合,每讲完几个基础理论后,都会配以实际的软件中的漏洞进行分析,以帮助大家更好地理解漏洞的原理.有兴趣的朋友可以结合本文与配套视频进行学习. 前言 我们之前所研究的漏洞,都是非常经典的栈溢出漏洞,也是最为常见

一个简单的远程溢出漏洞分析

人生第一个漏洞分析,好激动. 因为从来没有接触过漏洞分析方面,以前也只是看过一点书,所以一直想找个东西练练手,结果翻到了看雪Exploit me的题目,本来以为会很难,结果还是很基础的,适合我这样的新手练手. http://bbs.pediy.com/showthread.php?t=56998 进入正题 首先拿到了一个Windows程序,拖到IDA里打算看一下,结果发现程序逻辑出乎意料的简单.就是一个很常规的SOCKET流程带有一些错误处理. 下面详细说明. mov ebp,eax test

缓冲区溢出漏洞实验 20125108 冯相国

练习1 一.实验描述 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写. 二.实验准备 系统用户名shiyanlou,密码shiyanlou 实验楼提供的是64位Ubuntu linux,而本次实验为了方便观察汇编语句,我们需要在32位环境下作操作,因此实验之前需要做一些准备. 此过程消耗了不少时间,学校的网速实在太渣. 三.实验步骤

实验一:缓冲区溢出漏洞实验20115116黄婧

缓冲区溢出攻击:通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的. 一.实验要求 1.为了监控实验进程,我们采用私有课程方式,进入实验楼课程,单击加入私有课程,输入邀请码2YTE6J9X,个人信息中输入学号+姓名; 2.实验报告在博客园 发Blog公开,重点是实验过程中的运行结果(要有截图),遇到的问题.解决办法(不要是空洞的方法如“查网络”.“问同学”.“看书”等)以及分析(从中可以得到什么启示,有什么收获,教训

践踏堆栈-缓冲区溢出漏洞

践踏堆栈-缓冲区溢出漏洞 打算写这篇文章是因为在网上看过一篇论文,讲了缓冲区溢出破坏堆栈来执行恶意程序的漏洞.该论文请见参考资料1.这篇文章会涉及一些汇编的基础知识,以及虚拟内存的一些基本概念等.当然用来调试程序的系统是linux,工具是gcc.很久没有看过汇编和C语言了,错漏之处,还请指正. 1.概要 文章标题有提到堆栈和缓冲区,那么就先来探讨下这几个名词的定义.这里的缓冲区,指的就是计算机内一块连续的内存区域,可以保存相同数据类型的多个实例.C程序员最常见的缓冲区就是字符数组了.与C语言中其

Windows堆溢出漏洞简单总结

堆溢出漏洞的利用难度比栈溢出利用要大的多,每个不同的漏洞可能综合利用不同的技术.堆溢出漏洞的根源也是没有正确的使用非安全函数(这里是微软禁用的非安全函数),导致用户输入的数据覆盖掉其它内存地址.而我们的利用步骤也是通过覆盖可用内存数据,从而控制EIP.在这篇文章里

实验一——缓冲区溢出漏洞实验

Linux实验一 —— 缓冲区溢出漏洞实验 20122137 一.实验描述 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写. 二.实验准备 系统用户名shiyanlou,密码shiyanlou 实验楼提供的是64位Ubuntu linux,而本次实验为了方便观察汇编语句,我们需要在32位环境下作操作,因此实验之前需要做一些准备. 1.

实验一缓冲区溢出漏洞

一.实验描述 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写. 二.实验准备 系统用户名shiyanlou,密码shiyanlou 实验楼提供的是64位Ubuntu linux,而本次实验为了方便观察汇编语句,我们需要在32位环境下作操作,因此实验之前需要做一些准备. 1.输入命令安装一些用于编译32位C程序的东西: sudo apt

CVE-2010-2883Adobe Reader和Acrobat CoolType.dll栈缓冲区溢出漏洞分析

泉哥并没有给书中的那个exp,而且感觉有点扯,如果能直接在exp中看出shellcode那还分析什么.. 用的是另一个exp,加载Adobe Reader用windbg挂载,打开exp,发现程序异常如下.这里有个问题就是我每次调这个洞的时候虚拟机都会炸掉,别的洞就没事,我刚开始以为是我的调试器有问题或者什么的.后来发现原来这是一个堆喷的利用,我打开任务管理器看了一下,打开exp之前内存占用300MB左右,打开exp之后直接1.2GB,至于怎么发现是堆喷的后面会提到,总之不知道重启了多少次虚拟机T