首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >智能合约:整型溢出漏洞

智能合约:整型溢出漏洞

作者头像
yichen
发布2020-05-04 14:56:15
1.1K0
发布2020-05-04 14:56:15
举报

用书上的例子来介绍一下:

一个小朋友,他可以数着手指运算十以内的运算,比如 1+1=2,他可以用两个手指算出来,但是如果你问他 5+6 等于多少,他数完十个手指之后发现手指不够用了,就会把手指扳回来,说:结果为 1,对于小朋友来说,这个问题就超纲“溢出”了

在 solidity 中,当一个整型变量高于或者低于他所能承受的范围时,就会发生溢出,导致一些不可预期的情况出现。例如,当用户转账金额超过系统预设的最大值时,只要用户金额大于零,用户就可以直接将巨额的代币转走

代码片段

function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool){  uint cnt = _receivers.length;  uint 256 amount = uint256(cnt) * _value;  //出现了成发运算,且amount缺少溢出判断,因此存在溢出的可能  require(cnt > 0 && cnt <=20);  require(_value >0 && balances[msg.sender] >= amount);  //存在通过将amount溢出为0或极小值来绕过余额判断的可能  balances[msg.sender] = balances[msg.sender].sub(amount);  for(uint i = 0; i < cnt; i++){    balances[_receivers[i]] = balances[_receivers[i]].add(_value);    Transfer(msg.sender, _receivers[i], _value);  }  return true;}

这个函数的作用是:让用户同时向多个人转账。第一个参数 _receivers 为 Address 数组类型,代表接收者地址,也就是可以向一整个数组的人转账。第二个参数 _value 为转账金额。

这个函数的逻辑是:

获得数组的成员数(cnt),计算一共转多少钱(amount)

成员数要大于 0 且小于 20 ,然后转账的数值要大于 0 且要小于拥有的金额数才能继续

漏洞分析

uint 256 amount = uint256(cnt) * _value;

在上下文中,没有对 amount 进行溢出判断,如果攻击者将 amount 溢出为 0 或者其他很小的值就能绕过用于对账户余额的判断

require(_value >0 && balances[msg.sender] >= amount);

在代码中可以看到 转账的金额能够被 cnt 和 _value 所控制,所以我们可以操纵这俩数值,来达到目的

具体步骤如下:

  1. 创建两个地址,用于接收溢出转账
  2. 调用 batchTransfer() 函数,将 _receivers 设置为 uint256 的最大值 / 2 + 1
  3. 这样,当计算 amount = uint256 的最大值 + 1,就超过了 uint256 的最大范围,成功溢出为 0

奇数 115792089237316195423570985008687907853269984665640564039457584007913129639935 是 uint256 的最大值,如果把他除以二(刚好需要向下取整)然后加一,把这个作为 _value 的值,这样,再乘以一个 cnt 的值,得到的就刚好溢出

  1. 代码执行后,两个地址都会得到 _value 个 Token,也就是这两个账号会凭空增加 57896044618658097711785492504343953926634992332820282019728792003956564819968 个 Token(也就是 _value 值)

代码调试

去这里看一下:

https://cn.etherscan.com/address/0xc5d105e63711398af9bbff092d4b6769c82f793d

代码复制出来,然后拿到 remix IDE 里面去编译一下

然后在 Run 里面,选择 Environment 为 JavaScript VM,然后选择 BecToken 合约 点击 Deplay 进行部署

记录一下账户的地址:

1:0xca35b7d915458ef540ade6068dfe2f44e8fa733c(主账户)

2:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c

3:0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db

_receivers:["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"]_value:57896044618658097711785492504343953926634992332820282019728792003956564819968

然后点击 batchTransfer 的 transact

再用 balanceOf 看一下账户余额是不是变化了

一开始主账户的金额:

其他账户(以第二个为例)

转账之后第二个帐户的金额

再来看看第一个账户的金额,还是这样,这就说明我们复现成功了

规避整型溢出:SafeMath库

目前 solidity 还没有解决此问题,所以只能由各个合约自行完成整型溢出的判断

在任何时候,都不要在代码中直接使用 +、-、*、/ 来进行数学运算,而应使用 SafeMath 库

在 SafeMath 库中每个函数开头都用 语句进行了判断,对所有函数都进行了防溢出判断,可以有效地杜绝整型溢出问题

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈冠男的游戏人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码片段
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档