智能合约安全漏洞(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;
  }
} 
相关推荐
公链开发18 小时前
2026 Web3机构级风口:RWA Tokenization + ZK隐私系统定制开发全解析
人工智能·web3·区块链
ICkaihuuu20 小时前
MT4 黄金交易:如何设置与计算交易手数(2026 最新指南)
区块链
数据大魔方1 天前
【期货量化入门】Python获取期货实时行情(TqSdk完整代码)
开发语言·python·区块链
CryptoRzz1 天前
印度股票数据API对接实战(实时行情与IPO功能全解析)
websocket·区块链·github·共识算法·分布式账本
软件工程小施同学1 天前
区块链论文速读 CCF A--TDSC 2025 (3)
运维·服务器·区块链
ATMQuant2 天前
量化指标解码13:WaveTrend波浪趋势 - 震荡行情的超买超卖捕手
人工智能·ai·金融·区块链·量化交易·vnpy
Web3VentureView2 天前
SYNBO 协议亮相 ChainThink “Meme 回归” AMA:市场奖励机制深度剖析
网络·金融·web3·区块链·加密货币
企业对冲系统官2 天前
基差风险管理系统集成说明与接口规范
大数据·运维·python·算法·区块链·github
企业对冲系统官2 天前
大宗商品风险对冲系统统计分析功能的技术实现
运维·python·算法·区块链·github·pygame
焦点链创研究所2 天前
智慧协议:关于人类秩序最终形态的链上探索
科技·web3·去中心化·区块链