Wrong variable in claimed check allows same proof to be replayed, fully draining the contract.
The claim() function is designed to prevent the same treasure from
being claimed twice by checking claimed[treasureHash] before paying
out a reward.
However, the double-spend guard mistakenly checks claimed[_treasureHash]
where _treasureHash is an immutable state variable never assigned in
the constructor, making it permanently bytes32(0). Since claimed[bytes32(0)]
is never set to true, the check always passes and the same proof can
be replayed until all 100 ETH is drained.
Likelihood:
Any attacker can monitor the blockchain for the first legitimate claim() transaction and copy the proof, treasureHash, and recipient arguments directly from the calldata no special skills or tools required
The attack requires zero privileged access any externally owned account can call claim() with the copied arguments
All 10 claim slots can be exhausted in a single block since there is no cooldown, rate limiting, or time lock between callsImpact:
Impact:
The entire contract balance of 100 ETH can be drained by a single attacker using one valid proof submitted 10 times
All 9 remaining legitimate treasure finders who physically found real treasures are permanently denied their rewards since the contract is left with zero balance
The core protocol guarantee that only real treasure finders get paid is completely broken
Funds sent out via claim() are unrecoverable since no admin function can reverse completed ETH transfers
The result after test:
Replace _treasureHash with treasureHash in the double-spend guard so it checks the actual submitted treasure hash parameter instead of the permanently zero immutable variable.
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.
The contest is complete and the rewards are being distributed.