.call in _payout allows gas-intensive receive functions, inflating bidding costs and deterring legitimate biddersWhenever someone places a new bid using BidBeastsNFTMarketPlace::placeBid, the contract attempts to pay out the previous highest bidder through BidBeastsNFTMarketPlace::_payout function:
This _payout function implements a "partial pull-based mechanism" for the refunds. The reason I say "partial" is that, at first, it tries to send the Ether directly to the previous bidder using a low-level call. If that fails, then it credits the amount to failedTransferCredits, allowing the user to withdraw it later.
But that's where an issue is hiding in plain sight. Picture this:
Alice wants an NFT so bad that she doesn't mind doing some malicious activities to get it.
For this, she creates a contract to bid for the NFT, but in a way that if someone tries to send her Ether, it will consume a lot of gas. Something like this:
After placing the bid, Alice's contract becomes the highest bidder so far. But then Bob appears, in the hope of winning the auction, and places a higher bid.
However, when Bob's bid is in process, the contract tries to pay out Alice (the previous highest bidder) by calling her contract's receive function.
Since Alice's receive function is designed to consume a lot of gas; it will be really expensive for Bob to place his bid, and it will probably make him think twice before making the decision.
If Alice keeps doing this, it can lead to a situation where no one else can afford to outbid her, effectively locking the auction in her favour.
This is a classic example of an economic denial of service (EDoS) attack, where the attacker exploits the gas consumption to make it economically unfeasible for others to participate in the auction.
Likelihood: High
This auction doesn't restrict contracts from bidding, making it easy for attackers to exploit this vulnerability.
Impact: High
Auction Manipulation: Attackers can deter legitimate bidders, dominating auctions.
Financial Loss:
For Bidders: Legitimate bidders may lose out on winning auctions due to high gas costs, and those who attempt to bid will pay exorbitant gas fees.
For Sellers: Sellers may receive lower final prices for their NFTs as fewer bidders participate.
Add this MaliciousBidder contract in the test file:
After that, add this particular test_dosAttackViaMaliciousContract test in the test file:
Run the above test using the command:
The output we get:
There are two approaches the protocol can take to mitigate this issue; one is comparatively easier to implement, with not many changes, whereas the other is a more robust solution. Moreover, the way protocol wants their auctions to be run (the UX) will also influence the choice of mitigation:
Implement Gas Limits on External Calls (less preferred, and thus easier): Introduce a gas limit to prevent high gas consumption during payouts. For example, a gas limit of 30000 works fine, and fails the above test we just implemented in the Proof of Concept section. But do keep in mind, the attackers can still consume up to the limit.
Adopt a complete Pull-based Refund Mechanism: Instead of immediately attempting to return the bid amount to the previous bidder, the contract should credit the amount to a mapping—similar to how failedTransferCredits works—allowing bidders to withdraw their funds themselves. This aligns with best practices (e.g., OpenSea) and prevents EDoS attacks
BidBeast Marketplace has a Medium-severity reentrancy vulnerability in its "buy-now" feature that allows an attacker to disrupt the platform by blocking sales or inflating gas fees for legitimate users.
BidBeast Marketplace has a Medium-severity reentrancy vulnerability in its "buy-now" feature that allows an attacker to disrupt the platform by blocking sales or inflating gas fees for legitimate users.
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.