# Continue claiming reqrds after numberOfEpochs are over

TwabRewards.claimRewards lacks an epochId upper-bound check, letting users claim rewards for epochs beyond numberOfEpochs and drain the contract's token balance.

- Payout: Audit contest
- Severity: high
- Program: PoolTogether
- Found by: gpersoon
- Disclosed: 2022-02-03
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-12-pooltogether-findings/issues/20
- Library page: https://bugbountylibrary.com/r/c4%3A2021-12-pooltogether%3AH-03
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** TwabRewards.sol claimRewards/_calculateRewardAmount
**Root cause:** _calculateRewardAmount only requires block.timestamp > epochEndTimestamp but never verifies _epochId < _promotion.numberOfEpochs, so any epoch index past the promotion's range still yields a reward.
**Steps:**
1. Create promotion with numberOfEpochs=1, startTimestamp in the past, epochDuration=1
2. Call `claimRewards(promotionId, 1)` — passes since epoch 1 end timestamp is past
3. Call `claimRewards(promotionId, 2)`, `claimRewards(promotionId, 3)`, ... — each passes the epoch-not-over require and returns tokensPerEpoch
4. Repeat until contract token balance is exhausted
**How found:** Code review of _calculateRewardAmount: noticed the only guard is the epoch-end timestamp check, and no comparison against numberOfEpochs exists anywhere in the claim path.
**Exploitation:** Attacker repeatedly calls claimRewards with incrementing _epochId values beyond numberOfEpochs; each call mints a fresh reward since the claimed-epoch bitmap only tracks the specific index, draining tokens left by unclaimed epochs or shared with other promotions.
**Insight:** When a reward/vesting function validates time windows, also check the index/epoch is bounded by the configured count — timestamp checks alone don't cap iteration.
