# Anyone can steal all distributed rewards

Self-transfer while in rebase reuses stale cached shares, minting phantom rewards and letting anyone drain all unminted distributed rewards.

- Payout: Contest reward
- Severity: high
- Program: Ethereum Credit Guild
- Credited: evmboi32
- Disclosed: 2024-02-22
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/991
- Library page: https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:reward-distribution

**Target:** ERC20RebaseDistributor.sol transfer() rebase accounting
**Root cause:** transfer() caches rebasingState for msg.sender and to in memory; when msg.sender==to, the storage update to nShares is not reflected in the cached rebasingStateTo, so _shares2balance uses stale shares plus the amount, over-minting rewards.
**Steps:**
1. Alice enters rebase with 100 credit (X shares); unmintedRebaseRewards() >= 100e18.
2. Alice calls `transfer(alice, 100e18)` in one atomic tx.
3. First if mints 0 (fresh rebase); second if sets storage rebasingState[alice].nShares=0 but cached rebasingStateTo.nShares stays X.
4. Final if computes toBalanceAfter = _shares2balance(X, price, amount, rawToBalanceAfter) = 200 tokens vs raw 100, minting 100e18 phantom rewards.
5. Alice calls `exitRebase()` and keeps the stolen tokens.
**How found:** Code-reading of transfer() rebase branch: noticed rebasingStateFrom and rebasingStateTo are separate memory copies, and when from==to the storage write to nShares does not update the cached to-state, causing double-counting.
**Exploitation:** Attacker enters rebase, self-transfers an amount up to unmintedRebaseRewards(), then exits rebase atomically; phantom minted rewards are kept. Can frontrun any tx changing unmintedRebaseRewards to repeatedly rob all pending rewards with zero risk.
**Escalation:** Mempool frontrunning of every distribute/reward-changing tx to continuously drain unminted rewards.
**Insight:** In rebase/share-based tokens, audit self-referential transfers (from==to) and any path where from/to state is cached in memory before a storage mutation; aliasing of the two cached structs silently double-counts shares.
