# Attacker can freeze users first rewards

Unset withdraw address in Andromeda validator-staking sends auto-distributed rewards to the contract itself; attacker front-runs first claim to freeze them.

- Payout: Contest reward
- Severity: medium
- Program: Andromeda Ado
- Credited: J4X_
- Disclosed: 2024-05-14
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-05-andromeda-ado-judging/issues/50
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-05-andromeda-ado%3AM-11
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:race-window technique:source-review vuln:business-logic vuln:reward-distribution

**Target:** Cosmos SDK staking/distribution contract (andromeda-validator-staking)
**Root cause:** execute_claim sets DistributionMsg::SetWithdrawAddress only right before WithdrawDelegatorReward, but rewards are also auto-distributed on stake changes, jailing, or validator exit; if that happens before the first claim, the default withdraw address (the contract itself) receives the rewards.
**Steps:**
1. Owner stakes via execute_stake without ever calling execute_claim, so withdraw address remains the contract.
2. Attacker observes owner's pending execute_claim tx in mempool.
3. Attacker front-runs with execute_stake of 1 token on behalf of owner (execute_stake is unrestricted).
4. Stake increase triggers reward distribution to the contract's own address before SetWithdrawAddress runs.
5. Owner's claim then sets withdraw address but the already-distributed rewards sit in the contract, unrecoverable due to broken withdraw_funds.
**How found:** Reading Cosmos distribution docs to enumerate all paths that trigger reward distribution (stake change, jailing, validator exit) beyond explicit WithdrawDelegatorReward, then noticing execute_stake is permissionless and SetWithdrawAddress is only sent inside execute_claim.
**Exploitation:** Front-run the owner's first claim with a minimal stake on their behalf to force reward auto-distribution to the contract address, permanently stranding accrued rewards.
**Insight:** When a contract relies on a stateful config message (like SetWithdrawAddress) sent lazily inside one function, enumerate every other code path in the underlying module that consumes that config before it is set; permissionless stake/deposit functions are a cheap trigger.
