# DHC award staking: repeatable claim drains proxy balance

DHC award staking lacked a re-claim guard, letting anyone reset a claimed award with dust and repeatedly claim the fixed reward, draining ~$71.8K USDT.

- Payout: Real-world hack
- Severity: high
- Program: DHC
- Disclosed: 2026-09-05
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-09/DHC_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260905-dhc
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:reverse-engineering technique:source-review vuln:business-logic vuln:reentrancy

**Target:** Upgradeable award-staking proxy on BNB Chain
**Root cause:** The award state machine has no re-claim guard: participateAward() does not require the award to be unclaimed, so a claimed award (status 2) can be reset to status 1 with a dust pledge, and claimAward() pays the fixed reward again from the shared proxy DHC balance.
**Steps:**
1. Flash-loan USDT (DODO), buy DHC on PancakeSwap for seed, register an inviter via `setInviter` (award rejects zero inviter)
2. Call `createAward(reward)` (selector `0x7f200fee`) to record a fixed reward
3. Call `participateAward(id, fullPledge)` (`0xe3db9b54`) then `claimAward(id)` (`0x43609f36`) once, moving status to 2
4. Loop: `participateAward(id, 2 wei)` to reset status 2->1, then `claimAward(id)` to re-pay the fixed reward
5. Repeat until proxy DHC balance drained, dump DHC to USDT on PancakeSwap, repay flash loan, keep surplus
**How found:** Reconstructed from the on-chain trace at block 120055460: the caller was the attacker's contract rather than the proxy owner, and the four unverified award selectors' behavior (argument layout, token movements, status transitions) was inferred from the trace, revealing the missing claimed-flag guard.
**Exploitation:** Attacker flash-loaned ~10K USDT, bought DHC seed, registered an inviter, created a fixed-reward award, claimed once, then looped dust re-pledge + claim to drain the proxy's DHC balance, dumped DHC for USDT, repaid the loan, netting ~71,851 USDT across 4 txs in one block.
**Insight:** On staking/reward contracts, check whether a 'claimed' status can be flipped back by a cheap re-entry (dust pledge) and whether the reward is paid from a shared pool with no per-award reserve — trace status transitions and token outflows to spot repeatable claims.
