整型溢出漏洞

160 阅读1分钟

整型溢出漏洞

简介

在solidity中(当然这不只是solidity语言独有的问题其他语言也有),当一个整型变量高于或者低于他所能承受的范围时,就会发生溢出,导致一些不可预期的情况出现。比如:用户转账金额超过预设的最大值时,那就会出现溢出,从而绕过代码中的判断,并且只要用户金额大于零,用户就可以直接将巨额的代币转走。

代码案例

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

代码解析

函数的作用是:让用户向多人转账。

  • 第一个参数 _receiver 为 Address 数组类型,代表接收者地址,也就是向谁转账。
  • 第一个参数 _value uint256类型,代表转账金额。

函数的逻辑:

  1. 先进行判断 address地址数组不能是0地址。
  2. 转账金额不能是0,并且该代币需要转的总量len * _value,判断用户余额需要大于转账金额。
  3. 修改更新合约的存储信息和进行转账。

漏洞分析

主要问题代码:

uint 256 amount = len * _value;  

这一句没有进行溢出判断,如果攻击者将 amount 溢出为 0 或者其他很小的值就能绕过对账户余额的判断条件。

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

这个代码通过判断_value和amount来控制,只要绕过这个判断就能达到转账的目的,这就可以利用len * _value 溢出。

如何计算溢出

只需要计算 amount = (uint256 的最大值 + 1)

uint256的最大值

奇数: 115792089237316195423570985008687907853269984665640564039457584007913129639935

刚好溢出的计算

(uint256 的最大值 / 2) + 1

如果把uint256的最大值除以二(刚好需要向下取整)然后加 1,把这个作为 _value 的值,这样,再乘以一个 len (len大于2) 的值,得到的就刚好溢出。

Remix测试地址: remix.ethereum.org/