SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Wrong hash for treasure checked in claim function

Wrong hash claimed in the claim function leading to a douple spend or replay

Description

  • There is use of the variable '_treasureHash' in the 'claim()' function to check if it was already claimed and thereby prevent a replay attack.

  • The issue however is that the '_treasureHash' variable is never set and the '_markClaimed' mapping uses 'treasureHash' which is a totally diffrent variable

  • Since the variable is never set, it defaults to 0x0 and when claiming, the function always asks if 0x0 has been claimed. After the first claim of any treasure, claimed[0x0] becomes true thereby blocking other claims and the actual treasure parameter is never checked, allowing the same treasure to be claimed mutliple times

@> if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);

Risk

Likelihood:

  • Since the check to prevent already claimed treasuries from being claimed again is faulty, there is as a result no way of stopping treasuries from being reclaimed as many times as possible by malicious users

Impact:

  • Every treasure can be drained as a direct conseqeunce of these poor checks

Proof of Concept

The following proof of concept explains the attack above

Alice claims treasure hash 0xabc123... → passes (since claimed[0x0] is false)
claimed[0x0] is set to true
Bob tries to claim treasure hash 0xdef456... → reverts (because claimed[0x0] is now true)
But Alice could claim 0xabc123... again with a new valid proof since claimed[0xabc123...] was never actually checked

Recommended Mitigation

The mitigation is simple, simply do the right check for the correct variable

- if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);
+ if (claimed[treasureHash]) revert AlreadyClaimed(treasureHash);
Updates

Lead Judging Commences

s3mvl4d Lead Judge 18 days ago
Submission Judgement Published
Validated
Assigned finding tags:

broken double-claim protection

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!