SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
View results
Submission Details
Severity: low
Valid

`updateVerifier` allows zero address; constructor does not

Root + Impact

Description

  • Normal behavior: The verifier address should always be non-zero, matching constructor rules, including after admin updates.

  • Problem: The constructor reverts when the verifier is address(0). updateVerifier assigns verifier = newVerifier with no zero check. An owner mistake while paused bricks all future claim calls until a valid verifier is set again.

// @> constructor rejects zero
if (_verifier == address(0)) revert InvalidVerifier();
// @> update path allows zero — inconsistent with constructor
function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
verifier = newVerifier;
emit VerifierUpdated(address(newVerifier));
}

Risk

Likelihood:

  • Owner calls updateVerifier with zero while paused during a botched rotation or compromised key scenario.

  • Unlikely in steady state; operator error is the main path.

Impact:

  • Claims revert until a non-zero verifier is configured; no user payouts during the outage.

Proof of Concept

Explanation: After the owner sets verifier to address(0) via updateVerifier, claim cannot complete verification. The Foundry test below encodes that sequence; it requires fixture files from circuits/scripts/build.sh (for _fixture()).

Supporting code — run:

cd circuits && ./scripts/build.sh
cd ..
forge test --match-test testPoC_UpdateVerifierZeroBricksClaims -vv

Supporting code — test:

// contracts/test/TreasureHuntPoC.t.sol — testPoC_UpdateVerifierZeroBricksClaims
function testPoC_UpdateVerifierZeroBricksClaims() public {
(bytes memory proof, bytes32 treasureHash, address payable recipient) = _fixture();
vm.startPrank(OWNER);
hunt.pause();
hunt.updateVerifier(IVerifier(address(0)));
hunt.unpause();
vm.stopPrank();
vm.startPrank(PARTICIPANT);
vm.expectRevert();
hunt.claim(proof, treasureHash, recipient);
vm.stopPrank();
}

Recommended Mitigation

Explanation: Mirror the constructor: reject address(0) so a paused rotation cannot brick claim by pointing verifier at an address with no verify implementation. Reuse the existing InvalidVerifier() error for consistency.

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

Lead Judging Commences

s3mvl4d Lead Judge 18 days ago
Submission Judgement Published
Validated
Assigned finding tags:

no zero-address check in updateVerifier()

The issue is that `updateVerifier()` allows the owner to replace the verifier with an arbitrary address, including `address(0)`, even though the constructor explicitly treats a zero verifier as invalid and reverts with `InvalidVerifier()` during initial deployment. In other words, the contract establishes at deployment time that a null verifier address is not an acceptable configuration, but then fails to preserve that same invariant when the verifier is later updated through the admin recovery path.

Support

FAQs

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

Give us feedback!