Bid Beasts

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

Potential DoS Through Malicious NFT Contract

Root + Impact

The marketplace assumes ERC-721 transferFrom calls always succeed when preconditions (ownership / approval) are satisfied. A malicious or upgradeable NFT contract can make transferFrom revert selectively (for example, only for transfers from the marketplace), causing settlement logic to fail. Because the marketplace does not handle transfer failures, an NFT can become stuck in the contract and related flows (settlement, payouts) will revert or be left incomplete.

Description

  • The marketplace assumes NFT transfers will always succeed if ownership checks pass. However, a malicious or upgradeable NFT contract could implement transfer hooks that selectively revert, potentially locking NFTs in the marketplace after listing or preventing auction settlement.


// Root cause in the codebase with @> marks to highlight the relevant section
// In listNFT:
BBERC721.transferFrom(msg.sender, address(this), tokenId);
// In _executeSale:
BBERC721.transferFrom(address(this), bid.bidder, tokenId);

Risk

Likelihood:

  • Deploy a malicious ERC-721 that allows a first transfer (to the marketplace) but reverts on subsequent transferFrom calls originating from the marketplace address.

Seller lists the malicious NFT; listing succeeds because the token contract allows transfer to marketplace.

  • Auction or buy-now completes and marketplace calls _executeSale which calls transferFrom(address(this), buyer, tokenId). The malicious NFT contract reverts in this call.

  • _executeSale reverts (or settlement is left incomplete), leaving the NFT stuck in the marketplace and payouts unresolved.


Impact:

  • NFTs could become permanently locked in the marketplace if the NFT contract's transferFrom function reverts during settlement. This would prevent the seller from receiving payment and the buyer from receiving the NFT, with no recovery mechanism available.

  • NFTs may become permanently locked inside the marketplace contract, preventing sellers from reclaiming their tokens and buyers from receiving purchased NFTs.

  • Sellers may not receive payment and buyers may not receive refunds, producing financial loss or indefinite state inconsistency.

  • The marketplace can suffer denial-of-service on affected listings and reputational damage for inability to complete transactions.


Proof of Concept

Deploy a malicious ERC-721 that allows a first transfer (to the marketplace) but reverts on subsequent transferFrom calls originating from the marketplace address.

  • Seller lists the malicious NFT; listing succeeds because the token contract allows transfer to marketplace.

  • Auction or buy-now completes and marketplace calls _executeSale which calls transferFrom(address(this), buyer, tokenId). The malicious NFT contract reverts in this call.

  • _executeSale reverts (or settlement is left incomplete), leaving the NFT stuck in the marketplace and payouts unresolved.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract MaliciousERC721 is ERC721 {
bool public allowInitialTransfer = true;
address public marketplaceToBlock;
constructor() ERC721("Malicious", "MAL") {}
function mint(address to, uint256 id) external {
_mint(to, id);
}
// Allow marketplaceToBlock to be set after listing; marketplace address known in test
function setMarketplace(address _m) external {
marketplaceToBlock = _m;
}
// Override transferFrom to permit initial transfer to marketplace, but revert for transfers from the marketplace
function transferFrom(address from, address to, uint256 tokenId) public override {
// allow normal transfers except when 'from' is the marketplace and we've decided to block it
if (from == marketplaceToBlock) {
revert("Malicious: block transfers from marketplace");
}
// Normal behavior otherwise
super.transferFrom(from, to, tokenId);
}
}

Recommended Mitigation

Implement an emergency withdrawal mechanism for stuck NFTs after a timeout period, or use try-catch blocks with fallback logic

Add try/catch around external transferFrom and handle failures deterministically.

  • Switch payouts to pull-pattern (pending withdrawals).

  • Implement emergency reclaim for seller after a defined timeout.

  • Add events (NFTTransferFailed, NFTRescued) and monitoring.

  • Optionally maintain a vetted/whitelisted token list or verification process for new token contracts

try BBERC721.transferFrom(address(this), bid.bidder, tokenId) {
// Success path
} catch {
// Refund buyer, return NFT to seller
listing.listed = true;
bids[tokenId] = Bid(address(0), 0);
_payout(bid.bidder, bid.amount);
}
Updates

Lead Judging Commences

cryptoghost Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeasts Marketplace: Risk of Locked NFTs

Non-safe transferFrom calls can send NFTs to non-compliant contracts, potentially locking them permanently.

Support

FAQs

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

Give us feedback!