SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
Submission Details
Impact: low
Likelihood: high

Duplicate entry in ALLOWED_TREASURE_HASHES means only 9 unique treasures exist, one reward permanently unreachable

Author Revealed upon completion

Root + Impact

Description

  • The circuit defines 10 allowed treasure hashes baked into ALLOWED_TREASURE_HASHES, matching MAX_TREASURES = 10 in the contract. However, indices 8 and 9 contain identical values. Since each treasure hash corresponds to a unique physical secret, only 9 distinct secrets exist. The 10th reward slot can never be claimed by a unique finder — it is a duplicate of treasure 8.

// Baked-in set of 10 allowed treasure hashes (pedersen hashes).
global ALLOWED_TREASURE_HASHES: [Field; 10] = [
1505662313093145631275418581390771847921541863527840230091007112166041775502,
-7876059170207639417138377068663245559360606207000570753582208706879316183353,
-5602859741022561807370900516277986970516538128871954257532197637239594541050,
2256689276847399345359792277406644462014723416398290212952821205940959307205,
10311210168613568792124008431580767227982446451742366771285792060556636004770,
-5697637861416433807484703347699404695743570043365849280798663758395067508,
-2009295789879562882359281321158573810642695913475210803991480097462832104806,
8931814952839857299896840311953754931787080333405300398787637512717059406908,
// @> indices 8 and 9 are identical
-961435057317293580094826482786572873533235701183329831124091847635547871092,
-961435057317293580094826482786572873533235701183329831124091847635547871092
];

Risk

Likelihood:

  • Hardcoded in the circuit — present from deployment, affects every hunt instance

  • Trivially verifiable by inspection

Impact:

  • Only 9 unique physical treasures exist despite the contract promising 10 rewards

  • The finder of treasure 8 can claim twice using the same secret (same hash, same proof)

  • One 10 ETH reward slot is either permanently unclaimable or double-claimable by a single finder

  • Participants are misled — MAX_TREASURES = 10 and getRemainingTreasures() both suggest 10 prizes

Proof of Concept

No on-chain PoC needed — the duplicate is directly visible by comparing ALLOWED_TREASURE_HASHES[8] and ALLOWED_TREASURE_HASHES[9] in circuits/src/main.nr:

ALLOWED_TREASURE_HASHES[8] = -961435057317293580094826482786572873533235701183329831124091847635547871092
ALLOWED_TREASURE_HASHES[9] = -961435057317293580094826482786572873533235701183329831124091847635547871092

Recommended Mitigation

- -961435057317293580094826482786572873533235701183329831124091847635547871092
+ <unique_hash_for_tenth_treasure>

Support

FAQs

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

Give us feedback!