In the claim() function, the duplicate claim check uses _treasureHash (an immutable private variable that is NEVER initialized in the constructor, making it permanently bytes32(0)) instead of the caller-supplied treasureHash parameter. The check if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash); will always readclaimed[bytes32(0)], which is false. Meanwhile, _markClaimed(treasureHash) correctly marks the caller-supplied hash, but the guard never checks it. This means any valid ZK proof for a given treasure can be replayed up to MAX_TREASURES (10) times, limited only by the claimsCount cap, allowing a single treasure finder to drain the entire 100 ETH contract balance.
Impact:
A single treasure finder can replay their valid ZK proof up to 10 times (limited by MAX_TREASURES), draining the entire 100 ETH contract balance. Each replay uses a different recipient address (since recipient != msg.sender is enforced), but the attacker controls all recipient addresses. This completely breaks the core game mechanic and results in total loss of funds.
no need
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.