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

时间: 2024-11-02 18:51:52

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

Linux kernel 4.20 BPF 整数溢出漏洞分析

分析的代码为linux-4.20-rc3版本:https://elixir.bootlin.com/linux/v4.20-rc3/source.因为该漏洞影响`Linux Kernel 4.20rc1-4.20rc4`,主要Linux发行版并不受其影响. 一.简介 BPF的全称是Berkeley Packet Filter,字面意思意味着它是从包过滤而来,该模块主要就是用于用户态定义数据包过滤方法:从本质上我们可以把它看作是一种内核代码注入的技术,BPF最大的好处是它提供了一种在不修改内核代码

PHP “shmop_read()”远程整数溢出漏洞

PHP是广泛使用的通用目的脚本语言,特别适合于Web开发,可嵌入到HTML中. PHP的"shmop_read()"函数在实现上存在远程整数溢出漏洞,远程攻击者可利用此漏洞在受影响网络服务器中执行任意代码,造成拒绝服务. 解决方法 前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载: http://www.php.net/downloads.php 原文地址:https://www.cnblogs.com/mrhonest/p/10892552.html

暂停交易?ERC20合约整数溢出安全漏洞案例技术分析(一)

区块链兄弟社区,区块链技术专业问答先行者,中国区块链技术爱好者聚集地 作者:吴寿鹤,<区块链开发实战--以太坊关键技术与案例分析>的第一作者,<区块链开发实战--Hyperledger Fabric关键技术与案例分析>联合作者,IONChain 离子链 首席架构师,hyperLedger核心项目开发人员,区块链技术社区-区块链兄弟联合创始人.github: https://github.com/gcc2ge 来源:区块链兄弟,国内第一家专注区块链技术分享实战的公益性媒体社区 原文链

整数溢出漏洞小结

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

Java运行时环境JPEGImageWriter.writeImage函数整数溢出漏洞_

在使用PDFBOX的接口,代码如下: PDFImageWriter imageWriter = new PDFImageWriter(); imageWriter.writeImage(pdDoc, imageType, null, startPage, endPage, imageFilePath, 1, Constants.NUM_TWO_HUNDRED),发现图片生成了,但是报内存溢出错误.后面看了下源代码搜寻相关资料发现存在这样一个问题,所以更换JDK就OK了. Java运行时环境的JP

CVE-2015-1530 ,CVE-2015-1474 两个android整数溢出漏洞 分析

An integer overflow in Android media could be exploited to get media_server permission(CVE-2015-1530) Integer overflow leading to heap corruption while unflattening GraphicBuffer In Android(CVE-2015-1474)

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

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

整数溢出实验

视频链接:  课程编写 类别 内容 实验课题名称 整数溢出实验 实验目的与要求 了解整数及整数溢出的基本概念 了解整数溢出的常见类型 掌握整数溢出的基本原理 通过编写代码,体验整数溢出 实验环境 VPC1(虚拟PC) Windows XP操作系统 软件描述 命令行窗口 实验代码 预备知识 1.整数及整数溢出 关于整数的概念,应该说我们在上中学的时候就学过了.这里我们需要了解的是:整数分为无符号和有符号两类,其中有负符号整数最高位为 1,正整数最高位为 0,无符号整数无此限制:此外,常见的整数类型

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

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