Beatland Festival

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

The function FestivalPass::withdraw lacks security validation for the target parameter.

The function FestivalPass::withdraw lacks security validation for the target parameter.

Description

  • if the target is a contract, rather than an EOA (Externally Owned Account) address.

  • If the target contract does not have the appropriate functions to handle incoming Ether, the ETH will remain stuck in the target contract permanently.

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

Risk

Likelihood:

  • When the contract owner calls the withdraw function;

Impact:

  • the administrator loses control over the funds after the withdrawal.

Proof of Concept

the administrator calls the withdraw function with the address of the BlackHoleReceiver contract as the parameter.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
/// @title BlackHoleReceiver - A contract that can receive ETH but is unable to send it out
contract BlackHoleReceiver {
/// @notice Receive function to accept incoming ETH
receive() external payable {
// ETH is accepted but no further action is taken
}
/// @notice Returns the current ETH balance of the contract
function getBalance() external view returns (uint256) {
return address(this).balance;
}
// ❌ No withdraw / transfer / call functions, and no selfdestruct
// Therefore, ETH sent to this contract cannot be retrieved — funds are permanently locked
}

Recommended Mitigation

Ensure the withdraw function is designed to fully support secure fund transfers.

// To maximize the likelihood of a successful transfer;
function withdraw(address target) external onlyOwner {
+ require(target != address(0), "Invalid target");
- payable(target).transfer(address(this).balance);
+ (bool success, ) = payable(target).call{value: address(this).balance}("");
+ require(success, "ETH transfer failed");
}

Ensure the target contract has properly implemented complete and secure functions for handling ETH transfers.

contract SafeReceiver {
address public owner;
constructor() {
owner = msg.sender;
}
/// @notice Receive function to accept incoming ETH
receive() external payable {}
/// @notice Query the current ETH balance of the contract
function getBalance() external view returns (uint256) {
return address(this).balance;
}
/// @notice Allows only the owner to withdraw ETH to a specified address
function withdraw(address payable to, uint256 amount) external {
require(msg.sender == owner, "Not owner");
require(to != address(0), "Invalid target");
require(address(this).balance >= amount, "Insufficient balance");
(bool success, ) = to.call{value: amount}("");
require(success, "ETH transfer failed");
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 25 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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