SNARKeling Treasure Hunt

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

`updateVerifier()` accepts `address(0)`; the hunt is bricked until the owner updates agai

Description

updateVerifier() at lines 263-269 lacks the zero-address guard that the constructor applies at line 68. If the owner passes address(0) (or any address with no code) by mistake, every subsequent claim() reverts inside verifier.verify(...) because a call to a code-less address returns empty bytes, which Solidity 0.8 cannot decode as bool.

// constructor line 68
if (_verifier == address(0)) revert InvalidVerifier();
// updateVerifier lines 263-269 — no equivalent 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 address(0) guard
emit VerifierUpdated(address(newVerifier));
}

Risk

Likelihood: low. Requires an owner typo. Impact: temporary denial of claim() until the owner updates again; funds are not lost because emergencyWithdraw remains available.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "forge-std/Test.sol";
import {TreasureHunt} from "../src/TreasureHunt.sol";
import {IVerifier} from "../src/Verifier.sol";
contract PoC_L03 is Test {
TreasureHunt hunt;
address owner = address(0xDEAD);
function setUp() public {
address v = address(0xABCDEF);
vm.etch(v, hex"00");
vm.deal(owner, 200 ether);
vm.prank(owner);
hunt = new TreasureHunt{value: 100 ether}(v);
}
function test_zero_address_accepted() public {
vm.startPrank(owner);
hunt.pause();
hunt.updateVerifier(IVerifier(address(0))); // does NOT revert
hunt.unpause();
vm.stopPrank();
vm.prank(address(0xBAD));
vm.expectRevert(); // claim now always reverts
hunt.claim(hex"de", bytes32(uint256(1)), payable(address(0xA11CE)));
}
}

Recommended Mitigation

function updateVerifier(IVerifier newVerifier) external {
require(paused, "THE_CONTRACT_MUST_BE_PAUSED");
require(msg.sender == owner, "ONLY_OWNER_CAN_UPDATE_VERIFIER");
+ require(address(newVerifier) != address(0), "INVALID_VERIFIER");
+ require(address(newVerifier).code.length > 0, "VERIFIER_NOT_CONTRACT");
verifier = newVerifier;
emit VerifierUpdated(address(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!