Bid Beasts

First Flight #49
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Access Control Vulnerability in withdrawAllFailedCredits

Root + Impact

The withdrawAllFailedCredits function accepts a _receiver parameter but uses msg.sender when resetting balances and sending funds. This inconsistent use of _receiver and msg.sender creates an authorization flaw, enabling attackers to redirect another user’s credits to themselves.

Description

  • The withdrawAllFailedCredits function accepts an _receiver parameter but incorrectly uses msg.sender for both resetting the failedTransferCredits mapping and sending funds. This allows any attacker to steal other users' failed transfer credits by calling withdrawAllFailedCredits with a victim's address as the _receiver parameter. The function will check the victim's balance, reset msg.sender's balance (which is likely 0), and then attempt to send the victim's funds 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:

  • This occurs whenever a user has accumulated ETH in failedTransferCredits due to failed refunds, settlements, or withdrawals.

  • An attacker can always exploit this by passing a victim’s address as _receiver and receiving the victim’s funds, since no authorization checks prevent it.

Impact:

  • Attackers can steal all failed transfer credits belonging to any user. This could result in complete loss of funds for users who have accumulated credits from failed ETH transfers during bid refunds, auction settlements, or fee withdrawals.

Proof of Concept

// Attack scenario:
// 1. Victim has 10 ETH in failedTransferCredits[victim]
// 2. Attacker calls:
marketplace.withdrawAllFailedCredits(victimAddress);
// 3. Function reads amount = failedTransferCredits[victim] = 10 ETH
// 4. Function sets failedTransferCredits[attacker] = 0 (no effect)
// 5. Function sends 10 ETH to attacker (msg.sender)
// 6. Victim loses all their failed transfer credits

Recommended Mitigation

To validate that only valid user can withdraw own credit we need to add one line of code to validate.

require(msg.sender == _receiver, "Can only withdraw own credits");

function withdrawAllFailedCredits(address _receiver) external {
uint256 amount = failedTransferCredits[_receiver];
require(amount > 0, "No credits to withdraw");
require(msg.sender == _receiver, "Can only withdraw own credits");
failedTransferCredits[_receiver] = 0;
(bool success, ) = payable(_receiver).call{value: amount}("");
require(success, "Withdraw failed");
}
Updates

Lead Judging Commences

cryptoghost Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeast Marketplace: Unrestricted FailedCredits Withdrawal

withdrawAllFailedCredits allows any user to withdraw another account’s failed transfer credits due to improper use of msg.sender instead of _receiver for balance reset and transfer.

Support

FAQs

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

Give us feedback!