When a user calls claim(), the contract verifies a ZK proof and if valid — sends 10 ETH to the recipient and marks the treasure as claimed so nobody can claim it again.
The problem is that the anti-double-claim check uses the wrong variable. It checks claimed[_treasureHash] where _treasureHash is an immutable field that is never set in the constructor, so it's always bytes32(0). But the actual write goes to claimed[treasureHash] (the parameter). So the check and the write are looking at completely different storage slots — the check never sees the write and always passes.
Likelihood:
Anyone who gets a valid proof for any single treasure can replay the exact same call 10 times in a row — there's nothing stopping them.
No special access or setup needed, just a normal wallet and one proof generated off-chain.
Impact:
The attacker drains all 100 ETH from the contract in 10 transactions.
All claim slots get used up, so no other player can ever claim any treasure.
The attacker generates one valid proof for any treasure, then replays the same (proof, treasureHash, recipient) triple 10 times. Each call passes the claimed[bytes32(0)] check (always false) and transfers 10 ETH, draining the full contract balance.
Just remove the unused _treasureHash field and fix the check to use the function parameter:
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.