Platform: Code4rena
Start Date: 18/04/2024
Pot Size: $36,500 USDC
Total HM: 19
Participants: 183
Period: 7 days
Judge: Koolex
Id: 367
League: ETH
Rank: 52/183
Findings: 1
Award: $238.03
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Limbooo
Also found by: 0xabhay, 0xleadwizard, AM, ArmedGoose, Evo, HChang26, Infect3d, Jorgect, MiniGlome, SpicyMeatball, TheSchnilch, ahmedaghadi, favelanky, pontifex
238.0297 USDC - $238.03
The VaultManagerV2 prevents users from depositing and withdrawing funds in the same block with the same NFT id. The problem lies in the fact that it is not necessary to be the owner of the NFT to call the deposit()
function. So, if you remove your assets from a vault a malicious actor can call the deposit()
function in the same block as you called the withdraw()
function. The malicious actor can therefore prevent anyone from withdrawing assets by front-running every withdraw()
call.
The idToBlockOfLastDeposit
mapping is updated in the deposit()
function and withdraws occurring in the same block (same block.number
) will revert. The attacker can frontrun every withdraw()
call with a deposit()
call with the same NFT id. Furthermore, the attacker does not need to actually deposit funds during the deposit()
call because he can use his own contract implementing the Vault
interface or use a 0
amount.
Here is the affected code:
File: VaultManagerV2.sol L119: function deposit( uint id, address vault, uint amount ) external isValidDNft(id) // @audit Anyone can deposit with a valid NFT id { idToBlockOfLastDeposit[id] = block.number; Vault _vault = Vault(vault); // @audit The vault can be any arbitrary address _vault.asset().safeTransferFrom(msg.sender, address(vault), amount); _vault.deposit(id, amount); } /// @inheritdoc IVaultManager L134: function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock(); uint dyadMinted = dyad.mintedDyad(address(this), id); Vault _vault = Vault(vault); uint value = amount * _vault.assetPrice() * 1e18 / 10**_vault.oracle().decimals() / 10**_vault.asset().decimals(); if (getNonKeroseneValue(id) - value < dyadMinted) revert NotEnoughExoCollat(); _vault.withdraw(id, to, amount); if (collatRatio(id) < MIN_COLLATERIZATION_RATIO) revert CrTooLow(); }
The withdraw mechanism can be subject to DOS by a malicious actor, preventing any user from withdrawing assets from the protocol's vaults.
Manual review
Either remove the interdiction to withdraw in the same bock, or only allow NFT owner to deposit in the Vault registered with this NFT (i.e. use the isDNftOwner(id)
modifier for the deposit()
function)
DoS
#0 - c4-pre-sort
2024-04-27T11:35:32Z
JustDravee marked the issue as duplicate of #1103
#1 - c4-pre-sort
2024-04-27T11:51:44Z
JustDravee marked the issue as duplicate of #489
#2 - c4-pre-sort
2024-04-29T09:29:29Z
JustDravee marked the issue as sufficient quality report
#3 - c4-judge
2024-05-05T20:31:19Z
koolexcrypto marked the issue as not a duplicate
#4 - c4-judge
2024-05-05T20:31:27Z
koolexcrypto marked the issue as duplicate of #1266
#5 - c4-judge
2024-05-08T15:37:48Z
koolexcrypto changed the severity to 3 (High Risk)
#6 - c4-judge
2024-05-11T12:18:58Z
koolexcrypto marked the issue as satisfactory
#7 - c4-judge
2024-05-28T19:12:57Z
koolexcrypto marked the issue as duplicate of #930