整型溢出漏洞
简介
在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类型,代表转账金额。
函数的逻辑:
- 先进行判断 address地址数组不能是0地址。
- 转账金额不能是0,并且该代币需要转的总量
len * _value,判断用户余额需要大于转账金额。 - 修改更新合约的存储信息和进行转账。
漏洞分析
主要问题代码:
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/