withdraw() lacks access control, allowing any actor to trigger owner settlementEvery other administrative function in the contract (fund, pause, unpause, updateVerifier, emergencyWithdraw) is restricted to the owner. withdraw() is the only exception, containing no access control and callable by any external address once claimsCount >= MAX_TREASURES.
Funds always go to owner, so no direct theft is possible. However, any actor can front-run the owner's intended settlement transaction, causing the owner's tx to revert with NO_FUNDS_TO_WITHDRAW and forcing the owner to rely on third parties for execution of a semantically privileged lifecycle step.
Likelihood:
Any external address can call withdraw() once claimsCount >= MAX_TREASURES, meaning the condition for exploitation is met at the natural end of every hunt cycle without any special precondition.
A griefer monitoring the mempool can front-run the owner's settlement transaction on every withdrawal attempt, consistently forcing the owner's tx to revert with NO_FUNDS_TO_WITHDRAW.
Impact:
Inconsistent access control design, as withdraw() is the only admin function not restricted to the owner, undermining the contract's intended privileged lifecycle model.
Owner settlement can be griefed into a revert, forcing the owner to rely on third parties for execution of a semantically privileged lifecycle step.
Add an onlyOwner check, consistent with every other admin function in the contract, or use the existing onlyOwner modifier:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.