Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Use of transfer() for ETH Withdrawal

Use of transfer() for ETH Withdrawal

Description

  • Using .transfer() forwards only 2300 gas to the target. If the recipient is:

    • A smart contract that needs more than 2300 gas to execute fallback/receive logic,

    • Or has a complex fallback (e.g., logging, forwarding ETH, storing values),

    → The transfer will fail, potentially locking ETH in the contract forever.

    function withdraw(address target) external onlyOwner {
    payable(target).transfer(address(this).balance);
    }

    Risk

Likelihood:

  • Transfer failures: ETH will fail to reach the recipient if it’s a contract needing more than 2300 gas to receive ETH.

  • Locked funds: All ETH held in the contract could become permanently inaccessible.

  • Owner confusion: Owners might assume ETH has been withdrawn successfully when in fact it failed silently.

Impact:

  • Medium: Funds are not stolen, but they can be frozen.

  • Projects relying on smart contracts to receive ETH (like Gnosis Safe wallets) will be incompatible.

  • Fixing the issue may require redeployment or contract upgrades, especially if the contract lacks upgradeability features.

Proof of Concept

// Malicious or incompatible contract that needs more than 2300 gas to receive ETH
contract RevertingReceiver {
receive() external payable {
// Uses too much gas and fails with .transfer()
for (uint256 i = 0; i < 100; i++) {
// Dummy expensive operation
keccak256(abi.encodePacked(i));
}
}
}

Now imagine this is called:

festivalPass.withdraw(address(new RevertingReceiver()));

This will revert and block ETH withdrawal — even if the receiver is legitimate.


Recommended Mitigation

  • Use the low-level .call{value: amount}("") instead. It forwards all available gas and returns a success flag:

function withdraw(address target) external onlyOwner {
(bool success, ) = payable(target).call{value: address(this).balance}("");
require(success, "ETH transfer failed");
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.