Closed c4-submissions closed 1 year ago
141345 marked the issue as duplicate of #2039
141345 marked the issue as duplicate of #51
141345 marked the issue as duplicate of #1742
alex-ppg marked the issue as satisfactory
alex-ppg marked the issue as partial-50
alex-ppg marked the issue as satisfactory
alex-ppg changed the severity to 3 (High Risk)
Lines of code
github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/NextGenCore.sol#L189-L200
Vulnerability details
Description
MinterContract.mint
callsNextGenCore.mint
, which variables that accounts the amount of tokens each user minted is changed only after_mintProcessing
, that has a callback in_safeMint
. Because of that, attackers can reenterMinterContract.mint
beforetokensMintedAllowlistAddress
andtokensMintedPerAddress
are incremented, allowing them to bypass the max allowance check that limits the amount of tokens an user can mint for public or allowlist phase of a collection.Proof of Concept
Let's supose the following values:
tokensMintedPerAddress[_collectionID][attacker] = 2
(retrieved viaretrieveTokensMintedPublicPerAddress
)collectionAdditionalData[_collectionID].maxCollectionPurchases = 3
(retrieved viaviewMaxAllowance
)Adversary calls
MinterContract.mint
to mint one token. Logic that executes for public phase (and no delegation, sales mode not 3):tokensMintedPerAddress[_collectionID][attacker]
plus amount of tokens to mint andcollectionAdditionalData[_collectionID].maxCollectionPurchases
.3 <= 3
, which is true, so no reverts.gencore.mint
is called:The function has the subcall
_mintProcessing
, which has_safeMint
, a function withERC721.onERC721Received
callback:In the middle of
ERC721.onERC721Received
callback,MinterContract.mint
is called again:Let's see how the reenter will interact with key lines of the
MinterContract.mint
:tokensMintedAllowlistAddress[_collectionID][attacker]
isn't updated during the reentrancy, making the check3 <= 3
again:mintIndex
, because it relies oncollectionCirculationSupply
, which is updated before the callback.Only after the tokens are minted, the following lines of code finally are finally reach:
So, considering the attacker reentered only one time, the following unexpected outcome happened:
tokensMintedPerAddress[_collectionID][attacker] = 4
collectionAdditionalData[_collectionID].maxCollectionPurchases = 3
Attacker successfully minted more tokens than he could. The same exploit could be executed for allowlist, sincetokensMintedAllowlistAddress
is also updated only after the callback.Impact
Attacker can bypass the maximum purchases for allowlist or public phases, effectively being able to use
mint
more times than a normal user, which is specially dangerous for the phase 1.Tools Used
Manual Review
Recommended Mitigation Steps
Use
nonReentrant
modifier fromReentrancyGuard.sol
and follow the Checks-Effects-Interactions pattern.Assessed type
Reentrancy