# Stealing fund by applying reentrancy attack on removeCollateral, startLiquidationAuction, and purchaseLiquidationAuctionNFT

Reentrancy via ERC721 safeTransferFrom callbacks in removeCollateral/startLiquidationAuction/purchaseLiquidationAuctionNFT lets attacker zero out debt and keep all collateral, stealing borrowed funds.

- Payout: Contest reward
- Severity: high
- Program: Backed Protocol
- Credited: HE1M
- Disclosed: 2023-01-31
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-12-backed-findings/issues/102
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-backed%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained vuln:accounting-error vuln:flash-loan vuln:liquidation vuln:reentrancy

**Target:** PaprController.sol (removeCollateral, startLiquidationAuction, purchaseLiquidationAuctionNFT)
**Root cause:** removeCollateral sends NFT via safeTransferFrom before the debt/max-debt check, and purchaseLiquidationAuctionNFT zeroes remaining debt when isLastCollateral, so reentering during the transfer lets the attacker manipulate vault state so the final debt check passes with debt wiped.
**Steps:**
1. Flash loan 500k USDC; buy 10 NFTs (~50k each).
2. `addCollateral` all 10 NFTs.
3. `increaseDebt` max (~250k PaprToken).
4. `removeCollateral` NFT id 1; in `onERC721Received` callback reenter `removeCollateral` for ids 2..9.
5. On 9th callback call `startLiquidationAuction` for NFT id 10 (count now 1, debt > maxDebt so liquidatable).
6. Call `purchaseLiquidationAuctionNFT` for id 10 at startPrice (~3x oracle = 150k); isLastCollateral=true, remaining debt 115k is zeroed via `_reduceDebtWithoutBurn`.
7. Unwind: outer removeCollateral debt checks now pass (debt=0); sell NFTs, repay flash loan, keep ~100k profit.
**How found:** Code review of removeCollateral ordering: NFT transferred out via safeTransferFrom before the debt>max check, and purchaseLiquidationAuctionNFT's isLastCollateral branch zeroes remaining debt — combining these reveals a reentrancy path to erase debt.
**Exploitation:** Single-transaction flash-loan-funded attack: borrow max against 10 NFTs, reenter removeCollateral via ERC721 receive hook to strip 9 NFTs, trigger self-liquidation auction on the last NFT, buy it back at 3x oracle price; purchaseLiquidationAuctionNFT treats it as last collateral and writes off remaining debt, so attacker repays only 150k of 250k borrowed and recovers all collateral.
**Escalation:** Flash loan scales capital; attacker needs >=7 NFTs (n*price*50% > price*3) to profit. Backed noted a simpler variant: add one NFT, borrow max, start auction, purchase, reenter via safeTransferFrom to add more NFTs and borrow max, then debt is zeroed.
**Insight:** When a protocol transfers an NFT (safeTransferFrom) before validating solvency, and a separate function zeroes debt on 'last collateral', chain the two: reenter during the transfer to drain collateral down to one, self-liquidate, and buy back so the debt-writeoff branch fires. Check ordering of state checks vs external calls in every collateral-removal path.
