智能合约安全漏洞(2)——溢出漏洞

首先要说明,整型溢出漏洞在Solidity的0.8.0版本以前是一直存在的,并且有不少因此漏洞被攻击的案例。但在0.8.0版本之后,官方团队修复了此漏洞,再出现溢出情况时会直接revert掉。此篇讲述的是0.8.0版本之前的整型溢出漏洞案例。

整型溢出漏洞描述

在以太坊虚拟机(EVM)中为整数指定固定大小的数据类型,而且是无符号的。这意味着一个整型变量只能有一定范围的数字表示。例如,一个 uint8 ,只能存储范围 [0,255] 的数字,试图在这个uint8变量中存储 256,将会导致这个变量变为0。不加注意的话,只要没有检查用户输入又执行计算,导致数字超出存储它们的数据类型允许的范围,Solidity 中的变量就可以被用来组织攻击。

整型溢出攻击案例

2018年4月22日,黑客对BEC智能合约发起攻击,凭空取出巨量BEC代币并在市场上进行抛售,BEC随即急剧贬值,价值几乎为0,该市场瞬间土崩瓦解。

漏洞代码:

js 复制代码
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
    uint cnt = _receivers.length;
    uint256 amount = uint256(cnt) * _value;
    require(cnt > 0 && cnt <= 20);
    require(_value > 0 && balances[msg.sender] >= amount);
 
 
    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;
  } 

在batchTransfer函数中,出问题的地方在下面这行:

uint256 amount = uint256(cnt) * _value

其中变量cnt为转账的地址数量,可以通过外界的用户输入_receivers进行控制,_value为单地址转账金额,也可以直接进行控制。外界可以控制_receivers和_value的数值,那么我们就可以控制amount变量的值,让其产生非预期的值,导致向上溢出。如cnt = _receivers.length = 2,_value = 2255,这样amount = uint256(cnt) * _value = 2255*2超过uint256表示的最大值,导致溢出,最终amount = 0。 紧接着下面有一句对amount进行条件检查的代码require(_value > 0 && balances[msg.sender] >= amount);其中balances[msg.sender]代表当前用户的余额,amount代表要转的总币数。代码意思为确保单用户转账金额大于0,并且当前用户拥有的代币余额大于等于本次转账的总币数才进行后续转账操作。因为amount溢出后可以为一个很小的数字或者0(这里变成0),很容易绕过balances[msg.sender] >= amount的检查代码。从而产生巨大_value数额(这里为2**255)的恶意转账。

规避方法

  1. 现在可以直接升级Solidity到0.8.0版本以上规避此问题
  2. 在0.8.0版本以前,有一种选择是通过使用Open Zepplin 的 SafeMath库中定义的算术运算防止溢出。

在上述例子中,把问题行:

uint256 amount = uint256(cnt) * _value

改为:

uint256 amount = uint256(cnt).mul(_value)

SafeMath库中基本运算的方法如下所示:

js 复制代码
library SafeMath {
 
 
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }
 
 
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }
 
 
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }
 
 
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
} 
相关推荐
一水鉴天15 小时前
整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接 之2
数据库·人工智能·智能合约
dingzd952 天前
利用加密技术保障区块链安全
安全·web3·区块链·facebook·tiktok·instagram·clonbrowser
MicroTech20252 天前
微算法科技(NASDAQ: MLGO)研究分片技术:重塑区块链可扩展性新范式
算法·区块链
电报号dapp1192 天前
链游开发新篇章:融合区块链技术的游戏创新与探索
游戏·区块链
MicroTech20252 天前
微算法科技(NASDAQ: MLGO)引入高级区块链DSR算法:重塑区块链网络安全新范式
网络安全·区块链
胡乱编胡乱赢2 天前
区块链联邦学习思路一
区块链·联邦学习·思路·区块链联邦学习
元宇宙时间2 天前
RWA加密金融高峰论坛&星链品牌全球发布 —— 稳定币与Web3的香港新篇章
人工智能·web3·区块链
天涯学馆2 天前
如何在Solidity中使用映射和结构体
智能合约·solidity·以太坊
paceboy3 天前
如何制作免费的比特币冷钱包
区块链
小明的小名叫小明4 天前
区块链技术原理(15)-以太坊虚拟机 (EVM)
区块链