Closed c4-submissions closed 11 months ago
141345 marked the issue as primary issue
a2rocket (sponsor) disputed
not clear how the randomness can be broke, the randomness is returned after a specific number of blocks and once set it cannot be set again so the other transactions will fail. Also by fixing reentrancy attacks reported already we can avoid someone calling the mint function continuously.
141345 marked the issue as sufficient quality report
alex-ppg marked the issue as duplicate of #1464
alex-ppg marked the issue as unsatisfactory: Invalid
@alex-ppg Hi Alex, thanks for your time and your professionalism in judging the contest. I want to emphasize for a malicious user can force the team to spend all their availables LINK tokens to request useless random transactions.
Like I say, a malicious user can force the minting process to revert with a contract by not implementing the onERC721Received
method on it, and because the call to requestRandomWords()
to the VRF Coordinator occurs before the revert, a request is sended and the team have to pay with LINK amounts.
The minting process is open for any user, and the attacker can send many minting attempts to force the team to spend all their LINK tokens.
And because the transactions take less than 5 minutes to be treated by the Coordinator, the team have low amount of time to react to this potential attack.
In my humble opinion, this is an easy attack to carry out, costing the attacker only the price of gas.
It can be remediate by calling the _safeMint()
before the request to the VRF Coordinator.
Thanks for your time !
Hey @AxelAramburu, thanks for contributing! If a transaction issues a revert
, all state changes that are part of the transaction are rolled back. This includes event emissions as well as any function calls that perform state changes in external contracts. As such, the requestRandomWords
would not have been relayed if the transaction reverted on the onERC721Received
hook.
Lines of code
https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/NextGenCore.sol#L227-L232 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerVRF.sol#L71-L75 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerVRF.sol#L52-L63 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/ERC721.sol#L245-L251 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/RandomizerVRF.sol#L65-L68
Vulnerability details
Impact
Because the user can send a lot of request to the
VRF Coordinator
of Chainlink to generate randomness, anyone can consume all the LINK tokens allowed by the team to pay the requests and also pre-generate the hash token of a token before it's minted. This break the randomness generation of the tokens/NFT guarantee by the protocol.Proof of Concept
On the
NextGenCore
contract, the_mintProcessing()
internal functions is called each time a token is minted by the protocol withmint()
,burnToMint()
andairDropTokens()
.The protocol can use 3 differents types of contracts to generate randomness, here we use
RandomizerVRF
contract which use Chainlink. We can see the function callcalculateTokenHash()
on the randomizer contract to generate the token hash. TheRandomizerVRF
function work like this:Like it's well explained in the Chainlink documentations:
https://docs.chain.link/vrf/v2/subscription#how-vrf-processes-your-request
Chainlink VRFCoordinatorV2 is called with
requestRandomWords()
and emits a random request. AfterrequestConfirmations
blocks, an oracle VRF node replies to the coordinator with a random word, which supplies the random to the requesting contract viafulfillRandomWords()
callback. Interesting things is when the callCOORDINATOR.requestRandomWords()
is executed, an event is emitted and the request is processing. But if the minting process revert after the call to theVRF Coordinator
, the request is sended but all the state revert in the contract. A user can maliciously revert the transaction by trying to send the token to a contract that doesn't implement theonERC721Received()
method and revert the_safeMint
method. Therefore a user can send a big amount of transactions to theVRF Coordinator
for the sametokenId
without the token being minted. It causes multiples problems.A user can send a big number of transactions to consume all the LINK balance of the team account in a very little amount of time because a request can be executed in 3 blocks, (
uint16 public requestConfirmations = 3
), less than 5 minutes.NextGenCore
contract by thefulfillRandomWords()
callback. Now user can calculate the output of the next token with the artist script and broke the randomness of the token generation.Notes
I think this issue can be considered as High because the randomness of the token generation can be broken by anyone in simple way. And a malicious user can force the team to spend all their availables LINK tokens to request useless random transactions.
Tools Used
Manual Review, Chainlink Docs
Recommended Mitigation Steps
Like it's explained in the Security Considerations of the Chainlink VRF docs, you have to respect the procces explained: https://docs.chain.link/vrf/v2/security#dont-accept-bidsbetsinputs-after-you-have-made-a-randomness-request
You can decide to call the
_safeMint
function on thefulfillRandomWords()
callback to asure the token is not minted with zero token hash. Also create for example atokenToRequest[status]
that will be set to false or true to check the status of the requests and make the correct verification that a token can be minted. And add an Admin function to potentially unblock the situation if thefulfillRandomWords()
can't be executed caused by lack of funds.It's an example and implement a solution without introduce new bugs needs to be discussed. If I can help, I'll be glad to do so.
Assessed type
Oracle