Bid Beasts

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

No reentrancy guard and external calls

Root + Impact

Description

The marketplace performs multiple external calls to untrusted addresses, such as:

  • Sending ETH refunds via _payout.

  • Transferring NFTs to bidders via BBERC721.transferFrom.

  • Paying seller proceeds in _executeSale.

Currently, these external calls are not protected by any reentrancy guard (e.g., nonReentrant modifier). This leaves the contract vulnerable to reentrancy, where a malicious bidder or seller contract could re-enter the marketplace functions and manipulate state (e.g., repeatedly settle auctions, drain fees, or disrupt auction state).

function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
// @> External call to untrusted address with no reentrancy guard
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
}
}
function _executeSale(uint256 tokenId) internal {
Listing storage listing = listings[tokenId];
Bid memory bid = bids[tokenId];
listing.listed = false;
delete bids[tokenId];
// @> External NFT transfer (can call back into this contract)
BBERC721.transferFrom(address(this), bid.bidder, tokenId);
uint256 fee = (bid.amount * S_FEE_PERCENTAGE) / 100;
s_totalFee += fee;
uint256 sellerProceeds = bid.amount - fee;
// @> External ETH transfer to seller
_payout(listing.seller, sellerProceeds);
emit AuctionSettled(tokenId, bid.bidder, listing.seller, bid.amount);
}

Risk

Likelihood:

  • Occurs whenever _payout or _executeSale is triggered (e.g., during placeBid, settleAuction, takeHighestBid).

  • A malicious bidder or seller can deploy contracts with reentrant fallback functions.

Impact:

  • Auction state can be corrupted (e.g., double settlement).

  • Fees or ETH balances can be manipulated, leading to fund loss.

  • NFT ownership may become inconsistent with recorded listings.

Proof of Concept

contract ReentrantBidder {
BidBeastsNFTMarket public market;
uint256 public tokenId;
constructor(address _market, uint256 _tokenId) {
market = BidBeastsNFTMarket(_market);
tokenId = _tokenId;
}
// Reenters when receiving ETH
receive() external payable {
// Malicious reentry into settleAuction or takeHighestBid
market.settleAuction(tokenId);
}
function attack() external payable {
// Place bid with malicious contract as bidder
market.placeBid{value: msg.value}(tokenId);
}
}

This contract re-enters during refund or payout and attempts to settle the auction multiple times.

Recommended Mitigation

Add a reentrancy guard (e.g., OpenZeppelin’s ReentrancyGuard) to all state-changing functions that involve external calls:

- contract BidBeastsNFTMarket is Ownable(msg.sender) {
+ import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+ contract BidBeastsNFTMarket is Ownable(msg.sender), ReentrancyGuard {

And protect functions with nonReentrant:

- function placeBid(uint256 tokenId) external payable isListed(tokenId) {
+ function placeBid(uint256 tokenId) external payable isListed(tokenId) nonReentrant {
- function settleAuction(uint256 tokenId) external isListed(tokenId) {
+ function settleAuction(uint256 tokenId) external isListed(tokenId) nonReentrant {
- function takeHighestBid(uint256 tokenId) external isListed(tokenId) isSeller(tokenId, msg.sender) {
+ function takeHighestBid(uint256 tokenId) external isListed(tokenId) isSeller(tokenId, msg.sender) nonReentrant {
- function withdrawAllFailedCredits(address _receiver) external {
+ function withdrawAllFailedCredits(address _receiver) external nonReentrant {
- function withdrawFee() external onlyOwner {
+ function withdrawFee() external onlyOwner nonReentrant {

This ensures no reentrant calls can manipulate auction or payout state.

Updates

Lead Judging Commences

cryptoghost Lead Judge about 1 month 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.