首页
社区
课程
招聘
智能合约-整形溢出漏洞 笔记
发表于: 2020-4-29 11:42 21273

智能合约-整形溢出漏洞 笔记

2020-4-29 11:42
21273

在商城换的那本《智能合约安全分析和审计指南》的笔记

整型溢出漏洞

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

一个小朋友,他可以数着手指运算十以内的运算,比如 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 的值,得到的就刚好溢出

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

代码调试

这里 看一下

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


image.png


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


image.png


记录一下账户的地址:

1:0xca35b7d915458ef540ade6068dfe2f44e8fa733c(主账户)

2:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c

3:0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db


image.png


_receivers:

["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"]

_value:

57896044618658097711785492504343953926634992332820282019728792003956564819968


然后点击 batchTransfer 的 transact

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


一开始主账户的金额:


image.png


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


image.png


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


image.png


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


image.png


规避整型溢出:SafeMath库

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

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

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


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//