以太坊智能合约漏洞实战详解:整数溢出攻击

x01 溢出攻击事件

2018年4月22日,黑客对BEC智能合约发起攻击,凭空取出


个BEC代币并在市场上进行抛售,BEC随即急剧贬值,价值几乎为0,该市场瞬间土崩瓦解。

2018年4月25日,SMT项目方发现其交易存在异常,黑客利用其函数漏洞创造了


的SMT币,火币Pro随即暂停了所有币种的充值提取业务。

2018年12月27日,以太坊智能合约Fountain(FNT)出现整数溢出漏洞,黑客利用其函数漏洞创造了


的SMT币。

让我们一起以沉痛的心情缅怀以上一夜归零的代币。


0x02 整数溢出简介

整数溢出原理

通常来说,在编程语言里由算数问题导致的整数溢出漏洞屡见不鲜,在区块链的世界里,智能合约的Solidity语言中也存在整数溢出问题,整数溢出一般分为又分为上溢和下溢,在智能合约中出现整数溢出的类型包括三种:

乘法溢出

加法溢出

减法溢出

在Solidity语言中,变量支持的整数类型步长以8递增,支持从uint8到uint256,以及int8到int256。例如,一个 uint8类型 ,只能存储在范围 0到2^8-1,也就是[0,255] 的数字,一个 uint256类型 ,只能存储在范围 0到2^256-1的数字。

在以太坊虚拟机(EVM)中为整数指定固定大小的数据类型,而且是无符号的,这意味着在以太坊虚拟机中一个整型变量只能有一定范围的数字表示,不能超过这个制定的范围。

如果试图存储 256这个数字 到一个 uint8类型中,这个256数字最终将变成 0,所以整数溢出的原理其实很简单,为了说明整数溢出原理,这里以 8 (uint8)位无符整型为例,8 位整型可表示的范围为 [0, 255],255 在内存中存储按位存储的形式为下图所示:

8 位无符整数 255 在内存中占据了 8bit 位置,若再加上 1 整体会因为进位而导致整体翻转为 0,最后导致原有的 8bit 表示的整数变为 0。

上图即说明了智能合约中整数上溢的原理,同样整数下溢也是一样,如 (uint8)0 - 1 = (uint8)255。

溢出简单实例演示,这里以uint256类型演示:


将上述代码在编辑器上编译,部署。这里为了方便起见使用:http://remix.ethereum.org/#optimize=false&version=soljson-v0.4.25+commit.59dbf8f1.js编辑器。

加法溢出

这里将uint256 类型的max变量设置为它的最大值(2**256 - 1),然后在给max变量加上1,导致上溢,最终结果max的值输出为0。

减法溢出

这里将uint256 类型的变量min设置为它的最小值(0),如果在减去一个1,导致下溢,最后min的值便会变成一个很大的值,即2**256-1,也就是uin256类型的最大值。

乘法溢出

这里将uint256 类型的mul变量设置为2**255,然后在给mul变量乘以2,变成2**256,超过最大值导致上溢,最终结果mul的值输出为0。

通过例子我们可以看到uint256当取最大整数值,上溢之后直接返回值为0,uint256当取0下溢之后直接返回值为2^256-1。这是solidity中整数溢出场景的常规情况,其他类型如uint8等也是一样的原理。

整数溢出防护

为了防止整数溢出的发生,一方面可以在算术逻辑前后进行验证,另一方面可以直接使用 OpenZeppelin 维护的一套智能合约函数库中的 SafeMath 来处理算术逻辑。

使用Safemath方法后,不再产生溢出漏洞:

通过使用Safemath封装的加法,减法,乘法接口,使用assert方法进行判断,可以避免产生溢出漏洞。

0x03 案例分析

而上面的例子介绍了原理。下面我们将通过实际案例介绍溢出漏洞是如何在生产环境中进行恶意攻击的。

案例一 BEC

漏洞原理分析

BEC合约地址:0xC5d105E63711398aF9bbff092d4B6769C82F793D

在etherscan上的地址为:https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d#code

存在溢出漏洞的合约代码如下:

可以看到batchTransfer函数中,有如下代码uint256 amount = uint256(cnt) * _value,没有使用safemath library,直接使用乘法运算符,产生整数溢出的地方就在合理。

其中变量cnt为转账的地址数量,可以通过外界的用户输入_receivers进行控制,_value为单地址转账金额,也可以直接进行控制。

外界可以控制_receivers和_value的数值,那么我们就可以控制amount变量的值,让其产生非预期的值,导致向上溢出。如cnt = _receivers.length = 2,_value = 2**255,这样amount = uint256(cnt) * _value = 2**255*2超过uint256表示的最大值,导致溢出,最终amount = 0。

紧接着下面有一句对amount进行条件检查的代码require(_value > 0 && balances[msg.sender] >= amount);其中balances[msg.sender]代表当前用户的余额,amount代表要转的总币数。代码意思为确保单用户转账金额大于0,并且当前用户拥有的代币余额大于等于本次转账的总币数才进行后续转账操作。因为amount溢出后可以为一个很小的数字或者0(这里变成0),很容易绕过balances[msg.sender] >= amount的检查代码。从而产生巨大_value数额(这里为2**255)的恶意转账。

