Lucene search

K
code423n4Code4renaCODE423N4:2022-11-CANTO-FINDINGS-ISSUES-38
HistoryNov 25, 2022 - 12:00 a.m.

ETH can get stuck (and possibly be stolen as well).

2022-11-2500:00:00
Code4rena
github.com
4
turnstile contract
distributefees function
token ids
ownership validation
vulnerability
eth
stolen .

Lines of code

Vulnerability details

Impact

The Turnstile contract contains a distributeFees function which can only be called by owner to assign and distribute fee for a _tokenId.

However the function does not validates the _tokenId input value. So it is possible for the owner to provide any uint256 value as an input to the function and assign the sent ETH as the claimable fee for that particular _tokenId.

The _tokenIdTracker is a counter which is initialized as 0 and is incremented by 1 on every successful token mint.

If an invalid/non-existent _tokenId is provided to the distributeFees function, the associated sent ETH will get stuck in the Turnstile contract. The only way to recover those ETH is to mint new tokens till the _tokenIdTracker equals _tokenId and then call the withdraw function. Please also keep in mind that the Turnstile contract only allows minting of one NFT per account, hence the recovery task becomes more difficult.

Proof of Concept

Case 1

  • The Turnstile contract gets deployed.
  • 10 accounts call the register function and mints 10 Turnstile NFTs. The _tokenIdTracker equals 9.
  • The deployer of the contract calls the distributeFees function with 10 ETH and 100 as the _tokenId input value. The function call gets executed successfully. Those 10 ETH now gets stuck as the NFT with token id 100 does not exist.
  • To recover those ETH the deployer mints 91 more NFTs (each with different account) so that he now owns the NFT with token id 100.
  • The owner of token id 100 calls the withdraw function and recovers the 10 ETH.

Case 2

  • The Turnstile contract gets deployed.
  • 10 accounts call the register function and mints 10 Turnstile NFTs. The _tokenIdTracker equals 9.
  • The deployer of the contract calls the distributeFees function with 10 ETH and 100 as the _tokenId input value. The function call gets executed successfully. Those 10 ETH now gets stuck as the NFT with token id 100 does not exist.
  • To steal those ETH an attacker mints 91 more NFTs (each with different account) so that he now owns the NFT with token id 100.
  • The owner of token id 100 (Attacker) calls the withdraw function and steals the 10 ETH.

In Case 1 the deployer was able to recover the funds but he has to waste gas to mint 91 NFTs. This also results is 91 redundant NFTs. In Case 2 an attacker was successfully able to steal those 10 ETH as the register function can be invoked by anyone.

Tools Used

Manual review

Recommended Mitigation Steps

In distributeFees function a check must be introduced to validate that fee can only be distributed to already existing token ids.

        if (!_exists(_tokenId)) revert InvalidTokenId();  

The text was updated successfully, but these errors were encountered:

All reactions