SNARKeling Treasure Hunt

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

updateVerifier() Does Not Validate New Verifier Address Can Be Set to address(0)

Author Revealed upon completion

Root + Impact

Description

  • The constructor correctly validates if (_verifier == address(0)) revert InvalidVerifier().

  • updateVerifier(), which replaces the verifier at runtime, performs no such check, allowing the owner to accidentally (or maliciously, post-key-compromise) set the verifier to address(0) or any non-contract address.

function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
// @> No zero-address or code-size check on newVerifier
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Risk

Likelihood:

  • Requires a compromised or negligent owner key unlikely but non-zero.

Impact:

  • Setting verifier = address(0) causes verifier.verify(...) to revert on all future claim() calls, permanently bricking the hunt without a recovery path while the contract still holds funds.

  • Even if caught quickly, the contract must be unpaused → re-paused → verifier updated, during which time the hunt is halted.

Proof of Concept

function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
// @> No zero-address or code-size check on newVerifier
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Recommended Mitigation

Mirror the constructor's validation in updateVerifier():

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();
// Optional: verify new address has deployed code
uint256 size;
assembly { size := extcodesize(newVerifier) }
require(size > 0, "VERIFIER_NOT_A_CONTRACT");
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Support

FAQs

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

Give us feedback!