Summary
COMMON SNEK URI is accessible with valid tokens but also with invalid nonexistent tokens and therefore missing a assert check. People may think that the token ID is valid a it holds token URI of COMMON SNEK with some interesting metadata.
Vulnerability Details
@external
@view
def tokenURI(token_id: uint256) -> String[53]:
---------------------^
rarity: uint256 = self.tokenIdToRarity[token_id]
return self.rarityToTokenURI[rarity]
impact
This situation can be highly confusing for viewers who frequently explore new NFTs for their collections. If they attempt to search for a token URI using any arbitrary non-existent token ID, they will discover that the COMMON SNEK URI is associated with someone who owns the provided token ID, potentially leading them to believe that they have won the NFT in the snek raffle. This could result in people losing interest in participating in the raffle.
PoC: common snek uri with non-existent token
def test_token_uri_with_nonexistent_token_ids(raffle_boa, vrf_coordinator_boa, entrance_fee):
boa.env.set_balance(USER, STARTING_BALANCE)
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(0, raffle_boa.address)
print("\nNFT URI with Valid token ID 0 :", raffle_boa.tokenURI(0))
print("NFT URI with Invalid non-existent token ID 1:", raffle_boa.tokenURI(1))
pytest -v tests/snek_raffle_test.py::test_token_uri_with_nonexistent_token_ids -s
==================================================================== test session starts =====================================================================
platform linux -- Python 3.10.12, pytest-8.0.2, pluggy-1.4.0 -- /home/theirrationalone/vyperenv/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/home/theirrationalone/first-flights/2024-03-snek-raffle/.hypothesis/examples'))
rootdir: /home/theirrationalone/first-flights/2024-03-snek-raffle
plugins: titanoboa-0.1.8, cov-4.1.0, hypothesis-6.98.17
collected 1 item
tests/snek_raffle_test.py::test_token_uri_with_nonexistent_token_ids
NFT URI with Valid token ID 0 : ipfs:
NFT URI with Invalid non-existent token ID 1: ipfs:
PASSED
===================================================================== 1 passed in 1.56s ======================================================================
Tools Used
Manual review, Pytest
Recommendations
An assert (revert) check can be employed to mitigate this issue.
Please update your snek_raffle.vy
as follows:
.
.
.
# Errors
ERROR_NOT_ENDED: constant(String[25]) = "SnekRaffle: Not ended"
ERROR_TRANSFER_FAILED: constant(String[100]) = "SnekRaffle: Transfer failed"
ERROR_SEND_MORE_TO_ENTER_RAFFLE: constant(String[100]) = "SnekRaffle: Send more to enter raffle"
ERROR_RAFFLE_NOT_OPEN: constant(String[100]) = "SnekRaffle: Raffle not open"
ERROR_NOT_COORDINATOR: constant(String[46]) = "SnekRaffle: OnlyCoordinatorCanFulfill"
+ ERROR_NONEXISTENT_TOKEN: constant(String[46]) = "SnekRaffle: ERROR_NONEXISTENT_TOKEN"
.
.
.
@external
@view
def tokenURI(token_id: uint256) -> String[53]:
+ assert ERC721._exists(token_id), ERROR_NONEXISTENT_TOKEN
rarity: uint256 = self.tokenIdToRarity[token_id]
return self.rarityToTokenURI[rarity]
.
.
.
Mitigation PoC:
def test_tokenURI_reverts_with_nonexistent_token_ids(raffle_boa):
with boa.reverts("SnekRaffle: ERROR_NONEXISTENT_TOKEN"):
raffle_boa.tokenURI(1)
pytest -v tests/snek_raffle_test.py::test_tokenURI_reverts_with_nonexistent_token_ids -s
==================================================================== test session starts =====================================================================
platform linux -- Python 3.10.12, pytest-8.0.2, pluggy-1.4.0 -- /home/theirrationalone/vyperenv/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/home/theirrationalone/first-flights/2024-03-snek-raffle/.hypothesis/examples'))
rootdir: /home/theirrationalone/first-flights/2024-03-snek-raffle
plugins: titanoboa-0.1.8, cov-4.1.0, hypothesis-6.98.17
collected 1 item
tests/snek_raffle_test.py::test_tokenURI_reverts_with_nonexistent_token_ids PASSED
===================================================================== 1 passed in 1.57s ======================================================================