溢出原理

计算机中整数变量有上下界,如果在算术运算中出现越界,即超出整数类型的最大表示范围,数字便会如表盘上的时针从12到1一般,由一个极大值变为一个极小值或直接归零,此类越界的情形在传统的软件程序中很常见,但是否存在安全隐患取决于程序上下文,部分溢出是良性的(如tcp序号等),甚至是故意引入的(例如用作hash运算等)。

以太坊虚拟机(EVM)为整数指定固定大小的数据类型,这意味着一个整型变量只能有一定范围的数字表示,例如:一个uint8只能存储在范围[0,255]的数字,若试图存储256到一个uint8将变成0,不加注意的话,只要没有检查用户输入又执行计算,导致数字超出存储它们的数据类型允许的范围。

溢出实例

高卖低收(CVE-2018-11811)

类型描述:管理员通过修改合约中的参数来制造溢出漏洞,导致用户提币转出token之后,却收不到ETH(或收到极少量ETH),造成用户经济损失。

漏洞实例:合约Internet Node Token (INT)

合约地址:

https://cn.etherscan.com/address/0x0b76544f6c413a555f309bf76260d1e02377c02a#code

漏洞位置:L179

漏洞危害:用户提币之后,无法得到对应数额的ETH;

漏洞原理:sellPrice被修改为精心构造的大数后,可导致amount sellPrice的结果大于整数变量(uint256)最大值,发生整数溢出,从而变为一个极小值甚至归零。该值在程序语义中是用于计算用户提币应得的ETH数量,并在L179进行了校验,但该值被溢出变为极小值之后可以逃逸L179的校验,并导致用户售出token后只能拿到少量的(甚至没有)ETH。

下溢增持(CVE-2018-11687)

类型描述:管理员在特定条件下,通过调用合约中有漏洞的发币函数制造下溢,从而实现对自身账户余额的任意增加。

漏洞实例:合约Bitcoin Red(BTCR)

合约地址:

https://cn.etherscan.com/address/0x6aac8cb9861e42bf8259f5abdc6ae3ae89909e11#code

漏洞位置:L45

漏洞危害:管理员执行了一个正常向某个地址进行发币的操作,实际已经暗中将自身账户的余额修改为了一个极大的数;

漏洞原理:distributeBTR()函数的本意是管理员给指定地址发放一定数额的token,并从自身账户减少对应的token数量。减少管理员账户余额的操作为balances[owner] -= 2000 108 ,运算的结果将被存到balances[owner]中,是一个无符号整数类型。当管理员余额本身少于2000 * 108时,减法计算结果为负值,解释为无符号整数即一个极大值。

随意铸币 (CVE-2018-11812)

类型描述:管理员调用铸币函数给某个地址增加token时,利用溢出漏洞可以突破该函数只能增加token的限制,实际减少该地址的token数量,从而实现对任一账户余额的任意篡改(增加或减少)。

漏洞实例:合约PolyAi (AI)

合约地址:
https://cn.etherscan.com/address/0x5121e348e897daef1eef23959ab290e5557cf274#code

漏洞位置:L136

漏洞危害:管理员可以绕过合约限制,任意篡改所有地址的token余额;

漏洞原理:攻击者通过构造一个极大的mintedAmount,使得balanceOf[target] + mintedAmount发生整数溢出,计算结果变为一个极小值。

超额铸币(CVE-2018-11809)

类型描述:管理员通过构造恶意参数,可以绕过程序中规定的token发行上限,实现超额铸币。合约Playkey (PKT)存在此类漏洞,导致合约中的铸币上限形同虚设,从而发行任意多的token。此外,我们还发现Nexxus (NXX)、Fujinto (NTO)两个合约存在类似漏洞,这两个合约没有铸币上限限制,但同样的手段,可以溢出合约中一个用于记录已发币总量(totalSupply)的变量值,使其与市场中实际流通的总币数不一致。

漏洞实例:合约Playkey (PKT)

合约地址:
https://cn.etherscan.com/address/0x2604fa406be957e542beb89e6754fcde6815e83f#code

漏洞位置:红色标注的行L241

漏洞危害:管理员可以篡改已发币总量(totalSupply)为任意值,并绕过合约中的铸币上限超额发行token;

漏洞原理:_value在函数调用时被设置为精心构造的极大值,使得totalSupply + _value计算结果溢出后小于tokenLimit,从而轻易绕过L237行的铸币上限检测。

