The contract is designed to prevent replay attacks by checking if a treasure hash has already been claimed through theclaimedmapping before processing a new claim
The critical bug occurs because the replay protection check uses the wrong variable - it checks claimed[_treasureHash]
(an uninitialized immutable variable that always equalsbytes32(0)) instead ofclaimed[treasureHash]
(the function parameter containing the actual treasure hash being claimed)
Likelihood: HIGH
This will occur every time a valid proof is submitted multiple times
The bug is deterministic and will always bypass replay protection since
claimed[bytes32(0)]
remains false
Impact: CRITICAL
Allows draining the entire 100 ETH prize pool with a single valid proof
Attacker can replay the same proof up to 10 times (limited only by MAX_TREASURES)
Each replay transfers 10 ETH to the attacker's specified recipient address
Total loss: up to 100 ETH from replay exploitation
Remove the unused
_treasureHash
immutable variable and fix the replay protection check to use the correct parameter:
In `claim()`, the guard uses `claimed[_treasureHash]`, where `_treasureHash` is an immutable state variable that is never initialized to the caller-supplied treasure identifier, while the contract later marks `claimed[treasureHash] = true` using the function argument instead. As a result, the duplicate-claim check and the state update are performed against different keys, which means a previously claimed treasure is not actually blocked from being claimed again with the same valid proof and `treasureHash`. This breaks a core invariant of the protocol described in the README, namely, that each treasure can only be redeemed once, and allows one valid treasure/proof pair to be reused to drain rewards repeatedly until either the `MAX_TREASURES` cap or the contract balance is exhausted.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.