1.美链攻击过程
美链代币BEC为发行在以太坊上的ERC20代币,其具体合约的代码在该链接中合约代码。 向美链发起攻击的交易链接为攻击交易hash。
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;
美链攻击的具体流程分为以下四步: 1.首先构造一个_value值,使得_receivers.len() * _value产生向上溢出,结果为一个极小值amount。比如使用_receivers.len()为2,_value值为2**255,那么在上述代码中计算得到的amount值为0。 2.amount,value通过require验证。 3.在msg.sender中减去amount数量的代币。 4.为每个_receivers账户增加value数量的代币。 其中value为一个极大值,相当于对美链的BEC代币进行了增发,对应的_receivers账户可以获取大量BECtoken。
2.solidity中上溢与下溢
在solidity中变量进行+、-、*运算时会产生溢出,如加法运算和乘法运算会产生向上溢出,减法运算产生向下溢出。如2**255变量乘以2,获得的结果为0。具体溢出情况可参考以下合约代码:
pragma solidity 0.4.20;
contract TestFlow {
uint256 public zero = 0;
uint256 public max = 2**256 - 1;
uint256 public mm = 2**255;
function subUnderFlow() public constant returns (uint) {
uint256 a = zero - 1;
return a;
}
function addOverFlow() public constant returns (uint) {
uint256 a = max + 1;
return a;
}
function mulOverFlow() public constant returns (uint) {
uint256 a = mm * 2;
return a;
}
}
3.定义安全库
在对数字进行运算时,要采用安全函数,保证结果正确且不发生溢出。安全的数学运算库的使用如下所示。 第一步:导入SafeMath库文件
pragma solidity >=0.4.22 <0.6.0;
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
第二步,引用
pragma solidity >=0.4.22 <0.6.0;
import "./safemath.sol";
contract jisuan{
uint a=2;
uint b=5;
uint c=8;
//引入safemath库
using SafeMath for uint256;
//加法
function addNum()public view returns(uint d){
d=a.add(b);
}
//减法
function subNum()public view returns(uint d){
d=b.sub(a);
}
//乘法
function mulNum()public view returns(uint d){
d=a.mul(b);
}
//除法
function divNum()public view returns(uint d){
d=c.div(a);
}
}
|