超额分配(CVE-2018-11810)

类型描述:管理员通过制造溢出来绕过合约中对单地址发币的最大上限,可以对指定地址分配超额的token,使得对单地址的发布上限无效。

漏洞实例:合约LGO (LGO)

合约地址:
https://cn.etherscan.com/address/0x123ab195dd38b1b40510d467a6a359b201af056f#code

漏洞位置:红色标注的行L286

漏洞危害:管理员绕过合约中规定的单地址发币上限,给指定地址分配超额的token;

漏洞原理:一个极大的_amount可以使得算数加法运算holdersAllocatedAmount + _amount发生整数溢出,变为一个极小值,从而绕过L286的检测。

超额购币(CVE-2018-11809)

漏洞描述:买家如果拥有足够多的ETH,可以通过发送大量token制造溢出,从而绕过ICO发币上限,达到超额购币。

漏洞实例:合约EthLend (LEND)

合约地址:
https://cn.etherscan.com/address/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03#code

漏洞位置:红色标注的行L236

漏洞危害:调用者绕过合约中规定ICO的token容量上限,获得了超额购币;

漏洞原理:一个极大的_newTokens可以使得算数加法运算totalSoldTokens + newTokens发生整数溢出,变为一个极小值,从而绕过L236的检测。

溢出调试

减法溢出

案例代码如下:


pragma solidity ^0.4.22;contract TokenExample {address public owner;mapping(address => uint256) public balances;mapping(address =>mapping(address =>uint256)) public allowed;event Transfer(address _from,address _to,uint256 _value);modifier onlyOwner{require(msg.sender == owner);_;}constructor() public {owner = msg.sender;balances[owner] = 2000*10**8;}function distribute(address[] addresses) public onlyOwner{for(uint i=0;i < addresses.length;i++){balances[owner] -= 2000*10**8;balances[addresses[i]] +=2000*10**8;emit Transfer(owner,addresses[i],2000*10**8);}}
}

如上图所示,在智能合约中的distribute函数的功能是从owner账户向指定的地址列表传入代币,但是在对balance[owner]的账户做减法运算的时候,未使用SafeMath函数进行数值运算操作,而且也没有判断合约的owner是否有足够的代币,直接一个循环对owner进行减法处理,这里如果转出的代币总量大于owner账户余额,那么balance[owner]将会发生下溢,变成一个极大的值,下面在remix中演示操作一下:

编译合约

部署合约

下溢操作

调用distribute函数传入地址数组:["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"],使用owner分别向这两个地址发送代币。执行之前owner的余额:

执行distribute函数:

交易日志记录如下:

执行之后owner的余额:

可以从上面的结果当中看到合约的owner在执行完distribute函数之后,按理来说转账操作应该会使得合约的owner的代币减少,但是这里去不减反增了,故这里的“下溢”确实存在。

加法溢出 

案例:GEMCHAIN

合约地址:
https://cn.etherscan.com/address/0xfb340423dfac531b801d7586c98fe31e12a32f31#code

如上上图所示,该智能合约中的mintToken函数用于增发代币,但是在增发代币的过程中对于加法操作没有使用SafeMath函数进行数值运算操作,而且也没有使用require对是否发生溢出进行检查,故这里存在溢出风险,如果合约的owner给target增发较多数量的mintedAmount那么将会导致溢出问题的发生。使用remix演示如下:

编译合约

部署合约

上溢操作

第一次铸币:

首先,我们调用mintToken函数向地址"0x14723a09acff6d2a60dcdf7aa4aff308fddc160c"铸币,铸币数量"0x8000000000000000000000000000000000000000000000000000000000000000”即2的255次方

交易日志:

铸币之后地址“0x14723a09acff6d2a60dcdf7aa4aff308fddc160c”的余额为:

为了让其发生溢出,我们还需要向地址“0x14723a09acff6d2a60dcdf7aa4aff308fddc160c”铸币,铸币的数量仍然为:“0x8000000000000000000000000000000000000000000000000000000000000000”即2的255次方,目的就是为了让2的255次方+2的255次方发生溢出,超出uint256的最大范围。下面具体看操作

第二次铸币:

交易日志:

查看余额:

