Open code423n4 opened 1 year ago
dmvt marked the issue as primary issue
RedVeil marked the issue as sponsor acknowledged
RedVeil marked the issue as disagree with severity
dmvt changed the severity to 2 (Med Risk)
dmvt marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-01-popcorn/blob/d95fc31449c260901811196d617366d6352258cd/src/vault/Vault.sol#L151-L153 https://github.com/code-423n4/2023-01-popcorn/blob/d95fc31449c260901811196d617366d6352258cd/src/vault/Vault.sol#L480-L491 https://github.com/code-423n4/2023-01-popcorn/blob/d95fc31449c260901811196d617366d6352258cd/src/vault/Vault.sol#L594-L613
Vulnerability details
Impact
Vaults using hookable tokens present a reentrancy upon changing adapter. Each vault has the
takeFees()
modifier that processes performance and management fees. That modifier is invoked only in two opportunities:takeManagementAndPerformanceFees()
(external non access controlled) and when changing an adapter withchangeAdapter()
(external, non access controlled and depends on having a previously proposed adapter).The
takeFees()
modifier consumes from thetotalAssets()
balance to calculate the amount of management fees viaaccruedManagementFee()
:However, a vault using hookable tokens can perform a reentrancy if the following conditions are met:
changeAdapter()
call will go through)This reentrancy will be triggered by a hook called before the
safeTransferFrom()
upondeposit()
and the instructions performed inside that hook will occur after_mint()
and before the assets are effectively transferred to the vault:The hookable token can call
changeAdapter()
in a before transfer hook and the contract will use outdated balance values. This is because the vault increases the amount of shares before capturing the assets.Proof of Concept
A vault with a hookable token is deployed by Alice. The used token allows users to create custom hooks that run before and/or after each call. A change of adapter is enqueued.
proposeAdapter()
changeAdapter()
vault.deposit()
when the quit period passed (so thechangeAdapter()
call does not revert)takeFees()
modifier via thechangeAdapter()
call and proceeds to mint management fees to thefeeRecipient
.Output:
With the deposit and current amount of shares used for the PoC, it can be seen how the reentrant call yields in a total of
7.119e16
minted shares whereas a normal non-reentrant flow mints1.06e17
. This means that a33%
of the fees are not transferred to the recipient.The output was built by adding console logs inside each relevant Vault's function. To reproduce both non reentrant and reentrant scenarios comment the token hook and the respective parts of the PoC.
Tools Used
Manual Review
Recommended Mitigation Steps
Consider adding the
nonReentrant
modifier to thechangeAdapter()
function.