# CHALLENGER_REWARD can be used to drain reserves and free mint

Position owner sets an inflated liquidation price then self-challenges; the 2% CHALLENGER_REWARD is computed from the fake price, draining reserves and minting unlimited ZCHF.

- Payout: Contest reward
- Severity: high
- Program: Frankencoin
- Credited: Lirios
- Disclosed: 2023-06-09
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-04-frankencoin-findings/issues/458
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-frankencoin%3AH-06
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:oracle-manipulation

**Target:** MintingHub / Position auction challenge mechanism (Solidity)
**Root cause:** The challenger reward is computed as 2% of volumeZCHF = price * size, where price is owner-controlled and unbounded; when the position lacks funds, notifyLoss pulls from reserve and mints new ZCHF.
**Steps:**
1. Owner opens a Position with `_challengeSeconds = 0` and `_liqPrice = type(uint256).max / 1e20` (huge inflated price), 1 xchf collateral, paying 1000 ZCHF opening fee.
2. Owner calls `hub.launchChallenge(myPosition, 1 ether)` supplying 1 xchf collateral.
3. No bid can satisfy `_bidAmountZCHF * ONE_DEC18 >= price * _collateralAmount`, so the challenge always succeeds.
4. Owner calls `hub.end(challengeID)`; reward = `(volume * CHALLENGER_REWARD) / 1000_000` with volume = `_mulD18(price, _size)`.
5. Position lacks funds, so `FrankenCoin.notifyLoss()` drains the reserve and mints the remainder to the challenger.
**How found:** Code review of MintingHub.end() reward math: noticed reward derives from owner-set price via notifyChallengeSucceeded's volumeZCHF, and that notifyLoss mints when position is short.
**Exploitation:** Attacker needs ~1000 ZCHF for the opening fee plus 1 unit collateral. Open position with zero challenge period and max price, immediately launchChallenge and end() in one flow; challenger reward is paid from reserve then minted, yielding ~2.3e37 ZCHF and zeroing the reserve.
**Escalation:** Setting `_challengeSeconds = 0` allows open+challenge+end in a single transaction, making the drain atomic and repeatable.
**Insight:** When a payout formula multiplies an owner-controlled parameter (price) by size, check whether that parameter is bounded and whether the payout source (reserve/mint) can be forced by making the position insolvent; also check whether challenge/auction timing can be collapsed to zero.
