Beatland Festival

AI First Flight #4
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

withdraw uses transfer() which fails for contract recipients

Summary

withdraw uses transfer() which is limited to 2300 gas and will fail if the target is a contract with a receive() function that costs more than 2300 gas.

Vulnerability Details

In FestivalPass.sol:149:

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

transfer() forwards only 2300 gas. If target is a multisig wallet, proxy contract, or any contract whose receive() exceeds 2300 gas, the withdrawal permanently fails. All ETH from pass sales becomes stuck in the contract.

Impact

MEDIUM — ETH from pass sales can become permanently locked if the owner specifies a contract address as the withdrawal target.

Proof of Concept

contract GasHeavyReceiver {
uint256 public x;
receive() external payable { x = 1; } // costs >2300 gas with SSTORE
}
function testWithdrawFails() public {
vm.prank(user1);
festivalPass.buyPass{value: 0.05 ether}(1);
GasHeavyReceiver r = new GasHeavyReceiver();
vm.expectRevert();
festivalPass.withdraw(address(r));
}

Recommended Fix

Use call instead of transfer:

(bool ok,) = target.call{value: address(this).balance}("");
require(ok, "Transfer failed");
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!