Echidna 模糊器,这是使用 Hevm 的假阴性案例吗?

问题描述 投票:0回答:2

我的问题很简单。 我正在审核 Ethernaut22 合约:此处获取代码 https://ethernaut.openzeppelin.com/level/0x9CB391dbcD447E645D6Cb55dE6ca23164130D008

Ethernaut22.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


contract Ethernaut22 is Ownable{

    address public token1;
    address public token2;


    constructor() {}



    function setTokens(address _token1, address _token2) public onlyOwner {
        token1 = _token1;
        token2 = _token2;
    }



    function addLiquidity(address token_address, uint amount) public onlyOwner {
        IERC20(token_address).transferFrom(msg.sender, address(this), amount);
    }



    function swap(address from, address to, uint amount) public {
        require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");
        require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
        uint swapAmount = getSwapPrice(from, to, amount);
        IERC20(from).transferFrom(msg.sender, address(this), amount);
        IERC20(to).approve(address(this), swapAmount);
        IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
    }



    function getSwapPrice(address from, address to, uint amount) public view returns(uint){
        return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
    }



    function approve(address spender, uint amount) public {
        Ethernaut22Token(token1).approve(msg.sender, spender, amount);
        Ethernaut22Token(token2).approve(msg.sender, spender, amount);
    }



    function balanceOf(address token, address account) public view returns (uint){
        return IERC20(token).balanceOf(account);
    }
}



contract Ethernaut22Token is ERC20 {
    address private _dex;


    constructor(
        address dexInstance,
        string memory name,
        string memory symbol,
        uint256 initialSupply
    )ERC20(name, symbol){
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
    }


    function approve(address owner, address spender, uint256 amount) public {
        require(owner != _dex, "InvalidApprover");
        super._approve(owner, spender, amount);
    }
}

这段代码中有很多错误,但我想从我的针鼹模糊测试中了解一些东西

这是代码:

//SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "../Ethernaut22.sol";
import "@crytic/properties/contracts/util/Hevm.sol";

//Fuzzing Ethernaut22 Contract
//

contract fuzzerEthernaut22 is Ethernaut22{


    address constant deployer = address(0x20000);
    address constant dist = address(0x30000);
    address token1Address;
    address token2Address;


    constructor(){
        token1Address = address(new Ethernaut22Token(address(this), "Token1", "TKN1", 110));
        token2Address = address(new Ethernaut22Token(address(this), "Token2", "TKN2", 110));
        Ethernaut22Token(token1Address).transfer(deployer, 110);
        Ethernaut22Token(token2Address).transfer(deployer, 110);
    }



    function checkOwner()public{
        address actualOwner = owner();
        assert(owner() == deployer);
    }




    function checkBalance()public{
        uint amount1 = Ethernaut22Token(token1Address).balanceOf(deployer);
        assert(amount1 == 110);
    }



    function check2Balance()public{
        uint amount2 = Ethernaut22Token(token2Address).balanceOf(deployer);
        assert(amount2 == 110);
    }

    ----ERROR HERE-----
    //False-Negative?
    function check_access_from_another_address_on_set_tokens_function()public{
        hevm.prank(dist);
        setTokens(token1Address, token2Address);
        assert(token1 == address(0));
        assert(token2 == address(0));
    }
}

这也是我的 config.yaml 文件

testMode: assertion
testLimit: 50000
deployer: "0x20000"
sender: ["0x20000" ,"0x30000"]
psender: "0x20000"

cryticArgs: ['--solc-remaps', '@=node_modules/@']


filterFunctions: ["fuzzerEthernaut22.transferOwnership(address)", "fuzzerEthernaut22.renounceOwnership()"]
filterBlacklist: true

如果我通过 config.yaml 运行 Echidna,我会收到错误 函数 check_access_from_another_address_on_set_tokens_function()public{}

该属性的范围是检查如果发送者不是所有者合约则设置代币的可能性。

如果 token 1 和 token 2 等于 0,这意味着 dist 无法设置 token,因为不是所有者。

我得到的错误是:

approve(address,uint256):  passed! 🎉
token2():  passed! 🎉
addLiquidity(address,uint256):  passed! 🎉
check_access_from_another_address_on_set_tokens_function(): failed!💥
  Call sequence:
    check_access_from_another_address_on_set_tokens_function()

Event sequence: Panic(1): Using assert.
owner():  passed! 🎉
check2Balance():  passed! 🎉
getSwapPrice(address,address,uint256):  passed! 🎉
checkBalance():  passed! 🎉
setTokens(address,address):  passed! 🎉
checkOwner():  passed! 🎉
token1():  passed! 🎉
swap(address,address,uint256):  passed! 🎉
balanceOf(address,address):  passed! 🎉
AssertionFailed(..):  passed! 🎉
Unique instructions: 3129
Unique codehashes: 2
Corpus size: 18
Seed: 1397477069376912808

看起来,我无法识别我的 hevm.prank(dist) 从另一个地址调用函数, 所以发送者仍然是部署者。

同时,根据echidna文档,应该是使用Hevm导致漏报的典型案例。

有人可以解释一下为什么我得到这个吗? 谢谢建议,祝你有美好的一天

security blockchain smartcontracts audit auditing
2个回答
1
投票

也许我刚刚找到了解决问题的方法。

//SPDX-License-Identifier: AGPL-3.0