从上面的结果我们可以发现确实发生了溢出!可想而知,如果合约的owner在不校验溢出问题的情况下向某一地址铸币,那么该地址如果发生溢出,那么代币数量将会发生变化,时而出现减少的情况(因为发生溢出)。

特殊情况

有时候你会发现虽然我们看到一个合约当中有整型溢出的风险,例如在transfer函数中未使用require进行溢出校验,同时也没有使用SafeMath函数进行数值运算防护的情形,但是该合约当中已经规定了token的总量(totalSupply),而且没有铸币函数(mintToken)另外增加代币,那么合约总体来说是安全的,不存在整型溢出,为什么这样说呢?因为你永远都不会发生两个数值相加超过uint256的情况,但是在这中情况下你就应该将目光放到“乘法溢出”或“减法下溢”的问题上来进行查找,审计是否真的不存在“整型溢出”问题。

溢出防御

那么如何防范这种整型溢出问题呢?官方给出的建议是使用OpenZepplin提供的SafeMath函数库进行数值运算操作,使用SafeMath库函数可以有效的对溢出问题进行检查与防范,SafeMath函数库源代码如下:

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SignedSafeMath.sol


// SPDX-License-Identifier: MITpragma solidity ^0.8.0;/*** @title SignedSafeMath* @dev Signed math operations that revert on error.*/
library SignedSafeMath {/*** @dev Returns the multiplication of two signed integers, reverting on* overflow.** Counterpart to Solidity's `*` operator.** Requirements:** - Multiplication cannot overflow.*/function mul(int256 a, int256 b) internal pure returns (int256) {return a * b;}/*** @dev Returns the integer division of two signed integers. Reverts on* division by zero. The result is rounded towards zero.** Counterpart to Solidity's `/` operator.** Requirements:** - The divisor cannot be zero.*/function div(int256 a, int256 b) internal pure returns (int256) {return a / b;}/*** @dev Returns the subtraction of two signed integers, reverting on* overflow.** Counterpart to Solidity's `-` operator.** Requirements:** - Subtraction cannot overflow.*/function sub(int256 a, int256 b) internal pure returns (int256) {return a - b;}/*** @dev Returns the addition of two signed integers, reverting on* overflow.** Counterpart to Solidity's `+` operator.** Requirements:** - Addition cannot overflow.*/function add(int256 a, int256 b) internal pure returns (int256) {return a + b;}
}

应用了SafeMath函数的智能合约实例:

https://etherscan.io/address/0xB8c77482e45F1F44dE1745F52C74426C631bDD52#code

可以看到在上面的智能合约当中对于数值运算都使用了SafeMath函数进行操作,而且也使用了require对溢出校验进行防护,总体较为安全。

溢出列表

https://github.com/sec-bit/awesome-buggy-erc20-tokens/blob/59e167f74a8d7cf48eadf25a75c65e461450aea0/raw/totalsupply-overflow.txt

https://github.com/peckshield/vuln_disclosure/blob/7a1e99695945220f4bbc10100f72fa7ecb9e0a79/tradeTrap.csv

https://github.com/BlockChainsSecurity/EtherTokens/blob/6e1e0952bc2a4b213cdc6db6ba7a855d9c776242/GEMCHAIN/mint%20integer%20overflow.md

https://github.com/dwfault/AirTokens/blob/aff7102887096a6c8d384820835818f445f3401f/Link_Platform__LNK_/mint%20integer%20overflow.md

文末总结

整型溢出问题发生的根源还是在于合约的开发者在开发合约时未考虑到“整型溢出”问题,作为审计人员的我们在看到合约时也要保持清醒,对于存在疑惑的地方应该采用“调试、验证”的方法去排除疑虑,而且在审计的过程中也要十分的认真、细心才可以,不要放过任何一个有可能存在问题的地方,例如修饰器/修饰词对应的权限问题、逻辑处理问题等等。

参考链接

https://www.anquanke.com/post/id/147913

