SNARKeling Treasure Hunt

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

updateVerifier() accepts address(0) — verifier can be permanently bricked

Author Revealed upon completion

Root + Impact

updateVerifier() allows the owner to swap in a new verifier contract while the hunt is paused. Every call to claim() delegates proof validation to verifier.verify(); if verifier holds the zero address, that call will revert unconditionally, making it impossible for anyone to claim a reward.

function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
// @> no check that address(newVerifier) != address(0)
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Risk

Likelihood: Low

  • Requires the owner to pass address(0) — most likely an accidental copy-paste or scripting error during a verifier upgrade, rather than a deliberate act.

  • A compromised or phished owner key could use this as a targeted denial-of-service.

Impact: High

  • All future claim() calls revert with InvalidProof, permanently locking every unclaimed reward inside the contract.

  • Recovery requires calling updateVerifier() again with a valid address, which is only possible while the contract remains paused — the owner must notice the mistake before unpausing.

  • If the owner unpauses without noticing, claim() is broken for all users with no on-chain recovery path short of another pause-and-update cycle.

Proof of Concept

// Owner intends to deploy a new verifier but passes wrong argument
treasureHunt.pause();
treasureHunt.updateVerifier(IVerifier(address(0))); // accepted silently
treasureHunt.unpause();
// Now any claim attempt fails
treasureHunt.claim(validProof, validHash, recipient);
// Reverts: InvalidProof — verifier.verify() called on address(0)

Recommended Mitigation

-
+ function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
+ if (address(newVerifier) == address(0)) revert InvalidVerifier();
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Support

FAQs

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

Give us feedback!