MuBank Bond Manipulation
Exploit Transactions
0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680Victim Addresses
0x4aa679402c6afce1e0f7eb99ca4f09a30ce228abAvalancheLoss Breakdown
Similar Incidents
SuperTokenV2 share inflation
32%Nereus Oracle Overborrow
32%Platypus LP Cross-Asset Mispricing
27%Platypus Stale Collateral Withdrawal
26%Platypus PoolSAvax JIT LP Exploit
26%Stars Arena Callback Weight Reentrancy
26%Root Cause Analysis
MuBank Bond Manipulation
1. Incident Overview TL;DR
On Avalanche block 23435295, attacker EOA 0xd46b44a0e0b90e0136cf456df633cd15a87de77e sent transaction 0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680 to helper contract 0xe6c17763ca304d70a3fb334d05b2d29c2bb251e9. The helper flash-swapped MU from the Trader Joe MU/MUG pair, sold that MU into the MU/USDC.e pair to reshape both pools, then called MuBank.mu_bond and MuBank.mu_gold_bond while MuBank was reading the manipulated spot reserves. MuBank transferred 99102201128573810201054 raw MU and 9640237839603403398058 raw MUG for only 10290000000 raw USDC.e, minted 10290000000000000000000 raw MUMO to itself, and the attacker unwound the position back into 57659463903 raw USDC.e profit.
The root cause is a pricing design flaw in MuBank. Its bond quote logic reads live AMM reserves from Trader Joe and prices payouts by averaging getAmountIn and getAmountOut for the same trade size. That arithmetic mean is not manipulation resistant, so a flash-swap adversary can move the reserves and redeem MuBank inventory at an inflated payout within the same transaction.
2. Key Background
MuBank at 0x4aa679402c6afce1e0f7eb99ca4f09a30ce228ab exposes public mu_bond(address stable, uint256 amount) and mu_gold_bond(address stable, uint256 amount) entrypoints. Both functions accept approved stables such as USDC.e, transfer out MU or MUG from protocol inventory, and mint MUMO to MuBank itself. There is no delay, TWAP, or two-step settlement between quote formation and payout.
Two Trader Joe pools matter:
- MU/USDC.e pair
0xfacb3892f9a8d55eb50fdeee00f2b3fa8a85ded5 - MU/MUG pair
0x67d9aab77beda392b1ed0276e70598bf2a22945d
Trader Joe pairs support flash-swap callbacks through swap(..., data) and joeCall, so an attacker can borrow pool inventory intra-transaction, distort spot reserves, use the manipulated reserves against an external protocol, and repay before transaction end. That is exactly what happened here.
3. Vulnerability Analysis & Root Cause Summary
This incident is an ATTACK-category ACT opportunity against MuBank’s bond-pricing logic. MuBank does not use an oracle with any manipulation resistance; instead, it synchronously reads Trader Joe spot reserves and derives payout amounts from two opposite-side swap quote functions. In MuBank source, mu_bond and mu_gold_bond call _mu_bond_quote and _get_mug_bond_quote, then immediately transfer MU or MUG and mint MUMO. _mu_bond_quote scales the USDC.e reserve by 1e12, calls router.getAmountIn and router.getAmountOut, and averages them into mu_coin_bond_amount. _get_mug_bond_quote repeats the same pattern on the MU/MUG pool to derive the MUG bond amount. Because both helpers depend on current pair reserves, any attacker who can temporarily move those reserves can force MuBank to overpay inventory. The exploit therefore breaks the invariant that a stable deposit must not buy more MU or MUG from MuBank than it could obtain under manipulation-resistant pricing.
MuBank’s relevant source behavior is:
function mu_bond(address stable, uint256 amount) public nonReentrant {
(uint256 mu_coin_swap_amount, uint256 mu_coin_amount) = _mu_bond_quote(amount);
_stable.transferFrom(msg.sender, address(this), _adjusted_amount);
IERC20(_MuCoin).transfer(msg.sender, mu_coin_amount);
MuMoneyMinter(_MuMoney).mint(address(this), amount);
}
function mu_gold_bond(address stable, uint256 amount) public nonReentrant {
(uint256 mu_gold_swap_amount, uint256 mu_gold_bond_amount) = _get_mug_bond_quote(amount);
_stable.transferFrom(msg.sender, address(this), _adjusted_amount);
IERC20(_MuGold).transfer(msg.sender, mu_gold_bond_amount);
MuMoneyMinter(_MuMoney).mint(address(this), amount);
}
function _mu_bond_quote(uint256 amount) internal view returns (uint256 swapAmount, uint256 bondAmount) {
(uint112 reserve0, uint112 reserve1) = Pair(0xfacB3892F9A8D55Eb50fDeee00F2b3fA8a85DED5).getReserves();
reserve0 = reserve0 * (10 ** 12);
uint256 amountIN = router.getAmountIn(amount, reserve1, reserve0);
uint256 amountOUT = router.getAmountOut(amount, reserve0, reserve1);
uint256 mu_coin_bond_amount = (((((amountIN + amountOUT) * 10)) / 2) / 10);
return (amountOUT, mu_coin_bond_amount);
}
4. Detailed Root Cause Analysis
The exploit starts from MuBank’s quote dependency on instantaneous pool state. In the collected trace, the attacker first invoked the MU/MUG pair’s flash-swap path and borrowed 578987936812388669434540 raw MU:
0x67d9...45d::swap(578987936812388669434540, 0, 0xe6c177..., data)
ERC20::transfer(0xe6c177..., 578987936812388669434540)
0xe6c177...::joeCall(...)
Inside joeCall, the helper sold the borrowed MU into the MU/USDC.e pair and received 85123535028 raw USDC.e. The trace then shows MuBank reading the manipulated reserves:
JoePair::getReserves() -> 25472863623 USDC.e, 751727888303699108771531 MU
TraderJoeRouter::getAmountIn(3300000000000000000000, 751727888303699108771531, 25472863623000000000000)
TraderJoeRouter::getAmountOut(3300000000000000000000, 25472863623000000000000, 751727888303699108771531)
MuBank::mu_bond(USDC.e, 3300000000000000000000)
Those reserve reads are the breakpoint. MuBank does not observe any historical price, only the attacker-chosen state that exists after the flash-swap sell but before repayment. Under that state, MuBank transferred 99102201128573810201054 raw MU for 3300000000 raw USDC.e and then, through the chained MU/MUG quote logic, transferred 9640237839603403398058 raw MUG for another 6990000000 raw USDC.e. Balance diffs confirm the exact inventory drains and MUMO mint:
MuBank MU delta: -99102201128573810201054
MuBank MUG delta: -9640237839603403398058
MuBank USDC.e: +10290000000
MuBank MUMO: +10290000000000000000000
After extracting MuBank inventory, the helper used 74833535028 raw USDC.e to buy back 560397963969322482556602 raw MU from the MU/USDC.e pair, transferred 581014394591232029777560 raw MU back into the MU/MUG pair to satisfy the flash-swap invariant, sold the bonded 9640237839603403398058 raw MUG for 180974292926286893962912 raw MU, and finally sold 259460063432951156943008 raw MU into the MU/USDC.e pair. The last swap paid 57659463903 raw USDC.e directly to the attacker EOA, which matches the balance diff.
The flash swap is only an enabler. The actual defect is MuBank’s willingness to transfer protocol inventory against attacker-controlled reserve snapshots and invalid averaging math. If MuBank had used a manipulation-resistant oracle or delayed settlement, the same flash-swap path would not have produced the observed overpayment.
5. Adversary Flow Analysis
The adversary cluster contains:
- EOA
0xd46b44a0e0b90e0136cf456df633cd15a87de77e, the transaction sender and final profit recipient - Helper contract
0xe6c17763ca304d70a3fb334d05b2d29c2bb251e9, deployed in transaction0x9050550c8bf42653ae1affc755ca0878e972437db0206fe87aa3636ab8450250
The execution flow in transaction 0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680 is:
- Flash-swap
578987936812388669434540MU from the MU/MUG Trader Joe pair. - Sell that MU into the MU/USDC.e pair for
85123535028raw USDC.e, forcing MuBank’s source of price truth into an attacker-selected state. - Call
MuBank.mu_bond(USDC.e, 3300e18)andMuBank.mu_gold_bond(USDC.e, 6990e18)while those manipulated reserves remain live. - Receive
99102201128573810201054MU and9640237839603403398058MUG from MuBank, while MuBank mints10290e18MUMO to itself. - Spend
74833535028raw USDC.e to reacquire MU, repay the flash-swap leg in MU, then sell the bonded MUG into the now-reshaped MU/MUG pool. - Sell the remaining
259460063432951156943008raw MU into the MU/USDC.e pool and deliver57659463903raw USDC.e to the attacker EOA.
Every step is permissionless: Trader Joe flash swaps are public, Trader Joe router swaps are public, and MuBank bond functions are public. No privileged role, private key compromise, or attacker-specific artifact is required to realize the opportunity.
6. Impact & Losses
The measurable protocol-side extraction is:
57659463903raw USDC.e (57659.463903USDC.e) realized by the attacker EOA99102201128573810201054raw MU drained from MuBank inventory9640237839603403398058raw MUG drained from MuBank inventory
MuBank also minted 10290000000000000000000 raw MUMO to itself during the bond calls. The economic effect is that MuBank accepted only 10290 USDC.e while transferring out much more valuable MU and MUG inventory under manipulated reserve conditions.
7. References
- Exploit transaction:
0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680 - Helper deployment transaction:
0x9050550c8bf42653ae1affc755ca0878e972437db0206fe87aa3636ab8450250 - MuBank contract:
0x4aa679402c6afce1e0f7eb99ca4f09a30ce228ab - MU/USDC.e pair:
0xfacb3892f9a8d55eb50fdeee00f2b3fa8a85ded5 - MU/MUG pair:
0x67d9aab77beda392b1ed0276e70598bf2a22945d - Collected trace:
/workspace/session/artifacts/collector/seed/43114/0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680/trace.cast.log - Balance diff:
/workspace/session/artifacts/collector/seed/43114/0xab39a17cdc200c812ecbb05aead6e6f574712170eafbd73736b053b168555680/balance_diff.json - MuBank verified source:
https://api.etherscan.io/v2/api?chainid=43114&module=contract&action=getsourcecode&address=0x4aa679402c6afce1e0f7eb99ca4f09a30ce228ab