# PolterFinance: flash-loan collateral trick drains all lending reserves

PolterFinance on Fantom was drained of ~$7M via a flash-loan-funded deposit of 1 wei of SpookyToken collateral, then borrowing every reserve's full aToken balance.

- Payout: Real-world hack
- Severity: critical
- Program: PolterFinance
- Credited: DeFiHackLabs
- Disclosed: 2024-11-19
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2024-11/PolterFinance_exploit.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20241119-polterfinance-exploit
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:chained vuln:business-logic vuln:flash-loan vuln:oracle-manipulation

**Target:** Fantom lending pool (Aave-fork) at 0x867fAa51b3A437B4E2e699945590Ef4f2be2a6d5
**Root cause:** The lending pool's borrow accounting did not properly value/validate the depositor's collateral (a 1e18 SpookyToken deposit) before allowing borrows of the entire aToken liquidity of every reserve; the deposit/borrow path lacked a working health-factor check.
**Steps:**
1. Flash-borrow all SpookyToken from the WFTM/SpookyToken Uniswap V3 pool via `flash()`.
2. In `uniswapV3FlashCallback`, swap the borrowed SpookyToken through the JFTM/SpookyToken V2 pair to obtain WFTM, then swap WFTM back to SpookyToken via the router.
3. In `uniswapV2Call`, approve and `deposit(spookyToken, 1e18, address(this), 0)` into the PolterFinance pool.
4. For each reserve (WFTM, MIM, sFTMX, axlUSDC, wBTC, wETH, USDCe, wSOL): read `getReserveData`, then `borrow(token, token.balanceOf(reserveData.aTokenAddress), 2, 0, address(this))` to drain the full aToken balance.
5. Transfer borrowed tokens to the owner; repay the V2 pair `(a1*1000)/998 + 1` and the V3 flash loan `needToRepay`.
6. Send remaining SpookyToken and WFTM to the owner.
**How found:** Not described in the text; PoC shows the attacker identified that a minimal SpookyToken deposit was accepted as sufficient collateral to borrow the full liquidity of every reserve.
**Exploitation:** Attacker flash-borrowed SpookyToken from a Uniswap V3 pool, routed it through a V2 pair and the router to build a position, deposited a trivial 1e18 SpookyToken as collateral into the PolterFinance pool, then looped over every reserve calling `borrow` for the entire aToken balance and forwarded the proceeds to the attacker EOA. Flash loan repaid at the end; ~$7M net loss.
**Escalation:** Single deposit unlocked borrowing across all eight reserves simultaneously, maximizing drain in one transaction.
**Insight:** On Aave-fork lending pools, test whether a dust deposit (1 wei/1e18) can pass the health-factor check and unlock `borrow` of the entire aToken balance per reserve — the borrow loop over `getReserveData(...).aTokenAddress` is the drain primitive.
