RapBattle::goOnStageOrBattle
allows users to put their rapper on stage as defender and as challenger if defender is already on stage.
For defender, Rapper NFT is transferred to RapBattle
contract thus assuring that the caller i.e., msg.sender
is the owner of that NFT, but for the challenger neither Rapper NFT is transferred nor ownership info is validated (checking is the caller really the owner of that rapper NFT token id), allowing anyone to go on stage with anyone's rapper NFT.
Thus, getting the benefits of CredToken by sending anyone's rapper on stage and getting benefits from other's Rapper.
Along with that, a non existing rapper tokenId can be used for battle by the challenger and the stats returned by OneShot::getRapperStats
will have no relevance.
The vulnerability is present at line 50 inside RapBattle
contract where it lags the ownership check for the rapper NFT, the challenger will bring on the stage, without any ownership check it allows one to send anyone's rapper on the stage and take advantage from other's rapper of CredToken.
For participating in RapBattle it is a crucial step to mint own Rapper NFT train it on the Streets and earn CredToken on the basis of which the rapper get's more skilled and attain points, this being the intended design flow of the protocol.
But due to missing ownership check, one can take the advantage of other's rapper NFT in which other's put really hard efforts to train them on the streets practicing with all dedication, hard work, perseverance, patience, consistency. But others can take advantage of other's really hard worked rapper NFT and can send other's rapper on the stage claiming all the CredTokens.
Along, with that one can even pass tokenId which doesn't even exist and the rapper stats returned will be:
as the value for struct returned will be:
As a result of which the rapper skill points returned by RapBattle::getRapperSkill
will be 65 (even though a beginner rapper has 50 score but non existent rapper has 65), thus giving people unfair advantage for rap battle as it is used directly inside RapBattle::_battle
function.
One can participate in RapBattle without having their own Rapper NFT, they can send anyone's rapper NFT on stage and due to weak RNG being used they can always win.
One can even send non-existent token id for rap battle as OneShot::getRapperStats
returns values for rapper stats, and the user will get unfair advantage in rap battle for the rapper skill points as points returned for non-existing rapper token id is 65
.
A user can send anyone's token id on the stage for rap battle, over here we will also take advantage of the weak RNG to always win the rap battle.
Add the following Attack
contract in the file test/OneShotTest.t.sol
.
Along with exploiting the vulnerability related to no ownership check, the following Attack
contract exploits the weak RNG to go for battle only for a favorable outcome and win everytime.
Add the test in the file: test/OneShotTest.t.sol
Run the test:
Participants in the protcol:
user
: person holding rapper NFT who goes to stage first.
challenger
: another person holding rapper NFT.
fakeRapper
: The person who doesn't actually hold rapper NFT but uses challenger
NFT to go on stage and win CredToken
Manual Review, Unit Test in Foundry
Add an ownership check before allowing challenger to battle with rapper NFT token id.
The above recommendation fixes both the impacts discussed due to no ownership checks, as once ownership check is enabled the non-existent token id will not be allowed as msg.sender != address(0)
but still as a part of good practice make the OneShot::getRapperStats
to revert for non-existent token id.
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.