Closed captainmangoC4 closed 11 months ago
Issue created on behalf of judge in order to split into 2 findings
alex-ppg marked the issue as duplicate of #572
The Warden's recommendation is not correct for the periodic mint model as mintAndAuction
would still be affected, however, no 75% option exists and as such I will mark this submission as of sufficient quality.
alex-ppg marked the issue as satisfactory
alex-ppg marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/MinterContract.sol#L196-L254 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L189-L200
Vulnerability details
Bug Description
_mintProcessing()
inNextGenCore.sol
calls OpenZeppelin’s_safeMint()
from their ERC721 contract to mint a token._safeMint()
checks if the target address is a contract by calling_checkOnERC721Received()
to ensure it supports receiving NFTs. If the target address is a contract and implementsonERC721Received()
as expected, it may contain malicious logic that allows for a reentrancy attack intomint()
as it does not follow the checks, effects and interactions (CEI) pattern inNextGenCore.sol
andMinterContract.sol
. The problem arises due to the transaction dependency order, NextGenCore.sol#L193-L198:_mintProcessing()
is called beforetokensMintedPerAddress
is updated. This enables the attacker to reentermint()
and mint more than the_maxCollectionPurchases
without theirtokensMintedPerAddress
being updated, allowing them to bypass this require statement MinterContract.sol#L224:To add to this, if a collection is set to a periodic sales model whereby mints are limited to 1 token during each time period (e.g. minute, hour, day), this limit can also be bypassed allowing the attacker to mint multiple tokens within a time period. Looking at the transaction order MinterContract.sol#L234-L240:
gencore.mint()
is called before the logic for a collection with a periodic sales model (if (collectionPhases[col].salesOption == 3)
) is executed. This enables the attacker to reentermint()
and mint multiple tokens without thetDiff
being calculated, allowing them to bypass this require statement MinterContract.sol#L251:Impact
The attacker can mint more than the
maxCollectionPurchases
and multiple tokens within atimePeriod
. If projects that rely on this type of logic as part of their business model (e.g. Nouns DAO) experience this exploit it will likely have a detrimental impact on the value of the existing NFTs (if any) and the reputation of the project thereafter. Given the nature of these "long-lived" mints, this could be at the project's financial detriment.Proof of Concept
Alice utilizes NextGen to create a collection. She has 24 art pieces she would like sell using a periodic sales model. She sets the parameters as such that the sale will last 24 hours, setting each
_timeperiod
to an hour. To ensure the NFTs are fairly distributed she sets the_maxCollectionPurchases
to 1. As it stands users should only be allowed to mint 1 NFT per hour and 1 NFT per address. Despite this, Bob plans to exploit the contracts by minting all 24 NFTs to his address in one transaction. Here is an example of a malicious contract that would allow him to do this:Foundry test:
Tools Used
Manual Review and Foundry.
Recommended Mitigation Steps
Update
mint()
inNextGenCore.sol
to follow the CEI pattern, NextGenCore.sol#L193-L198:Use OpenZeppelin’s
nonReentrant()
modifier from theirReentrancyGuard.sol
contract onmint()
inMinterContract.sol
, MinterContract.sol#L196:Assessed type
Reentrancy