Eggstravaganza

First Flight #37
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Invalid

EggstravaganzaNFT owner can mint as much as wants

Summary

The owner of the contract EggstravaganzaNFT has the ability to mint Eggs without entering a game. Even though the owner wouldn't be a participant or winner of the game, if the eggs are tradable on an NFT platform for real money, the owner could abuse the protocol.

Vulnerability Details

Using function EggstravaganzaNFT::setGameContract owner of the contract EggstravaganzaNFT can manipulate state variable with function EggstravaganzaNFT::gameContract in a way to put own address. Then owner can call EggstravaganzaNFT::mintEgg and mint Eggs.

Poc

  1. Owner calls EggstravaganzaNFT::setGameContract with own address

  2. Owner can mint NFT Egg using EggstravaganzaNFT::mintEgg

Proof of code

Place following into the EggHuntGameTest.t.sol

function testNFTOwnerCanMintMaliciously() public {
//this contract is owner so no need to use vm.prank
nft.setGameContract(owner);
nft.mintEgg(owner, 10);
assertEq(nft.ownerOf(10), owner);
}

Impact

Owner will get NFT Eggs without playing the game, and if NFT Eggs have real value players would be in unfair situation.

Tools Used

  • Vs Code: Cloned the repository locally and identified the vulnerability through manual review.

Recommendations

A simple solution is to introduce a boolean variable EggstravaganzaNFT::gameContractSet that is responsible for preventing changes to the EggstravaganzaNFT::gameContract variable after its first initialization. This way, the EggstravaganzaNFT contract remains tied to the game, and its owner cannot manipulate the minting of Eggs. Below are the necessary changes to the EggstravaganzaNFT contract.

contract EggstravaganzaNFT is ERC721, Ownable {
/// @notice The only allowed contract to mint eggs (e.g. the EggHuntGame)
address public gameContract;
+ bool public gameContractSet = false;
/// @notice Tracks the total number of minted eggs.
uint256 public totalSupply;
/// @notice Constructor initializes the ERC721 token with a name and symbol.
constructor(string memory _name, string memory _symbol)
ERC721(_name, _symbol) Ownable(msg.sender)
{}
/// @notice Only the owner can set the game contract allowed to mint eggs.
function setGameContract(address _gameContract) external onlyOwner {
+ require(gameContractSet != true, "Game contract already set")
require(_gameContract != address(0), "Invalid game contract address");
gameContract = _gameContract;
+ gameContractSet = true;
}
/// @notice Public function to mint a new Eggstravaganza NFT.
/// Only the approved game contract can mint eggs.
function mintEgg(address to, uint256 tokenId) external returns (bool) {
require(msg.sender == gameContract, "Unauthorized minter");
_mint(to, tokenId);
totalSupply += 1;
return true;
}
}
Updates

Lead Judging Commences

m3dython Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Trusted Owner

Owner is trusted and is not expected to interact in ways that would compromise security

Support

FAQs

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