Description
The _payout function does not check if the recipient address is the zero address (0x0) before transferring ETH, which could lead to permanent loss of funds.
Expected Behavior
The function should validate that the recipient address is not the zero address before attempting to transfer ETH.
Actual Behavior
The function transfers ETH to any address provided, including the zero address, which would result in the funds being permanently lost.
Root Cause
The function lacks a validation check for the zero address:
function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
}
}
Risk Assessment
Impact
If ETH is sent to the zero address, the funds will be permanently lost, resulting in financial loss for the protocol or its users.
Likelihood
The likelihood is low because it would typically require a programming error or deliberate action to pass the zero address as a recipient.
Proof of Concept
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/BidBeastsNFTMarketPlace.sol";
import "../src/BidBeasts_NFT_ERC721.sol";
contract ExploitTest is Test {
BidBeastsNFTMarket market;
BidBeasts nft;
function setUp() public {
nft = new BidBeasts();
market = new BidBeastsNFTMarket(address(nft));
vm.deal(address(market), 1 ether);
}
function testZeroAddressPayout() public {
uint256 initialBalance = address(market).balance;
console.log("Initial market balance:", initialBalance);
vm.prank(address(market));
market.exposed_payout(address(0), 1 ether);
console.log("Market balance after:", address(market).balance);
assertEq(address(market).balance, initialBalance - 1 ether, "Funds were sent to zero address");
}
}
contract ExposedMarket is BidBeastsNFTMarket {
constructor(address _nft) BidBeastsNFTMarket(_nft) {}
function exposed_payout(address recipient, uint256 amount) public {
_payout(recipient, amount);
}
}
Recommended Mitigation
Add a zero address check to the _payout function:
function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
}
}
function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
require(recipient != address(0), "Cannot send to zero address");
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
}
}
Explanation
The fixed implementation adds a check to ensure that the recipient address is not the zero address, preventing accidental loss of funds.