The withdrawAllFailedCredits() function is designed to allow users to withdraw their own failed transfer credits that accumulated when direct ETH transfers failed during auction payouts. When the _payout() function encounters a recipient address that cannot receive ETH (such as a contract without payable functions), it credits the amount to failedTransferCredits[recipient] mapping so the user can withdraw it later. The intended behavior is that users should only be able to withdraw their own accumulated credits by calling withdrawAllFailedCredits() with their address as the _receiver parameter, which would read their balance, clear their balance, and send the ETH to their address.
The withdrawAllFailedCredits() function contains a critical parameter mismatch that enables direct fund theft. The function reads the credit balance from failedTransferCredits[_receiver] (the parameter), but incorrectly clears the balance from failedTransferCredits[msg.sender] (the caller) and sends the funds to msg.sender. This means an attacker can call withdrawAllFailedCredits(victim_address) to steal the victim's entire failed transfer credit balance while their own credit balance (which is typically 0) gets cleared instead. Since the victim's balance is never actually cleared, the same attack can be repeated infinitely until the contract's ETH is completely drained, making this a critical fund drainage vulnerability affecting any user with accumulated failed transfer credits.
Likelihood:
Failed transfer credits accumulate naturally during regular NFT marketplace operations when auction payouts to contract addresses fail. The _payout() function automatically credits failedTransferCredits[recipient] whenever payable(recipient).call{value: amount}("") returns false, which occurs commonly when sending ETH to contracts without proper receive/fallback functions, multisig wallets, or accounts with reverted transfers.
The withdrawAllFailedCredits() function has no access control restrictions and accepts any address as the _receiver parameter, making it immediately exploitable by any attacker who can identify addresses with non-zero failed transfer credits. Attackers can easily scan the blockchain for users with accumulated credits and execute the theft through simple contract interaction.
Impact:
Victims suffer 100% permanent loss of their entitled withdrawal funds with no recovery mechanism. The PoC demonstrated successful theft of 10 ETH from just 2 exploit executions, with the victim's original 5 ETH credit balance remaining unchanged, proving that attackers can drain unlimited amounts while preserving the victim's balance for infinite repeated exploitation until the contract's entire ETH balance is stolen.
The vulnerability undermines the entire auction settlement system by making failed transfer credits worthless, as any user's accumulated credits become immediately vulnerable to theft. This creates systemic risk where legitimate users lose trust in the marketplace's ability to handle failed payouts safely, potentially leading to mass abandonment of the platform and destruction of the NFT marketplace
This mitigation preserves the intended functionality for legitimate users who need to withdraw their own failed transfer credits while completely preventing attackers from accessing other users' balances.
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.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.