On 25 March 2025, MIM Spell was exploited for 6,261.13 ETH (~$12.9M) due to a vulnerability in the integration of the RouterOrder and Cauldron contracts.
The attacker was able to borrow funds, liquidate themselves then borrow funds again without repaying them. This was due to the liquidation process not overwriting records in RouterOrder that counted as collateral, allowing exploiter to falsely borrow additional funds after liquidation.
Exploits Transactions:
There are a number of exploit transactions across two wallets, below is an example of one set of transactions.
Preparation
https://arbiscan.io/tx/0xef64da328604bca1aee4b86504dbd2f81cc0f5d7d1b80bdca7011e470c076e0e
Borrow and Create Order
https://arbiscan.io/tx/0xcdfce7234225e445f764399407f1256c52f8b75db3baf77493bb4c88c8aacfd1
Liquidate and borrow again
https://arbiscan.io/tx/0x5416a5f23af22bd1c6c92dbbdb382da681884ed2be07f5c0903ab2241
Addresses
Attack wallet:
ARB: 0x51c9d0264d829a4F6d525dF2357Cd20Ea79b5049
ARB/ETH: 0xAF9e33Aa03CAaa613c3Ba4221f7EA3eE2AC38649
Attack contract: 0xf29120acd274a0c60a181a37b1ae9119fe0f1c9c
Step by Step
This triggered a solvency check:
The position passed the solvency check at this stage.
These actions raised the total borrowPart to over 420K units, now exceeding the collateral value (5 * $WBTC * 0.85), enabling liquidation. Note that when interacting through Cauldron, a solvency check is conducted at the end of cook() function after a series of actions and in individual action, eg. _borrow(), solvency is not required.
0xf291 proceeds to manage funds as 'swapper':
In the final phase, the attacker borrowed an extra 362K MIM and swapped it for WETH. However, because the public ‘inputAmount’ variable stored in ‘GmxV2CauldronRouterOrder' has not been updated, Cauldron still considers the Order to be worth 5 WBTC as collateral. The flawed solvency check allowed the borrow to go through, despite considerably over-leveraging.
The tactic was repeated across multiple orders, enabling the attacker to drain approximately $12.9M in total.
The vulnerability lies in the integration of RouterOrder and CauldronV4. The OrderAgent and RouterOrder are periphery contracts according to Abracadabra doc, Agent handles the creation and management of RouterOrder clones.
The OrderAgent.create() function creates a new order for a specific user with given parameters. It is called by the GmxV2CauldronV4 contract to set up a new order in response to user actions. RouterOrder.init() not only initializes the order but also triggers the order creation on the GMX Router.
During the liquidation process, the ‘sendValueInCollateral()’ function sends all funds to the liquidator, in this case attack contract 0xf291, but the public ‘inputAmount’ variable stored in ‘GmxV2CauldronRouterOrder' is not updated. Nor is the order effectively nullified in any other way. All other logic besides the highlighted line deals with ‘normal’ collateral that is not in RouterOrder. As a result, the exploiter remains able to borrow an additional 85% of the value treated as collateral.
The attacker utilized three main wallets for the exploit:
Each was funded on the morning of the attack on 25 March 2025. 0xAF9e and 0xe9A4 received an initial deposit of ETH from Tornado Cash, whilst 0xe9a4 was used to fund 0x51c9.
Funds from the exploit were aggregated into 0xAF9e immediately after then bridged from Arbitrum network to Ethereum via Stargate protocol in batches of 500 ETH.
Once on ETH, the funds were split into 3 different wallets:
As of 26 March the ETH remains in those wallets. The MIM team have reached out to the attacker to offer a 20% bounty, double the standard 10% offered in many exploits, in exchange for the return of the funds. If accepted, the offer would see around $10.4M returned whilst the exploiter would keep around $2.6M.
To keep up to date on the latest incident alerts and statistics follow @certikalert on X, or read our latest analysis on certik.com.