SNARKeling Treasure Hunt

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

`updateVerifier()` lacks the zero-address and contract-code validation the constructor performs, allowing the owner to brick all future claims

Author Revealed upon completion

Scope: contracts/src/TreasureHunt.sol

Root + Impact

Description

The constructor guards against a zero verifier address:

constructor(address _verifier) payable {
if (_verifier == address(0)) revert InvalidVerifier();
...
verifier = IVerifier(_verifier);
...
}

But updateVerifier() has no equivalent sanity check:

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

An owner who fat-fingers a zero address (or points the contract at an
EOA or a non-conforming contract) silently replaces the verifier. The
emitted VerifierUpdated event still reports success. After
unpause(), every subsequent claim() reverts (either during the
external call, or because the verifier returns false), bricking the
hunt until the owner corrects the value.

Risk

Likelihood: MEDIUM — requires an owner-mistake, not an external
exploit; but the contract documents the owner as trusted, so
accidental misuse is the realistic concern.

Impact: LOW — no ETH is lost (owner can reset to the correct
verifier and resume, or emergencyWithdraw to recover); the hunt is
temporarily DoS'd.

Proof of Concept

vm.prank(owner);
hunt.pause();
vm.prank(owner);
hunt.updateVerifier(IVerifier(address(0))); // no revert; emits VerifierUpdated
vm.prank(owner);
hunt.unpause();
vm.prank(participant);
hunt.claim(proof, treasureHash, recipient); // reverts on verifier.verify()

Recommended Mitigation

Mirror the constructor's check:

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();
+ require(address(newVerifier).code.length > 0, "VERIFIER_NOT_CONTRACT");
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Disclosure

This finding was identified and written up with the assistance of an
autonomous AI auditor (Anthropic Claude).

Support

FAQs

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

Give us feedback!