# PresaleV5: spot-price oracle manipulation drains presale via flash loan

PresaleV5 priced buyWithEth from a manipulable Uniswap V3 slot0, letting a flash-loan attacker skew the pool and buy discounted presale tokens for ~2.3 ETH profit.

- Payout: Real-world hack
- Severity: high
- Program: PresaleV5
- Credited: DeFiHackLabs
- Disclosed: 2025-08-19
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-08/PresaleV5_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250819-presalev5
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained vuln:business-logic vuln:flash-loan vuln:oracle-manipulation

**Target:** PresaleV5 buyWithEth / fetchPrice oracle on Uniswap V3 pool
**Root cause:** buyWithEth quotes token price from the current slot0 of a Uniswap V3 pool with no TWAP or independent oracle, so the price is manipulable within a single transaction.
**Steps:**
1. Flash-borrow 502,970,865,652,897,166,554,244,795 sale tokens from V3 pool 0x9DeDf5bb... at 1% fee
2. In uniswapV3FlashCallback, approve router and swap borrowed sale tokens into WETH on the manipulated pool 0xA3C2076e... (fee 3000), crashing the sale-token price
3. Unwrap WETH; call `presale.fetchPrice(1 ether)` to read the depressed quote
4. Call `presale.buyWithEth{value: balance}(balance*17/100/quote, false)` to buy cheap presale tokens
5. Loop 200x: wrap ETH, swap sale tokens back into WETH on the manipulated pool, re-read fetchPrice, buy 0.3 ETH worth of presale tokens at the skewed quote
6. Repay flash loan (borrowed + fee1) in sale tokens, using exactOutputSingle to acquire any shortfall
7. Unwrap remaining WETH and send ETH profit to attacker EOA
**How found:** Attacker recognized that fetchPrice/buyWithEth read live slot0 from a thin V3 pool, making the presale price directly controllable by a large swap funded by a flash loan.
**Exploitation:** Single-transaction flash-loan attack: borrow sale tokens, dump them into the oracle pool to depress price, buy presale tokens at the manipulated low quote via buyWithEth, sell received tokens back into the pool, repeat 200 times, repay loan, keep ~2.3 ETH.
**Escalation:** Iterative 200-round loop compounds the price skew to maximize discounted purchases within one flash-loan transaction.
**Insight:** When a sale/IDO contract quotes price via fetchPrice from a Uniswap V3 slot0, check pool liquidity depth and whether a single swap can move the quote; flash-borrow the sale token itself to dump into the oracle pool and buy discounted inventory in a loop.
