BidBeastsNFTMarket::withdrawAllFailedCredits function ,making it extremely easy to grab someone else's fund which were not transfered successfully during the _payout internal function call .Description: In the BidBeastsNFTMarket::withdrawAllFailedCredits function there is no check if msg.sender is the _reciever. Hence, if transaction of some user is failed by some reason in the BidBeastsNFTMarket::_payout function ,anyone can withdraw that money by calling the withdrawAllFailedCredits function by passing the victim's address and receiving the funds on the attacker's address.
Likelihood Medium
The (bool success, ) = payable(recipient).call{...} will return false primarily if the recipient is a smart contract that:
Does not have a receive() or a payable fallback() function to accept the Ether.
Has a function to receive Ether, but that function uses more than 2300 gas (the amount forwarded by .call by default).
The receiving function intentionally reverts.
Impact: High
If some user places a bid ,but a higher bid is placed by some other user ,then at the time of reallocation of funds to the previous user , if that transaction is failed by any reason , anyone can withdraw the funds of previous user by calling the BidBeastsNFTMarket::withdrawAllFailedCredits passing the address of previous user and receiving the funds in the msg.sender address.
Proof of Concept:
Seller list it's NFT on the BidBeastsNFTMarket.
innocentNewUser bids on it which is a smart contract with no fallback or receive function with msg.value of BUY_NOW_PRICE + EXTRA_AMOUNT. Hence ,making the innocentNewUser the owner of the NFT and hence the contract will return extra Eth back .
No fallback or recieve function ,hence making the transaction to fail.
This mapping will be called failedTransferCredits[recipient] += amount;.
BIDDER_1 calls the withdrawAllFailedCredits(address(innocentNewUser)).
EXTRA_AMOUNT will move to BIDDER_1's address after giving a 5% cut.
Proof of Code
Following code will show the Attack.
Recommended Mitigation:
Inside the BidBeastsNFTMarketPlace::withdrawAllFailedCredits introduce a line of code in the starting checking if the _reciever is equal to msg.sender, hence preventing anyone from withdrawing someone else's ETH .And only the owner of that amount can get it back(incase of gas fee issues).
More robust solution can be to introduce a backup account in the Bid struct ,which means adding the parameter address backupAccount.
So if their main account i.e bidder does not have a recieve or fallback function ,they have an option of receiving the ETH on that backupAccount.
Changing the logic of the logic accordingly by updating the _executeSale and _payout functions .
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.