JavaScript 不可用。

我们检测到浏览器禁用了 JavaScript。请启用 JavaScript 或改用支持的浏览器来继续访问

SmartMesh SMT 的 Solidity 智能合约因整型溢出漏洞被攻击,合约内有做防范,但是被绕过去了 [整数溢出攻击]

作者:Anban Chu

发表日期:2018年05月26日

所属目录:黑客攻击

场景描述

2018 年 4 月 25 日,SMT 币因为整型溢出漏洞被攻击,火币 Pro 随即暂停了所有币种的充值提取业务进行大排查。

合约列表

攻击如何运作

Solidity 智能合约容易受到整数溢出或下溢的影响。当变量超过它使用的数据类型的最大值或最小值时,它们就会发生。发生这种情况时,该值分别环绕最小或最大范围的另一端。如下是一段 Solidity 代码演示:

// SPDX-License-Identifier: MIT
pragma solidity ^0.4.20;

contract AnbangDemo {
    uint256 public min = 0;
    uint256 public max = 2 ** 256 - 1;

    function add(uint256 _x) public view returns (uint256) {
        return max + _x;
    }

    function minus(uint256 _y) public view returns (uint256) {
        return min - _y;
    }
}

max 的值是 2^256 - 1

  • 当执行 add(1) ,得到的结果是 0
  • 当执行 add(2) ,得到的结果是 1
  • 当执行 add(3) ,得到的结果是 2

min 的值是 0

  • 当执行 minus(1) ,得到的结果是 115792089237316195423570985008687907853269984665640564039457584007913129639935
  • 当执行 minus(2) ,得到的结果是 115792089237316195423570985008687907853269984665640564039457584007913129639934
  • 当执行 minus(3) ,得到的结果是 115792089237316195423570985008687907853269984665640564039457584007913129639933

这就是 Solidity 的整数溢出。

推荐做法

推荐使用 SafeMath 操作,而不是自己做检验。自己做操作可能会出现遗漏的情况。

下面是对整型的数据进行处理的演示操作。

// SPDX-License-Identifier: MIT
pragma solidity ^0.4.20;

contract AnbangDemo {
    uint256 public min = 0;
    uint256 public max = 2**256 - 1;

    function add(uint256 _x) public view returns (uint256) {
        // ❌ bad
        // return max + _x;

        // ✅ good
        uint256 c = max + _x;
        assert(c >= max);
        return c;
    }

    function minus(uint256 _y) public view returns (uint256) {
        // ❌ bad
        // return min - _y;

        // ✅ good
        assert(_y <= min);
        return min - _y;
    }

    // 乘法
    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;
    }
}

solidity 0.8.0 版本已经修复了此问题,推荐使用新版本。如果使用的是之前的老版本,推荐继续使用 SafeMath 。

SmartMesh (SMT) 合约内被攻击的函数 ƒ

function transferProxy(address _from, address _to, uint256 _value, uint256 _feeSmt,
    uint8 _v,bytes32 _r, bytes32 _s) public transferAllowed(_from) returns (bool){

    if(balances[_from] < _feeSmt + _value) revert();

    uint256 nonce = nonces[_from];
    bytes32 h = keccak256(_from,_to,_value,_feeSmt,nonce);
    if(_from != ecrecover(h,_v,_r,_s)) revert();

    if(balances[_to] + _value < balances[_to]
        || balances[msg.sender] + _feeSmt < balances[msg.sender]) revert();
    balances[_to] += _value;
    Transfer(_from, _to, _value);

    balances[msg.sender] += _feeSmt;
    Transfer(_from, msg.sender, _feeSmt);

    balances[_from] -= _value + _feeSmt;
    nonces[_from] = nonce + 1;
    return true;
}

攻击者在交易中利用了这个功能在交易 0x1abab4c8db9a30e703114528e31dee129a3a758f7f8abc3b6494aad3d304e43f 中进行攻击

Function: transferProxy(
    address _from, address _to, uint256 _value,
    uint256 _feeSmt, uint8 _v, bytes32 _r, bytes32 _s
)

