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

H-3: Missing Challenger's NFT Ownership Verification in _battle Function, allowing attackers to participate as a challenger in battles with NFTs they don't own

Summary

Description: The _battle function lacks validation of NFT ownership, allowing attackers to participate in battles with NFTs they don't own.

function _battle(uint256 _tokenId, uint256 _credBet) internal {
address _defender = defender;
require(defenderBet == _credBet, "RapBattle: Bet amounts do not match");
uint256 defenderRapperSkill = getRapperSkill(defenderTokenId);
uint256 challengerRapperSkill = getRapperSkill(_tokenId);
uint256 totalBattleSkill = defenderRapperSkill + challengerRapperSkill;
uint256 totalPrize = defenderBet + _credBet;
uint256 random =
uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % totalBattleSkill;
// Reset the defender
defender = address(0);
emit Battle(msg.sender, _tokenId, random < defenderRapperSkill ? _defender : msg.sender);
// If random <= defenderRapperSkill -> defenderRapperSkill wins, otherwise they lose
if (random <= defenderRapperSkill) {
// We give them the money the defender deposited, and the challenger's bet
credToken.transfer(_defender, defenderBet);
credToken.transferFrom(msg.sender, _defender, _credBet);
} else {
// Otherwise, since the challenger never sent us the money, we just give the money in the contract
credToken.transfer(msg.sender, _credBet);
}
totalPrize = 0;
// Return the defender's NFT
oneShotNft.transferFrom(address(this), _defender, defenderTokenId);
}

Vulnerability Details

Missing Checks: The _battle function doesn't include require statements or other mechanisms to verify that the challenger address actually own the _tokenId they are using in the battle.

Exploitation: An attacker could utilize any arbitrary valid NFT token IDs of anyone (even that of defender's) who owns a Rapper NFT, within the _battle function to participate in Rap Battle and earn cred token.

#POC

The following POC shows how an attacker can participate in rap battle without owning the Rapper NFT and earn cred token. In below example, attacker is using defender's tokenID to participate in the battle and can potentially win the rap battle.

function testUserCanRapBattleWithoutNFT() public twoSkilledRappers{
address attacker = makeAddr("Attacker");
//defender locking his NFT for Rap Battle
vm.startPrank(user);
oneShot.approve(address(rapBattle), 0);
cred.approve(address(rapBattle), 3);
rapBattle.goOnStageOrBattle(0, 3);
vm.stopPrank();
//Attacker using defender's tokenID to participate in the Rap Battle
vm.startPrank(attacker);
rapBattle.goOnStageOrBattle(0, 3);
vm.stopPrank();
console.log(cred.balanceOf(attacker));
console.log(cred.balanceOf(user));
}

Impact

Compromised Fairness: The battle system's integrity is undermined as attackers can use NFTs they don't own and earn unlimited cred bets.

Tools Used

Manual Review

Recommendations

function _battle(uint256 _tokenId, uint256 _credBet) internal {
+ require(msg.sender == oneShotNft.ownerOf(_tokenId));
address _defender = defender;
require(defenderBet == _credBet, "RapBattle: Bet amounts do not match");
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.