# Anyone can prevent themselves from being liquidated as long as they hold one of the supported NFTs

removeFeeder() lacked admin auth, letting anyone delete all NFTFloorOracle feeders so getPrice() reverts on staleness, blocking liquidateERC721() and letting insolvent borrowers evade liquidation.

- Payout: Contest reward
- Severity: high
- Program: ParaSpace
- Credited: IllIllI
- Disclosed: 2023-04-27
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-11-paraspace-findings/issues/402
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-paraspace%3AH-04
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:dos vuln:liquidation vuln:oracle-manipulation

**Target:** NFTFloorOracle / ERC721OracleWrapper price oracle in ParaSpace lending pool
**Root cause:** removeFeeder() used onlyWhenFeederExisted (an existence check, not auth) instead of onlyRole(DEFAULT_ADMIN_ROLE); the internal _removeFeeder already re-checks existence, so the external guard provided no access control.
**Steps:**
1. Call `NFTFloorOracle.removeFeeder(feeder)` for each registered feeder as any unprivileged address.
2. Each call revokes UPDATER_ROLE from the feeder and pops it from `feeders`.
3. With no feeders, `setPrice()` can no longer be called (onlyRole(UPDATER_ROLE)), so `assetPriceMap[_asset].updatedAt` stops advancing.
4. After EXPIRATION_PERIOD (1800 blocks), `getPrice()` reverts with 'NFTOracle: asset price expired'.
5. `ERC721OracleWrapper.latestAnswer()` -> `ParaSpaceOracle.getAssetPrice()` -> `GenericLogic._getAssetPrice()` -> `calculateUserAccountData()` -> `LiquidationLogic.executeLiquidateERC721()` all propagate the revert, so `PoolCore.liquidateERC721()` reverts.
**How found:** Code review of NFTFloorOracle access modifiers: noticed the comment claims owner-only but the modifier is onlyWhenFeederExisted, which performs no caller authentication, and traced the revert path from getPrice() up through the oracle wrapper into liquidation logic.
**Exploitation:** A borrower near liquidation removes all feeders, freezing NFT prices and making every liquidateERC721() call revert. The position stays open, giving the borrower a free option on whether prices recover before admins restore feeders; the attacker can re-remove feeders after any admin fix. A competitor could time removal to block liquidations at a chosen price threshold.
**Escalation:** Attacker can repeat removal after admin re-adds feeders, sustaining the DoS indefinitely; affects all NFT collateral except UniswapV3 positions.
**Insight:** When a function's docstring says 'owner only' but the modifier is an existence/state check, verify the modifier actually authenticates msg.sender; also check whether the internal callee already enforces the same state check, revealing the external guard as decorative. Trace oracle revert propagation into liquidation paths to gauge DoS impact.
