Bid Beasts

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

Theft of failedTransferCredits via withdrawAllFailedCredits

Root + Impact

Description

  • Normal behavior: Withdrawals of previously failed transfers should only allow the rightful beneficiary to withdraw their own credited balance.

Issue: withdrawAllFailedCredits(address _receiver) reads credits for _receiver but transfers to msg.sender and zeros failedTransferCredits[msg.sender] — enabling theft and replay withdrawa

// Root cause in the codebase with @> marks to highlight the relevant section
function withdrawAllFailedCredits(address _receiver) external {
uint256 amount = failedTransferCredits[_receiver];
require(amount > 0, "No credits to withdraw");
// @> Incorrectly zeros caller instead of _receiver
failedTransferCredits[msg.sender] = 0;
// @> Sends funds to msg.sender even when _receiver's credits were queried
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Withdraw failed");
}

Risk

Likelihood:

  • Any caller can invoke this function with _receiver set to a victim address holding failedTransferCredits — occurs whenever failedTransferCredits[victim] > 0.

  • Repeated calls will succeed until contract funds depleted because victim's stored credit remains unchanged.

Impact:

  • Immediate theft of credited ETH (transfer to attacker).

  • Persistent drain (attacker can repeatedly call and receive same funds if credits not cleared correctly),ncept

// Attacker calls with victim address:
market.withdrawAllFailedCredits(victim);
// Attacker receives victim's credited ETH and victim's failedTransferCredits[victim] remains unchanged.

Recommended Mitigation

@@
- 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");
- }
+ // Allow caller to withdraw their own failed credits only, CEI pattern
+ function withdrawAllFailedCredits() external {
+ uint256 amount = failedTransferCredits[msg.sender];
+ require(amount > 0, "No credits to withdraw");
+ // Effects
+ failedTransferCredits[msg.sender] = 0;
+ // Interaction
+ (bool success, ) = payable(msg.sender).call{value: amount}("");
+ require(success, "Withdraw failed");
+ }
Updates

Lead Judging Commences

cryptoghost Lead Judge about 1 month 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.