In contract OneShot
function mintRapper()
due to not following pattern Checks Effects Interactions lead to cross contract reentrancy.
In contract OneShot
function mintRapper()
do not following Checks Effects Interactions pattern.
Function mintRapper()
call _safeMint
which trigger onERC721Received
function call on msg.sender
if its contract.
Only after that function mintRapper()
change the state for new rapper:
1 First User call OneShot.mintRapper()
2 First User call RapBattle.goOnStageOrBattle()
3 Attacker monitoring mempool for the goOnStageOrBattle()
transaction of first user.
4 Attacker put his transaction right after first user goOnStageOrBattle()
transaction.
5 In this transaction attacker trigger his contract which is IERC721Receiver
to do following steps:
5.1. call OneShot.mintRapper()
5.2. in function onERC721Received
which trigger by the OneShot
contract call RapBattle.goOnStageOrBattle()
6 Contract RapBattle
calls _battle()
inside goOnStageOrBattle()
7 Function _battle()
trigger following calls RapBattle.getRapperSkill
-> OneShot.getRapperStats
8 At this stage contract OneShot
already minted an attacker rapper, but not properly Initialize metadata for attacker rapper.
9 Function OneShot.getRapperStats
returns wrong stats for attacker rapper
10 Function RapBattle.getRapperSkill
returns 65 point for the attacker rapper, instead of 50
11 Function _battle()
performs incorrect winner calculations:
The RapBattle._battle()
to calculate a winner use totalBattleSkill
which depends on challengerRapperSkill
:
To determinate winner RapBattle._battle()
compares random
with defenderRapperSkill
.
As we can see the random
belongs to set of integers from 0
to totalBattleSkill
.
Attacker can manipulate the totalBattleSkill
adding to his skill 15 points.
First User rapper has defenderRapperSkill
= 50
Attacker rapper has challengerRapperSkill
= 65, although the correct value without reentrancy would be 50
Without reentrancy defender wins in 50 randoms from 100 // 50% of winning
With reentrancy defender wins in 50 randoms from 115 // ~43% of winning
Attacker can increase his chances to win up to 7% without staking an Rapper NFT in Streets
.
Manual review.
Make the following changes in OneShot.mintRapper()
https://github.com/Cyfrin/2024-02-one-shot/blob/47f820dfe0ffde32f5c713bbe112ab6566435bf7/src/OneShot.sol#L29C1-L36C6
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.