BidBeastsNFTMarketPlace::withdrawAllFailedCredits function checks credits for arbitrary receiver but updates state and withdraws funds for the msg.sender, enabling a malicious user to drain all contract funds.Normal behaviour: When a payout to a bidder fails, the amount owed to them is stored in the BidBeastsNFTMarketPlace::failedTransferCredits mapping. The user can then withdraw this amount by calling the BidBeastsNFTMarketPlace::withdrawAllFailedCredits function. Only the user whose original payout failed should be the one allowed to withdraw the funds.
When a payout to a bidder fails, any user can withdraw this amount by calling the BidBeastsNFTMarketPlace::withdrawAllFailedCredits function, instead of the funds' rightful owner. A malicious user can then re-enter this function to withdraw all funds available in the BidBeastsNFTMarketPlace contract balance.
Likelihood: High
The vulnerability occurs when
A payout using the internal BidBeastsNFTMarketPlace::_payout function fails.
A malicious user takes advantage of the failed fund transfer to steal the funds owed to the original recipient of BidBeastsNFTMarketPlace::_payout
The malicious user then re-enters the BidBeastsNFTMarketPlace::withdrawAllFailedCredits function to drain all funds from the contract.
Impact: High
Loss of funds:
Funds owed to bidders can be stolen by a malicious user / contract.
Full contract balance drain:
The whole marketplace contract balance can be drained by re-entering the vulnerable function.
The following test demonstrates how a user that has set up an Attacker contract can drain the marketplace
contract funds by re-entering the withdrawAllFailedCredits function after a payout to a given user has failed.
As a PoC include the following mock contracts and test in the Foundry test suite and run with forge test --mt test_WithdrawAllFailedCredits_FullDrain -vv:
In addition, the nonReentrant modifier can be used as follows for defense-in-depth:
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.