MethodID: 0xeb502d45
[0]:  000000000000000000000000df31a499a5a8358b74564f1e2214b31bb34eb46f
[1]:  000000000000000000000000df31a499a5a8358b74564f1e2214b31bb34eb46f
[2]:  8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
[3]:  7000000000000000000000000000000000000000000000000000000000000001
[4]:  000000000000000000000000000000000000000000000000000000000000001b
[5]:  87790587c256045860b8fe624e5807a658424fad18c2348460e40ecf10fc8799
[6]:  6c879b1e8a0a62f23b47aa57a3369d416dd783966bd1dda0394c04163a98d8d8

该交易发生在区块 5499035 ,此时在区块链中:

  • balances[_from] ( balances[0xdf31a499a5a8358b74564f1e2214b31bb34eb46f] ) 的余额为 0
  • balances[_to] ( balances[0xdf31a499a5a8358b74564f1e2214b31bb34eb46f] ) 的余额为 0
  • balances[msg.sender] ( balances[0xd6a09bdb29e1eafa92a30373c44b09e2e2e0651e] ) 的余额为 0

transferProxy 的参数中

  • _from0xdf31a499a5a8358b74564f1e2214b31bb34eb46f
  • _to0xdf31a499a5a8358b74564f1e2214b31bb34eb46f
  • _value0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  • _freeSmt0x7000000000000000000000000000000000000000000000000000000000000001.
    • 注意 0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 0x7000000000000000000000000000000000000000000000000000000000000001 是 0 .这将有助于快速理解。
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.20;

contract AnbangDemo {
    uint256 public a = 0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    uint256 public b = 0x7000000000000000000000000000000000000000000000000000000000000001;
    uint256 public c = a + b; // 结果是 0
}

现在,让我们看一下 transferProxy

第一个 if 语句 if(balances[_from] < _feeSmt + _value) revert();。因为 balances[_from]是 0 和 _feeSmt + _value 也是 0, 此时 if 的条件的结果是 false ( 0 < 0 的表达式返回 false ) ,此时 revert() 没有被触发,参数通过了这个检查。

第二个 if 语句是判断地址的,这个地址是正确的,所以可以通过检查。

第三个 if 语句: 同样是拦截失败的,合约作者是打算在这里执行溢出检查的,然而却没有发生溢出。 balances[_to] 是 0 因此 balances[_to] + _value < balances[_to] 是 false。 balances[msg.sender]是 0 因此 balances[msg.sender] + _feeSmt < balances[msg.sender] 是 false。 因此,这两个条件都是 false 和 revert() 没有被触发。

随后就是合约内记账了,没有任何检查了,余额的状态变量将被修改为:

balances[_to] += _value; 导致地址所有者 _to 获得大量代币 balances[_from] -= _value + _feeSmt; 导致所有者 _from 失去 0 个代币

OKO 可以查看状态的改变

0x55f9…e081 Before After Diff
balanceOf[0xdf31a499a5a8358b74564f1e2214b31bb34eb46f] 0 0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0x8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
nonce[0xdf31a499a5a8358b74564f1e2214b31bb34eb46f] 0 1 1
balanceOf[0xd6a09bdb29e1eafa92a30373c44b09e2e2e0651e] 0 0x7000000000000000000000000000000000000000000000000000000000000001 0x7000000000000000000000000000000000000000000000000000000000000001

https://oko.palkeo.com/0x1abab4c8db9a30e703114528e31dee129a3a758f7f8abc3b6494aad3d304e43f/

小结

这里的 SMT 已经做了溢出检查的逻辑,应该也做了单元测试,但是极端情况下,被绕过去了。 建议使用 SafeMath 库操作,不要自己写,否则可能因为考虑不周被攻击。

相关攻击

  • BEC 美蜜合约出现重大漏洞:https://www.secrss.com/articles/2194




以上就是本篇文章的全部内容了,希望对你有帮助。

>> 本站不提供评论服务,技术交流请在 Twitter 上找我 @anbang_account