SNARKeling Treasure Hunt

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

updateVerifier() missing zero-address check allows owner to permanently brick all future claims

Author Revealed upon completion

Root + Impact

Description

  • The constructor correctly guards against a zero-address verifier using the InvalidVerifier error. However, updateVerifier() has no equivalent check, allowing the owner to set the verifier to address(0) — either accidentally or maliciously. Once set, all future claim() calls will silently fail verification and revert with InvalidProof, permanently locking participants out.

// Constructor correctly guards this:
constructor(address _verifier) payable {
// @> zero-address check present here
if (_verifier == address(0)) revert InvalidVerifier();
verifier = IVerifier(_verifier);
}
// updateVerifier() does not:
function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
// @> no zero-address check — address(0) accepted silently
verifier = newVerifier;
}

Risk

Likelihood:

  • Requires owner action, accidental mis-paste or wrong address during verifier upgrade

  • Owner is trusted but human error is realistic during contract maintenance

Impact:

  • All future claim() calls revert InvalidProof — participants permanently locked out

  • ETH remains in contract, recoverable by owner via emergency Withdraw(), but hunt is effectively dead

  • InvalidVerifier error is already defined in the contract but unused in this function, clear oversight

Proof of Concept

The hunt.updateVerifier() function is missing an address(0) check.

function testUpdateVerifierZeroAddressBricksContract() public {
vm.startPrank(owner);
hunt.pause();
hunt.updateVerifier(IVerifier(address(0))); // accepted — no zero-address check
hunt.unpause();
vm.stopPrank();
// Any claim now reverts — call to address(0) fails at ABI decoding level
// before InvalidProof is even reached
vm.prank(attacker);
vm.expectRevert();
hunt.claim(hex"deadbeef", bytes32(uint256(1)), payable(recipient));
}

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;
}

Support

FAQs

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

Give us feedback!