Note:
- No bad debt remains. The ~49k USDC shortfall created by the attack was covered internally within hours; no external lenders incurred losses.
Tx. hash of debt coverage: Base Transaction Hash: 0x9a75859269... | BaseScan- The affected sAMM-cUSDO/USDC lending market on Morpho has been switched to close‑only mode from our vault, allocations are set to 0, borrowers may close their positions at will.
Date of Incident: 25 May 2025, 11:29pm UTC
Summary:
On 25 May 2025 at 11:29pm UTC, an attacker exploited an oracle vulnerability in the Morpho lending market involving Aerodrome AMM LP tokens for the cUSDO/USDC pair. By using flash loans to swap large amounts, the attacker was able to manipulate the price oracle of the LP tokens and thereby was able to first overcollateralize and then undercollateralize their position, resulting in a loss of 48k USDC for the lenders in that Morpho market. The root cause was an LP price oracle design flaw, using a combination of hardcoded values for USDO (=1 USD) and relying on AMM pool reserves as inputs to value the LP token. Morpho smart contracts are safe and operated as designed - this event was not an issue with any of Morpho contracts but with the curation of the cUSDO/USDC market. As of now, the 49,303 USD bad debt resulting from the attack was fully compensated by the curator, i.e. depositors in the Clearstar OpenEden USDC Vault are not exposed to any losses.
Incident Timeline and Attack Steps
The attack was executed atomically with this transaction: Base Transaction Hash: 0xd8c4f742e2... | BaseScan
o) Attacker wallet funded from TornadoCash on Eth mainnet and bridge to Base through Symbiosis
i) Attacker Contract Deployment & Flashloan
The attacker contract (entrypoint of the tx, borrower) first deployed another separate contract (supplier) to act as a lender/supplier in the Morpho market. This was required because a lender cannot simultaneously be a borrower. The borrower contract flash loaned 6,666,666.67 USDC from Morpho, of which 300,000 USDC was sent to the supplier contract for step ii).
ii) Increasing available liquidity on AMM-cUSDO/USDC
The supplier contract lent 300,000 USDC to the Morpho market to deepen the available liquidity.
iii) Initial LP Position Creation
The borrower contract swapped 108,000 USDC into 103,941.001 cUSDO on Aerodrome, which was then paired with another 199,068.571 USDC to mint an LP token. The transactions so far were ordinary and the fair value of this LP token is 305,088.39 USD (using 1 USDC = 1 USD and 1 cUSDO = 1.02 USD for simplicity in walking through the post mortem)
iv) Using LP Tokens as Collateral
The Aerodrome LP tokens worth 305,088.39 USD were deposited into the Morpho market as collateral, in order for the attacker to later borrow from it.
v) Oracle Manipulation via Large Swap
- The attacker swapped a very large amount of USDC for cUSDO in the Aerodrome pool (3,500,000 USDC for 784,773 cUSDO).
- USDC reserves spike significantly as the pool’s AMM curve responds to the trade.
- cUSDO reserves are heavily depleted.
- The pool’s internal price for USDC becomes very low (i.e., USDC depegged vs. cUSDO in the Aerodrome pool, it takes very little cUSDO to acquire a large amount of USDC).
- The oracle does not consult the pool’s price but relies on an external price (Chainlink) for USDC, which remains close to $1 and ignores the manipulated/depegged market price in the pool.
- The attacker thereby inflates the LP token price since the amount of USDC in the pool has increased massively (+3,500,000) whereas the cUSDO reserve decreased by a much smaller amount (-784,773 cUSDO). Summed together, the total value of LP tokens reported by the oracle increased by 2,699,531.54 USD.
- The oracle (see code below) relied on the pool’s reserves to determine LP token value, whereby the price of USDC was sourced from Chainlink, the conversion rate from cUSDO to USDO was taken from the ERC-4626 contract of cUSDO, and the USDO price was hardcoded to $1 since the token is fully backed by T-Bill reserves.
vi) Excessive Borrowing Against Inflated LP Value
The attacker is then able to borrow 363,935 USDC from the AMM-cUSDO/USDC market against the LP tokens (which are only worth around 305,088.39 USD at fair market value used to mint the LP).
vii) Reversing Price Distortion
The attacker then reversed the manipulation by swapping the 784,773 cUSDO back into 3,497,832.51 USDC.
- Due to swap fees (which are not counted as part of the Aerodrome pool reserves used as an input to the oracle), the actual reserves in the pool now decreased compared to the pre-exploit state (i.e. the reserves now are lower than after the attacker minted the LP in step iii). Now, the oracle undervalues the LP tokens, making them look worth less than they actually are.
- The swap fees for LPs and the Aerodrome protocol fee sum up to 19’414.39 USD, i.e. the LP token price reported by the oracle is now 19’414.39 USD less compared to the unexploited/correct state after step ii) where the attacker minted LP tokens.
- The LP token collateral posted by the attacker falls below the 86% liquidation LLTV because of this undervaluation.
viii) Self-Liquidation for Additional Profit
With the new lower oracle price, the attacker was able to trigger a liquidation on their own position and re-acquire their LP collateral at an under-collateralized value and earning the liquidation bonus, earning a profit of 13,396.17 USD (the attacker paid 291,692.22 USDC to free up the LP token collateral worth 305,088.39 USD). The total profit for the attacker / bad debt for the lenders so far is 72,242.78 USDC (difference between borrowed amount of 363,935 USDC and repaid amount of 291’692.22 USDC).
ix) Withdraw supplied liquidity
The supplier contract withdraws 265,519.05 USDC liquidity, i.e. realizing a loss on the attacker’s side of 34,480.95 USDC (from the 300k USDC deposited in step ii), which offsets the attackers profit and consequently reduces the loss attributable to other lenders in this market.
x) Exploit Repeated with Lower Amounts
The exploiter contract repeated the attack, this time depositing 15,321.81 USD worth of LP tokens (minted from 10,000 USDC + 5,217.46 cUSDO on Aerodrome), swapping 3,000,000 USDC into 687,981.38 cUSDO to manipulate the LP token oracle again, and then borrowing 26,173.17 USDC against the overvalued collateral, i.e. causing more bad debt (although at this stage unrealized). This still happened within the same atomic transaction as all the other steps. The atomic self-liquidation was not repeated for this second attack iteration, but was carried out by the attacker wallet non-atomically in two separate transactions 10min and 14minutes after the initial exploit. The liquidations result in realizing bad debt of an additional 11’541.64 USDC (including the liquidator bonus). The total loss for the lenders thereby sums up to 40,303.47 USDC (11,541.64 + 72,242.78 - 34,480.95). The full amount was covered by the Curator, i.e. depositors to the Clearstar OpenEden USDC Vault (which had exposure to the exploited market) do not suffer any losses as of now.
xi) Bridge & TornadoCash
Finally, the attacker converted the obtained USDC and cUSDO into ETH, bridged it back to mainnet and then sent most of the funds to TornadoCash.
Root Cause Analysis
The vulnerability stemmed from the design of the custom LP price oracle:
function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80) {
uint256 totalSupply = LP_TOKEN.totalSupply();
(uint256 r0, uint256 r1, ) = LP_TOKEN.getReserves();
(uint256 usdcReserves, uint256 cusdoReserves) = IS_USDC_T0 ? (r0, r1) : (r1, r0);
uint256 usdcValue = (usdcReserves * 1e12 * USDC_PRICE_FEED.getPrice()) / 1e8;
uint256 cusdoValue = CUSDO.getAssets(cusdoReserves);
uint256 lpPrice = ((cusdoValue + usdcValue) * 1e18) / totalSupply;
return (0, int256(lpPrice), 0, 0, 0);
}
Key Flaws in the Oracle: use of raw pool reserve balances combined with external price feeds
- Hardcoded USDO price and reliance on pool reserves:
- LP Token Price = (USDC reserves * USDC price + cUSDO reserves * cUSDO conversion rate * hardcoded USDO price) / LP token supply
- This was the main cause of the attack, as the pricing logic could be predicted and gamed by an attacker to overvalue LP tokens. While the external USDC price and the conversion of cUSDO to USDO was correctly valued, the inflated amount of reserves caused the oracle to report a much higher price.
- Swap fees not counted as part of pool reserves:
- This is prone to manipulation via large swaps (e.g. by using flash loans), where the paid fees caused the reserves to decrease. This allowed the second part of the exploit where the LP token collateral was undervalued.
- The attacker was able to cause temporary reserve imbalances, misleading the oracle and extracting profit in both the overcollateralization (borrowing) and undercollateralization (liquidation) phases.
High-level summary of exploit actions:
- Morpho → 0xE11: Flashloan 6.66m USDC.
- 0xE11 → 0x8F5 / 0x8F5 → Morpho: Helper deposits (for market liquidity, appearing more legitimate).
- 0xE11 → Aerodrome (0xfe…): Large swaps (USDC → cUSDO, then cUSDO → USDC).
- Aerodrome → Fee Collector (0xDFC): Swap fees accrue, untracked by the oracle.
- 0xE11 → Morpho: LP tokens used as collateral.
- Morpho → 0xE11: Overborrowing of USDC.
- 0xE11 (liquidator) → Morpho: Position liquidation.
- Surplus USDC: Attacker withdraws, repays flashloan, keeps the remaining as profit.
Impact
- Funds stolen / Bad debt: 49,303.14 USDC, fully compensated by the Curator.