智能合约安全漏洞(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;
  }
} 
相关推荐
Web3VentureView1 天前
SYNBO深度参与Ethereum on Tour 上海交大站:从高校 Builder 到链上一级市场基础设施
人工智能·web3·区块链·加密货币·synbo
ithadoop2 天前
Solana入门:区块链新手速成指南(第二阶段:开发入门)
rust·web3·区块链·智能合约·solana
Bczheng13 天前
二十.读写交易索引和验证交易
区块链
曦月逸霜3 天前
区块链技术与应用学习笔记(持续更新中)
笔记·学习·区块链
Web3VentureView3 天前
SYNBO维港私享局:在香港Web3嘉年华最后一天,打开链上一级市场的共识现场
人工智能·web3·区块链·加密货币·synbo
TechubNews4 天前
专访新火集团首席经济学家付鹏:解读比特币资产属性、香港楼市与普通人理财建议——Techub News对话实录
人工智能·区块链
王苏安说钢材A4 天前
无锡佳钛合不锈钢有限公司不锈钢焊管厂家
区块链
财迅通Ai4 天前
能源板块强势领涨,汇添富能源ETF(159930.SZ)单日大涨3.41%
区块链·能源·中国神华·陕西煤业
Web3VentureView4 天前
SYNBO亮相香港《前瞻》活动,联手HashKey共筑链上原生一级市场新范式
人工智能·web3·区块链·加密货币·synbo
每日综合4 天前
Web3 多链时代,安全与体验如何兼得?UKey Wallet 的“解题思路”
安全·web3·区块链