# MidasCapital: read-only reentrancy in Curve LP oracle enables liquidations

Read-only reentrancy in MidasCapital's Curve LP oracle let attacker manipulate STMATIC price mid-remove_liquidity and liquidate four FJ* markets for $650k.

- Payout: Real-world hack
- Severity: high
- Program: - MidasCapital
- Analysis by: DeFiHackLabs
- Disclosed: 2023-01-16
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2023-01/Midas_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20230116-midas
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained vuln:flash-loan vuln:liquidation vuln:oracle-manipulation vuln:reentrancy

**Target:** Compound-fork lending markets (FJCHF/FJEUR/FJGBP/FAGEUR) with Curve LP collateral oracle on Polygon
**Root cause:** The price oracle reads Curve pool's virtual_price/underlying price during remove_liquidity, before the pool state is finalized, so a reentrant call sees a manipulated LP price and treats healthy positions as undercollateralized.
**Steps:**
1. Flash loan WMATIC via Balancer, then Aave V3, then Aave V2 (nested callbacks).
2. `unitroller.enterMarkets([WMATIC_STMATIC, FJCHF, FJEUR, FJGBP, FAGEUR])`.
3. `curvePool.add_liquidity([0, 270_000e18], 0)` to seed STMATIC/WMATIC Curve pool.
4. `WMATIC_STMATIC.mint(mintAmount)` to deposit LP as collateral.
5. `curvePool.add_liquidity([0, WMATIC.balanceOf(this)], 0)` then `curvePool.remove_liquidity(LPAmount, [0,0], true)` — reentrancy point where oracle price is read mid-withdrawal.
6. Call `liquidate()` which invokes `liquidateBorrow` on FJCHF/FJEUR/FJGBP/FAGEUR against WMATIC_STMATIC, then `redeem` and transfer seized STMATCI_F to receiver.
7. Repay flash loans; profit in WMATIC.
**How found:** Likely spotted that the oracle's getUnderlyingPrice reads Curve pool state that is transient during remove_liquidity, and that MidasCapital's liquidateBorrow path does not use a reentrancy guard or check pool invariants — a classic read-only reentrancy pattern on Curve LP oracles.
**Exploitation:** Attacker flash-borrowed WMATIC across Balancer/Aave V2/V3, deposited Curve LP as collateral in MidasCapital, then triggered remove_liquidity with donate_dust=true. During the callback the oracle read a stale/manipulated LP price, making the attacker's own position appear underwater; the attacker then liquidated the four fiat markets (FJCHF, FJEUR, FJGBP, FAGEUR) whose collateral was the same LP, seizing STMATIC LP tokens at a discount and redeeming for profit (~$650k).
**Escalation:** Chained three flash-loan sources to maximize capital and used the same manipulated LP as collateral across four separate markets, multiplying seized value.
**Insight:** Any lending market using a Curve LP token as collateral and pricing it via the pool's virtual_price/spot state is vulnerable to read-only reentrancy: trigger remove_liquidity with donate_dust and read the oracle inside the callback before the pool rebalances. Check whether the oracle uses get_virtual_price vs. a TWAP and whether liquidateBorrow is reentrancy-guarded.