实际攻击的恶意转账记录:https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f

从转账记录中可以看到分别给两个账户转了很大一笔代币。

攻击模拟演示

为了真实模拟黑客攻击过程,下面进行演示。

实战操作视频链接:https://v.qq.com/x/page/w082246wiuo.html

下面我们将BEC合约的代码在remix编辑器编译,部署,然后调用batchTransfer函数。

转入两个需要转账的账户:["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"]

然后转入单用户转账金额57896044618658097711785492504343953926634992332820282019728792003956564819968(2**255)

最后转账后,转出账户:0xca35b7d915458ef540ade6068dfe2f44e8fa733c代币不变,转入的两个账户余额都变为很大的值。

漏洞修复

在代码中可以看到,在语句balances[msg.sender] = balances[msg.sender].sub(amount)和balances[_receivers[i]] = balances[_receivers[i]].add(_value)中,调用Safemath库中的安全函数来完成加减操作,所以这里在进行乘法运算时也需要使用Safemath来运算,将代码uint256 amount = uint256(cnt) * _value改为uint256 amount = uint256(cnt).mul(_value);,如图所示,使用Safemath库之后,攻击的转账失败。

案例二 SMT

BEC合约地址:0x55F93985431Fc9304077687a35A1BA103dC1e081

在etherscan上的地址为:https://etherscan.io/address/0x55f93985431fc9304077687a35a1ba103dc1e081#code

存在溢出漏洞的合约代码如下:


从代码中可以看到这里的加、减、乘都没有使用安全处理,直接进行算数运算,而且_from, _to, _value, _feeSmt都可控,导致在if(balances[_from] < _feeSmt + _value) revert();这里, 将_value, _feeSmt构造为相加刚好溢出为0的值


如上面的value和feeSmt相加然后去掉最高位,结果为0,小于balances[_frome],使得if条件不成立,绕过判断。

通过攻击的交易信息中可以看到,黑客构造的内容:

上图可以看出,_value和_feeSmt相加刚好溢出为0,绕过if条件判断,从而给to账户和msg.sender账户分别转入value和feeSmt的代币。

案例三 FNT

FNT合约地址:0x82Cf44bE0768A3600c4BDeA58607783A3A7c51AE

在etherscan上的地址为:https://etherscan.io/address/0x82cf44be0768a3600c4bdea58607783a3a7c51ae#code

此漏洞跟案例一的BEC漏洞,如出一辙。存在溢出漏洞的合约代码如下:

从上述代码中可以看到变量receivers和amounts可控,首先计算需要转出的全部数额totalAmount,然后判断totalAmount大于0,并且msg.senders的余额大于totalAmount的数值,最后从receivers和amounts中一一转账。

这里的溢出点在计算转账总额的地方totalAmount += amounts[i];,当amount的元素相加产生溢出时,可以构造溢出时为一个很小的值比如1或者2,构造的amounts如下:

所以通过上面的构造,我们成功绕过了判断,最后给两个账户分别转了2个代币和115792089237316195423570985008687907853269984665640564039457584007913129639935个(2*256-1)的代币。

通过攻击的交易信息可以看到结果跟我们分析的一致:

0x04 总结

我们在开发智能合约时,开发人员如果不加注意的话,只要没有检查用户输入内容,而且最终将输入带入执行计算,导致计算结果数字超出存储它们的数据类型允许的范围,那么此智能合约的输入内容就可以被用来组织攻击,导致安全漏洞。

所以为了防止整数溢出的发生,一方面可以在算术逻辑前后进行验证,另一方面可以直接使用 OpenZeppelin 维护的一套智能合约函数库中的 SafeMath 来处理算术逻辑。

很多项目在合约中已经导入了SafeMath库,但是因为开发者的粗心大意导致部分运算忘记添加本来已经using的safemath库,出现溢出漏洞。所以除了开发者自己提高安全开发意识之外,找专业的安全团队对合约进行全面的审计也是非常必要的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272

推荐阅读更多精彩内容

  • 最近,智能合约漏洞很火。 让我们再来看一下4月22日BeautyChain(BEC)的智能合约中一个毁灭性的漏洞。...
    wind_飘阅读 536评论 0 1
  • 最近一段时间,关于智能合约漏洞的问题层出不穷。我将以SMT为蓝本进行复盘,通过复盘去深层次了解和防止类似事件发生。...
    廖全磊LesterLiao阅读 4,332评论 0 4
  • 两个人散完步,于亮亮打车送方丽娜回家。 到了楼底下,两个人好像都有点儿不舍。小区里静悄悄的,不远处有几位大爷大妈坐...
    上官飞鸿阅读 895评论 10 35
  • 我现在只能这么安慰自己:天将降大任于斯人,必先苦其心智,劳其筋骨,饿其体肤,空乏其身,行佛乱其所为! 人到中年,还...
    包小凡阅读 195评论 0 0
  • 细讲张国荣的童年-出道经历。再送给大家极少人知道的张国荣早期冷门歌!下面的歌,点击歌名就会直接跳转到播放器听。 1...
    混子姐阅读 1,406评论 0 4