Anyone can call RapBattle::goOnStageOrBattle()
, as a challenger, passing a tokenId
which isn't already been minted.
The vulnerability is made possible because only the defender is required to transfer his NFT (so it must exist in this case) and his creds to the contract.
Also, because of OneShot::getRapperStats()
suffering from the same problem, any inexistent NFT will always have a pretty high skill score of 65.
Note: for this to work the attacker don't even need to have cred tokens and, if he loses, the transaction will simply revert because he doesn't have the tokens to send to the winning defender.
Anyone can infinitely challenge the defenders with an inexistent NFT until they win, causing them to claim free Cred
tokens.
Manual Review
In the following test we demonstrate that an attacker is able to use an inexistent NFT with a skill score of 65 to enter the battle.
And thus, if he knows the defender has a lower skill level, he's more probable to win.
Force the challenger to transfer his NFT so you can be sure that:
the NFT actually exists
the challenger actually owns the NFT
Alternatively, if you don't want to waste gas for the two additional transfers required for this first fix, you can just add the following checks inside goOnStageOrBattle
:
Note: if the _tokenId
do not exists ERC721::ownerOf()
will revert with ERC721NonexistentToken
Note: we can also assert before entering the battle that the challenger has enough cred tokens to pay if he loses. It isn't mandatory since the transaction will revert later even without it but, by checking before entering _battle()
, we can save some gas.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.