Open c4-bot-10 opened 9 months ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as primary issue
Seems unlikely because numRoundsClaimed[msg.sender] has been incremented when reentering. lowerBound and currentRound are going to be incremented too. But the POC seems successful.
brandinho (sponsor) confirmed
HickupHH3 marked the issue as selected for report
HickupHH3 marked the issue as satisfactory
Mitigated here
Lines of code
https://github.com/code-423n4/2024-02-ai-arena/blob/main/src/MergingPool.sol#L154
Vulnerability details
Impact
When a fighting round ends, winners for the current round get picked and allocated respective rewards. These rewards are fighter NFTs that can be claimed by such winners. When you claim your rewards for a round or several rounds, the
numRoundsClaimed
state variable which stores the number of rounds you've claimed for gets updated to reflect your claim and each winner can only ever claim up to the amounts they win for each given round. That means if you try to batch-claim for two given rounds for which you won 2 fighter NFTs, your NFT count after the claim should be whatever your current balance of NFT is plus 2 fighter NFTs.The issue here is that there's a way to mint additional fighter NFTs on top of the fighter NFTs you're owed for winning even though the
claimRewards
function has implemented a decent system to prevent over-claims. For one, it's relatively complex to spoof a call pretending to be the_mergingPoolAddress
to mint but a malicious user doesn't need to worry too much about that to mint more fighters; they just need to leverage using a smart contract for engineering a simple reentrancy.Proof of Concept
Consider this call path that allows a malicious user to reach this undesired state:
claimRewards
supplying the args(string[] calldata modelURIs, string[] calldata modelTypes, uint256[2][] calldata customAttributes)
The root cause of this issue stems from the
roundId
. The amount of times you can reenter theclaimRewards
function depends on theroundId
. So let's say theroundId
is 3, it mints 6 NFTs:Here's a POC to show one such attack path in the code Place the code in the
MergingPool.t.sol
test contract and do the setup:testReenterPOC
is the attack POC testAttack contract:
Malicious user leveraging reentrancy test result:
Non-malicious users test POC:
Non-malicious users doing a normal claim result:
Tools Used
Manual review
Recommended Mitigation Steps
Use a
nonReentrant
modifier for theclaimRewards
function.Assessed type
Reentrancy