pragma solidity ^0.8.0;

import "../Ethernaut22.sol";

//Fuzzing Ethernaut22 Contract
//


contract fuzzerEthernaut22 is Ethernaut22{

    address constant deployer = address(0x20000);
    address constant dist = address(0x10000);

    address public token1Address;
    address public token2Address;

    event AssertionFailed(string);

    constructor(){
        token1Address = address(new Ethernaut22Token(address(this), "Token1", "TKN1", 110));
        token2Address = address(new Ethernaut22Token(address(this), "Token2", "TKN2", 110));
        Ethernaut22Token(token1Address).transfer(deployer, 110);
        Ethernaut22Token(token2Address).transfer(deployer, 110);
    }


    // Emitting an AssertionFailed because in config.yaml we setted 0x10000 as sender
    // Passed
    function check_access_from_another_address_on_set_tokens_function()public{
        if(msg.sender == owner()){
            setTokens(token1Address, token2Address);
            assert(token1 != address(0));
            assert(token2 != address(0));
        }else{
            emit AssertionFailed("Sender is not the contract owner!");
        }
    }
}

我的config.yaml

testMode: assertion
testLimit: 50000

deployer: "0x20000"
sender: ["0x10000"]


cryticArgs: ['--solc-remaps', '@=node_modules/@']


filterFunctions: ["fuzzerEthernaut22.transferOwnership(address)", "fuzzerEthernaut22.renounceOwnership()"]
filterBlacklist: true

我只是避免使用 hevm 因为我无法识别这一点。 在 config.yaml 中,我刚刚将发送者设置为地址 0x10000,将部署者设置为 0x20000

因此,如果我尝试通过不同的地址调用函数 setTokens(address, address)public onlyOwner{...},则使用 Ownable.sol 和设置的owner() 作为部署者(0x20000),我只会收到回复。

这是通过 config.yaml 调用 echidna 并将发送者设置为 0x10000 的结果

fuzzerEthernaut22
approve(address,uint256):  passed! 🎉
token2Address():  passed! 🎉                                                                                       
token2():  passed! 🎉                                                                                              
check_balance_deployer_token1():  passed! 🎉                                                                       
check_owner():  passed! 🎉                                                                                         
check_addresses():  passed! 🎉                                                                                     
check_balance_deployer_token2():  passed! 🎉                                                                       
addLiquidity(address,uint256):  passed! 🎉                                                                         
check_access_from_another_address_on_set_tokens_function(): failed!💥                                              
  Call sequence:                                                                                                   
    check_access_from_another_address_on_set_tokens_function()                                                     
                                                                                                                   
Event sequence: AssertionFailed(«Sender is not the contract owner!») from: 0xa329c0648769a73afac7f9381e08fb43dbea72
owner():  passed! 🎉                                                                                               
getSwapPrice(address,address,uint256):  passed! 🎉                                                                 
setTokens(address,address):  passed! 🎉
token1():  passed! 🎉
token1Address():  passed! 🎉
swap(address,address,uint256):  passed! 🎉
balanceOf(address,address):  passed! 🎉
AssertionFailed(..): failed!💥
  Call sequence:
    check_access_from_another_address_on_set_tokens_function()

Event sequence: AssertionFailed(«Sender is not the contract owner!») from: 0xa329c0648769a73afac7f9381e08fb43dbea72
Unique instructions: 1912
Unique codehashes: 2
Corpus size: 12
Seed: 5517650708752309081

这是通过 config.yaml 调用 echidna 并将发送者设置为 0x20000 的结果

fuzzerEthernaut22
approve(address,uint256):  passed! 🎉
token2Address():  passed! 🎉                                                                                                       
token2():  passed! 🎉                                                                                                              
addLiquidity(address,uint256):  passed! 🎉
check_access_from_another_address_on_set_tokens_function():  passed! 🎉
owner():  passed! 🎉
getSwapPrice(address,address,uint256):  passed! 🎉
setTokens(address,address):  passed! 🎉
token1():  passed! 🎉
token1Address():  passed! 🎉
swap(address,address,uint256):  passed! 🎉
balanceOf(address,address):  passed! 🎉
AssertionFailed(..):  passed! 🎉
Unique instructions: 3469
Unique codehashes: 2
Corpus size: 19
Seed: 4335292047975925473

希望这个解决方法对某人有用。

如果有问题请告诉我(特别是对于Hevm使用)


0
投票

我遇到了类似的问题,花了我一段时间才理解 - 恶作剧的实际作用可能有点隐藏。如果你看一下源代码,恶作剧函数上面有一条注释:

...

// Performs the next smart contract call with specified `msg.sender`
function prank(address newSender) external;

...

请注意,它说的不是“下一个函数”,而是“下一个智能合约调用”。我进行了快速测试,似乎很准确:

function callee() public {
    emit LogAddr(msg.sender);
}

function caller() public {
    hevm.prank(address(0x1));
    callee();       <-- msg.sender logged 0x10000 (default for Echidna - not pranked...)
}

function callerFixed() public {
    hevm.prank(address(0x2));
    this.callee();  <-- msg.sender logged 0x2 (pranked!)
}

恶作剧仅适用于实际的

(address).call()
电话。因此,如果您调用自己的方法,因为它们属于另一个合约,那么它会起作用,但否则不行。

祝你好运!

© www.soinside.com 2019 - 2024. All rights reserved.