智能合约审计之整形溢出攻击相关推荐

  1. 区块链100讲:智能合约审计指南

    智能合约代码的审计,目前还不是技术社区内经常会讨论的主题.今年3月6日,发表在博客网站[Schneier on Security]上的一篇博客(原文链接:[https://www.schneier.c ...

  2. 【智能合约系列009-如何做智能合约审计?】

    研究报告[Finding The Greedy, Prodigal, and Suicidal Contracts at Scale])指出,目前在以太坊中,有89%的智能合约代码都或多或少存在安全漏 ...

  3. 智能合约审计之条件竞争

    文章前言 与大多数区块链一样,以太坊节点汇集交易并将其形成块,一旦矿工解决了共识机制(目前Ethereum的ETHASH PoW),这些交易就被认为是有效的,解决该区块的矿工也会选择来自该矿池的哪些交 ...

  4. 智能合约审计之DDOS概述

    拒绝服务(DOS) 对智能合约进行DOS攻击的方法有很多种,其根本的目的是使合约在一段时间内或者永久无法正常运行,通过拒绝服务攻击,也可以使合约中的ether永远无法提取出来,下面将会列出几种常见的攻 ...

  5. 区块链安全100问 | 第七篇:智能合约审计流程及审计内容

    零时科技--专注于区块链安全领域 深圳零时科技有限公司(简称:零时科技),公司成立于2018年11月,是一家专注于区块链生态安全的实战创新型网络安全企业,团队扎根区块链安全与应用技术研究,以丰富的安全 ...

  6. 智能合约审计之访问控制

    基础知识 权限的概念 权限是指为了保证职责的有效履行,任职者必须具备的对某事项进行决策的范围和程度.它常常用"具有xxxxxxx的权利"来进行表达,比如:公司的CEO具有否定某项提 ...

  7. 智能合约审计之权限校验错误

    Tx.origin鉴权 简单介绍 tx.origin是Solidity的一个全局变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址,在智能合约中使用此变量进行身份验证可能会使合约受到类似网 ...

  8. 编写区块链游戏学智能合约 教程2:僵尸攻击人类

    该教程来自 CryptoZombies 网址:https://cryptozombies.io/zh/course/ CryptoZombies 是个在编游戏的过程中学习 Solidity 智能协议语 ...

  9. 智能合约审计之evilReflex攻击

    文章前言 在这篇文章中,我们对曾经出现过的一种叫做evilReflex的安全漏洞进行分析研究,攻击者可以通过该漏洞将存在evilReflex漏洞的合约中的任意数量的token转移到任意地址. 漏洞分析 ...

  10. 【智能合约审计】————23、EthLendToken

    合约地址:https://etherscan.io/address/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03#code pragma solidity ^0 ...

最新文章

  1. 浅谈批处理获取管理员运行权限的几种方法
  2. 【CSS进阶】CSS 颜色体系详解
  3. deeplearning URL
  4. wallpaper怎么设置锁屏_Apple ID密码忘了怎么找回?丨如何让面容和指纹解锁立马失效?...
  5. Lync2013 升级错误总结8 Lync2013 日志总是提示进程 RtcHost(5724) 收到了一个无效的客户端证书...
  6. json解析库go-simplejson使用
  7. mysql 全局排序 取第n条_MySQL分组排序取前N条记录 以及 生成自动数字序列 的SQL...
  8. 整理一年中使用到的工具和类库,可能正有你需要的!
  9. mysql数据库接收不了中文乱码_mysql数据库 中文乱码
  10. 在Word中自定义多级列表样式
  11. 计算机网络之物联网的特点及其主要应用领域
  12. vue中控制浏览器滚动
  13. 单片机能做什么创意小发明?新手怎么制作单片机智能小车?
  14. 马斯克为什么这么厉害?底层有一套强大的思维方式!
  15. MySQL——MySQL的flush
  16. 玄学小问题之input.focus()没生效的问题
  17. 技术员常用的工具晨枫U盘维护工具V2.0版
  18. 微信第一个小程序----------EGO微商小程序部署搭建
  19. 大数据技术解决 征信环节中产生的问题
  20. 高德地图放大Marker icon

热门文章

  1. 消息队列的介绍及配置
  2. python读书心得体会_终于领会学习python的心得体会
  3. mysql myd文件过大_Mysql数据文件大小超过4G了如何缩小优化
  4. 编码:隐匿在计算机软硬件背后的语言
  5. Java 11 发布线路图:有哪些值得期待的新特性?
  6. P2339 [USACO04OPEN]Turning in Homework G 笔记/题解
  7. CentOS7部署YApi
  8. window 脚本文件.bat获取最高权限拷贝文件及c++调用.bat文件示例
  9. python图片转换成文字_在python中将图像转换为字节文字 - python
  10. 打开chm文件c语言,chm格式,手把手教你chm文件怎么打开