Bid Beasts

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

Access Control Vulnerability in Failed Transfer Credit Withdrawal Mechanism

Root + Impact

Description

Intended Functionality :

  1. The failedTransferCredits mapping stores ETH amounts that failed to transfer during payouts

  2. When a transfer fails in _payout, it stores the amount

  3. Users should be able to later withdraw their failed credits

The vulnerability stems from poor access control design where:

  1. The function accepts an arbitrary address parameter

  1. It fails to validate the relationship between the caller and the credit owner

  1. It updates the wrong storage variable during withdrawal

@> 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: Medium

  • The likelihood is considered Medium because:

    1. The vulnerability requires specific conditions to be exploitable:

    • There must be failed transfers in the system

    • The contract must have ETH balance from these failed transfers

    • The attacker needs to identify addresses with failed transfer credits

Impact: Medium

  • The vulnerability allows:

    1. Unauthorized withdrawal of other users' failed transfer credits

    1. Potential for multiple withdrawals of the same credits since wrong balance is zeroed

    1. Loss of funds for legitimate users who experienced failed transfers

Proof of Concept

This PoC demonstrates that:

  1. Failed transfers are easily triggered using contract mechanics

  1. The vulnerability is straightforward to exploit

  1. Multiple attackers can exploit the same failed transfer credits

  1. No special permissions or complex setup required

  1. The attack can be repeated until all funds are drained

// SETUP
// 1. Alice lists NFT for 10 ETH
// 2. Bob wins auction with 10 ETH bid
// 3. Transfer to Alice fails (e.g., Alice is a contract that rejects transfers)
// 4. System stores 10 ETH in failedTransferCredits[alice]
// EXPLOIT
function exploitFailedCredits() external {
// Mallory can steal Alice's failed credits
market.withdrawAllFailedCredits(alice); // Mallory gets 10 ETH
// Alice's balance is untouched, allowing repeated theft
market.withdrawAllFailedCredits(alice); // Mallory gets another 10 ETH
}

Recommended Mitigation

Implement the fixed version of the function that:

  1. Removes the _receiver parameter

  1. Only allows users to withdraw their own failed transfer credits

  1. Properly updates the credit balance of the withdrawing user

  1. Adds relevant events for better tracking and transparency

- remove this code
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");
}
+ add this code
function withdrawAllFailedCredits() external {
uint256 amount = failedTransferCredits[msg.sender]; // Only read own credits
require(amount > 0, "No credits to withdraw");
failedTransferCredits[msg.sender] = 0; // Zero own credits before transfer
(bool success, ) = payable(msg.sender).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!