TreasureHunt.withdraw() is intended to be an owner-only function that sweeps the contract balance to the owner once all treasures have been claimed. However, the function lacks the onlyOwner modifier. Any external caller can invoke withdraw() once claimsCount >= MAX_TREASURES, forcing the fund sweep to execute at a time of the attacker's choosing rather than the owner's.
While funds are always sent to owner and cannot be redirected, this breaks the protocol's intended access control and allows a malicious actor to grief the owner by triggering the withdrawal at an unfavourable moment (e.g., front-running the owner's own withdrawal call, or triggering it mid-hunt if the claims count condition is somehow met early via the _treasureHash bypass).
Likelihood:
The function is public with no access restriction; any watcher of the mempool can call it the moment claimsCount reaches MAX_TREASURES
Combined with the _treasureHash bypass (separate Critical finding), claimsCount can be incremented rapidly, making this condition reachable immediately
Impact:
Funds always reach the owner, so there is no theft
The owner loses control over the timing of the withdrawal
An attacker can front-run the owner's withdrawal transaction, denying the owner the ability to choose when to sweep
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.