-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
personalMint()
reentrancy attack
#8
Comments
Nice and valid issue. A note on mitigation: function _claimIssuance(address _human) internal {
(uint256 issuance, uint256 startPeriod, uint256 endPeriod) = _calculateIssuance(_human);
if (issuance == 0) {
// No issuance to claim, simply return without reverting
return;
}
+ mintTimes[_human].lastMintTime = uint96(block.timestamp);
// mint personal Circles to the human
_mintAndUpdateTotalSupply(_human, toTokenId(_human), issuance, "");
emit PersonalMint(_human, issuance, startPeriod, endPeriod);
} |
Thank you @0xmahdirostami! |
We greatly appreciate your report on the potential reentrancy attack in the personalMint() function. This is a valid and important finding. You've correctly identified that updating the mint time after minting creates a vulnerability due to the ERC1155 onReceived callback. |
Github username: --
Twitter username: --
Submission hash (on-chain): 0xec7bc17cdbb6208d02f4a0ac0ea20b9ec3edd1f02deacab6e2ed24110ac2450f
Severity: high
Description:
Impact
Attacker can claim the initial issuance as many times as he wants (only block.gaslimit limits how many times the reentrancy can be executed)
Description
The
personalMint()
function allows a registered user to mint personal Circles for themselves: after registration and v1 status check it calls the_claimIssuance()
function which soon calls_mintAndUpdateTotalSupply()
->_mint()
which mints and calls.onERC1155Received
if the user is not an EOA aka passes the greater than zeroto.code.length
check in_doSafeTransferAcceptanceCheck()
.Hub.sol
-_claimIssuance()
The user can reenter the
personalMint()
function from.onERC1155Received()
and claim & mint issuance again and again an arbitrary amount of times because norregisterHuman()
norpersonalMint()
prevents a contract calling these functionsThis wouldn't even be a huge problem if the
issuance
during the reentrancy would use the updated values for calculations in_calculateIssuance()
: howevermintTimes[_human].lastTime
is only updated after the mint as we can see in_claimIssuance()
which ultimately allows the attacker to claim their initial issuance as many times they want with continually reenteringpersonalMint()
from.onERC1155Received()
.Full Execution flow
personalMint()
->_claimIssuance()
->mintAndUpdateTotalSupply()
->_mint()
->_updateWithAcceptanceCheck()
->_acceptanceCheck()
->_doSafeTransferAcceptanceCheck()
->(to).onERC1155Received()
Proof of Concept
0xdead
through self-invitation viaregisterHuman()
that callspersonalMint()
on every.onERC1155Received()
call to the contract up to 1000x timespersonalMint()
from0xdead
contractHub
mints the issuance tokens via_mint
it calls.onERC1155Received
from_doSafeTransferAcceptanceCheck()
0xdead
contract reenterspersonalMint()
to claim the same amount of issuance tokens again and again -> repeats 1000x timesRecommendation
Consider to add a reentrancy guard to
personalMint()
. Consider to only allow EOAs to callregisterHuman()
andpersonalMint()
. Change_claimIssuance
so the last mint time is updated before the actual mint:The text was updated successfully, but these errors were encountered: