The normal behavior of emergencyWithdraw is to let the contract owner safely withdraw any amount of ETH to a chosen recipient address when the contract is paused (for emergency fund recovery).
The specific issue is that the function performs an external low-level ETH transfer (.call{value}) without the nonReentrant modifier that already exists in the contract (and is used on claim()). This violates the Checks-Effects-Interactions pattern and leaves the function exposed to reentrancy.
Likelihood:
Owner calls emergencyWithdraw while the contract is paused and passes a malicious contract as the recipient
Owner account is compromised or tricked into interacting with a malicious recipient (social engineering / phishing)
Impact:
Malicious recipient can re-enter emergencyWithdraw (or trigger other state-changing logic) before the first call completes
Funds can be drained beyond the intended amount (or cause unexpected behavior during an emergency)
Attack flow (for demo):
Deploy MaliciousRecipient pointing to TreasureHunt.
Pause the contract.
Owner calls MaliciousRecipient.attack(amount).
The fallback/receive re-enters emergencyWithdraw before the first transfer finishes.
The contract already implements a clean nonReentrant modifier (and locked state variable) and applies it to claim(). Adding it here is a one-line, zero-risk fix. For consistency you should also consider adding it to the withdraw() function.
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.