Normal behavior:
The withdraw function should safely transfer the contract’s ETH balance to a target address, ensuring no external contract can interfere with or re-enter the withdrawal logic.
Specific issue:
The withdraw function lacks a reentrancy guard. If the target is a contract, its receive() or fallback() function can re-enter withdraw during the ETH transfer, potentially leading to double-withdrawals or other logic errors if the function is ever extended.
Likelihood:
The bug will occur whenever the owner withdraws to a contract address that attempts to re-enter withdraw during the ETH transfer.
The risk increases if future changes add state changes or additional logic after the transfer.
Impact:
Allows a malicious contract to re-enter the withdrawal logic, which could lead to double-withdrawals or other attacks if the function is modified.
Violates best practices and exposes the contract to upgradability and composability risks.
The following Foundry test demonstrates the vulnerability. It deploys a malicious contract that re-enters withdraw during the ETH transfer. The test fails with "Reentrancy was attempted," proving the lack of a reentrancy guard.
To prevent reentrancy, add a reentrancy guard to the withdraw function. The recommended fix is to inherit from OpenZeppelin's ReentrancyGuard and add the nonReentrant 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.