科普 | 智能合约安全审计入门篇 —— 溢出漏洞

By:小白@慢雾安全团队

背景概述

上周写了智能合约安全审计入门篇——重入漏洞,这次我们接着来说一个同样很经典的漏洞——?溢出漏洞。

前置知识

首先我们还是先来看看溢出是什么:

算术溢出或简称为溢出分为两种:上溢和下溢。所谓上溢是指在运行单项数值计算时,当计算产生出来的结果非常大,大于寄存器或存储器所能存储或表示的能力限制就会产生上溢,例如在solidity中,uint8所能表示的范围是0-255这256个数,当使用uint8类型在实际运算中计算255+1是会出现上溢的,这样计算出来的结果为0也就是uint8类型可表示的最小值。同样的,下溢就是当计算产生出来的结果非常小,小于寄存器或存储器所能存储或表示的能力限制就会产生下溢。例如在Solidity中,当使用uint8类型计算0-1时就会产生下溢,这样计算出来的值为255也就是uint8类型可表示的最大值。

如果一个合约有溢出漏洞的话会导致计算的实际结果和预期的结果产生非常大的差异,这样轻则会影响合约的正常逻辑,重则会导致合约中的资金丢失。但是溢出漏洞是存在版本限制的,在Solidity<0

functionincreaseLockTime(uint_secondsToIncrease)public{lockTime+=_secondsToIncrease;}

functionwithdraw()public{require(balances>0,"Insufficientfunds");require(block

}

声音 | 上海股交所总经理:区块链想要大规模发展要做好社会科普工作:金色财经报道,上海股交所总经理张云峰表示,区块链当前还处于一个“概念”的阶段,距离成熟应用,影响到百姓的日常生活还有很长的路要走。对于“区块链”和其会带来的社会和经济效果,沈阳应当持审慎的态度。区块链想要大规模发展,一方面要做好这项复杂技术的社会科普工作,加快社会大众对区块链的了解。另一方面,要充分发挥市场的作用,让企业用实实在在的技术创新,赋能实体经济的发展。[2019/11/17]

漏洞分析

我们可以看到,TimeLock合约充当了时间保险库。用户可以将代币通过deposit函数存入该合约并锁定,且至少一周内不能提现。当然用户也可以通过increaseLockTime函数来增加存储时间,用户在设定的存储期限到期前是无法提取TimeLock合约中锁定的代币的。首先我们发现这个合约中的increaseLockTime函数和deposit函数具有运算功能,并且合约支持的版本是:0

fallback()externalpayable{}

functionattack()publicpayable{timeLock

}

这里我们将使用Attack攻击合约先存入以太后利用合约的溢出漏洞在存储未到期的情况下提取我们在刚刚TimeLock合约中存入并锁定的以太:

1.首先部署TimeLock合约;

2.再部署Attack合约并在构造函数中传入TimeLock合约的地址;

3.调用Attack.attack函数,Attack.attack又调用TimeLock.deposit函数向TimeLock合约中存入一个以太,之后Attack.attack又调用TimeLock.increaseLockTime函数并传入uint类型可表示的最大值加1再减去当前TimeLock合约中记录的锁定时间。此时TimeLock.increaseLockTime函数中的lockTime的计算结果为2^256这个值,在uint256类型中2^256这个数存在上溢所以计算结果为2^256=0此时我们刚刚存入TimeLock合约中的一个以太的锁定时间就变为0;

4.这时Attack.attack再调用TimeLock.withdraw函数将成功通过block.timestamp>lockTime这项检查让我们能够在存储时间未到期的情况下成功提前取出我们刚刚在TimeLock合约中存入并锁定的那个以太。

下面是攻击流程图:

修复建议

到这里相信大家对溢出漏洞都有自己的理解了,那么下面我们就以开发者和审计者的角度来分析如何预防溢出漏洞和如何快速找出溢出漏洞:

作为开发者

1.使用SafeMath来防止溢出;

2.使用Solidity0.8及以上版本来开发合约并慎用unchecked因为在unchecked修饰的代码块里面是不会对参数进行溢出检查的;

3.需要慎用变量类型强制转换,例如将uint256类型的参数强转为uint8类型由于两种类型的取值范围不同也可能会导致溢出。

作为审计者

1.首先查看合约版本是否在Solidity0.8版本以下或者是否存在unchecked修饰的代码块,如果存在则优先检查参数的溢出可能并确定影响范围;

2.如果合约版本在Solidity0.8版本以下则需要查看合约是否引用了SafeMath;

3.如果使用了SafeMath我们需要注意合约中有没有强制类型转换,如果有的话则可能会存在溢出的风险;

4.如果没有使用SafeMath且合约中存在算术运算的我们就可以认为这个合约是可能存在溢出风险的,在实际审计中还要结合实际代码来看。

郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。

银河链

[0:15ms0-2:17ms