Bid Beasts

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

Unrestricted Burn Function Allows Arbitrary NFT Destruction

Description

In a standard ERC721 implementation, the burn() function should only allow the token owner or an approved operator to destroy the token. This ensures that users cannot maliciously burn assets they do not own.

Issue:
In the BidBeasts contract, the burn() function is publicly callable without any ownership or approval checks. As a result, any address can destroy any NFT, even if it does not belong to them. This leads to loss of assets for legitimate owners.

https://github.com/CodeHawks-Contests/2025-09-bid-beasts/blob/449341c55a57d3f078d1250051a7b34625d3aa04/src/BidBeasts_NFT_ERC721.sol#L23-L26

function burn(uint256 _tokenId) public {
_burn(_tokenId);
emit BidBeastsBurn(msg.sender, _tokenId);
}

Risk

Likelihood:

  • The burn() function is marked public, so every user on-chain can call it.

  • No condition (require) prevents attackers from targeting arbitrary tokenIds.

Impact:

  • Any NFT in circulation can be permanently destroyed by anyone, causing direct financial loss.

  • The marketplace contract (BidBeastsNFTMarket) relying on these NFTs may also malfunction if an auctioned NFT is burned mid-auction.

Proof of Concept

A Forge test demonstrating an attacker burning another user's token:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "forge-std/Test.sol";
import "../src/BidBeasts_NFT_ERC721.sol";
contract UnrestrictedBurnPoC is Test {
BidBeasts nft;
address public constant OWNER = address(0x1);
address public constant ALICE = address(0x2); // rightful owner of NFT
address public constant ATTACKER = address(0x3); // will call burn()
uint256 public constant TOKEN_ID = 0;
function setUp() public {
vm.prank(OWNER);
nft = new BidBeasts();
vm.prank(OWNER);
nft.mint(ALICE);
assertEq(nft.ownerOf(TOKEN_ID), ALICE);
assertEq(nft.balanceOf(ALICE), 1);
}
function test_attackerCanBurnOthersToken() public {
// ATTACKER (not owner or approved) calls burn on ALICE's token
vm.prank(ATTACKER);
nft.burn(TOKEN_ID);
// ownerOf should now revert because the token no longer exists
vm.expectRevert();
nft.ownerOf(TOKEN_ID);
// ALICE's balance should be zero
assertEq(nft.balanceOf(ALICE), 0);
}
}

Recommended Mitigation

Implement the onlyOwner modifier to the burn() function as well.
Replace the function with:

- function burn(uint256 _tokenId) public {
- _burn(_tokenId);
- emit BidBeastsBurn(msg.sender, _tokenId);
- }
+ function burn(uint256 _tokenId) public onlyOwner {
+ _burn(_tokenId);
+ emit BidBeastsBurn(msg.sender, _tokenId);
+ }
Updates

Lead Judging Commences

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