# Sturdy Finance: read-only reentrancy drains $800k via LP oracle

Read-only reentrancy in Sturdy Finance's Balancer LP oracle let attacker borrow against inflated collateral, draining ~$800k.

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

**Target:** Lending pool oracle reading Balancer/Curve LP token prices
**Root cause:** Sturdy's price oracle read Balancer pool state (BPT rate) during a join/exit, when the pool was mid-transaction and the LP token price was manipulable; the lending pool trusted that stale/inflated price for collateral valuation.
**Steps:**
1. Flashloan 50,000 wstETH + 60,000 WETH from Aave V3.
2. Add 1,100 ETH to Curve steCRV pool via `add_liquidity` to mint ~1,023 steCRV.
3. In `Exploiter.yoink`: `joinBalancerPool()` to enter the Balancer stETH-stable pool, then `depositCollateralAndBorrow()` against Sturdy using the inflated LP price.
4. While still inside the Balancer join (pool state inconsistent), call Sturdy `borrow` — oracle reads manipulated BPT price.
5. `exitBalancerPool()` then `withdrawCollateralAndLiquidation()` to extract borrowed funds.
6. Repay Aave flashloan; keep ~$800k profit.
**How found:** Post-mortem analysis: researchers noticed Sturdy's oracle read Balancer BPT rate directly, which is manipulable during a join/exit callback — a classic read-only reentrancy surface.
**Exploitation:** Attacker flash-borrowed wstETH/WETH, minted steCRV, then used a Balancer join callback window to make Sturdy's oracle read an inflated LP token price, borrowed against it, exited the pool, and withdrew/liquidated to net ~$800k.
**Escalation:** Chained Balancer pool join/exit reentrancy with Sturdy's LP-collateral oracle to inflate borrow capacity, then flashloan-funded the whole sequence.
**Insight:** Any lending protocol that prices Balancer/Curve LP tokens by reading pool state on-chain is vulnerable to read-only reentrancy: check whether the oracle reads `getRate()`/`getPoolTokens()` mid-join, and whether a callback (e.g. Balancer `onJoinPool`) can reenter the borrower before the pool settles.

