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: 115/183
Findings: 2
Award: $7.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MrPotatoMagic
Also found by: 0x175, 0x486776, 0x77, 0xAkira, 0xAsen, 0xDemon, 0xabhay, 0xblack_bird, 0xlemon, 0xloscar01, 0xtankr, 3docSec, 4rdiii, Abdessamed, AlexCzm, Angry_Mustache_Man, BiasedMerc, Circolors, Cryptor, DMoore, DPS, DedOhWale, Dinesh11G, Dots, GalloDaSballo, Giorgio, Honour, Imp, Jorgect, Krace, KupiaSec, Mrxstrange, NentoR, Pechenite, PoeAudits, Ryonen, SBSecurity, Sabit, T1MOH, TheFabled, TheSavageTeddy, Tychai0s, VAD37, Vasquez, WildSniper, ZanyBonzy, adam-idarrha, alix40, asui, blutorque, btk, c0pp3rscr3w3r, caglankaan, carrotsmuggler, d_tony7470, dimulski, dinkras, djxploit, falconhoof, forgebyola, grearlake, imare, itsabinashb, josephdara, kartik_giri_47538, ke1caM, kennedy1030, koo, lionking927, ljj, niser93, pep7siup, poslednaya, ptsanev, sashik_eth, shaflow2, steadyman, turvy_fuzz, ubl4nk, valentin_s2304, web3km, xyz, y4y, zhaojohnson, zigtur
0.0234 USDC - $0.02
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L119-L127 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L42-L44 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L134-L143
VaultManagerV2::deposit
contains the isValidDnft
modifier, which only ensures that a DNft is valid, not that the owner is calling the deposit function. The issue lies in the fact that any user can call deposit with any DNft and update idToBlockOfLastDeposit[id] = block.number
.
This can be used to block the DNft owners withdrawal of funds, as the check in withdraw will revert as the DNft has had a deposit in the same block by the malicious user. The cost for this attack is gas fees and the protocol has stated it will be deploying on ETH, therefore this is why risk is medium.
function deposit( uint id, address vault, uint amount ) external isValidDNft(id) { idToBlockOfLastDeposit[id] = block.number;
The deposit
function contains the isValidDNft
modifier:
modifier isValidDNft(uint id) { if (dNft.ownerOf(id) == address(0)) revert InvalidDNft(); _; }
The modifier checks that the dNft has a non-zero address, i.e it's been minted. Meaning that any user can deposit funds for any valid dNft, depositing is not restricted to only the owner.
idToBlockOfLastDeposit
is utilised to stop flashloans attack by using a locking mechanism, where once an dNft has a deposit in a block, that block.number
is stored. When calling VaultManagerV2::withdraw
this variable is checked against the current block number, and the function reverts if a deposit has been completed within this block:
function withdraw( uint id, address vault, uint amount, address to ) public isDNftOwner(id) { if (idToBlockOfLastDeposit[id] == block.number) revert DepositedInSameBlock();
The issue arises from the fact that this lock is not user/ address specific, meaning that after a deposit has been made within one block for a dNft, any attemp to withdraw will revert due to the check above.
This can leads to withdrawals reverting due to random deposits within the same block before the withdrawal call is included in the block. Another issue is that a malicious user can monitor withdrawal attempts for specific dNfts, and ensure that a dust deposit using that dNft is placed before the withdrawal within the same block, causing the withdrawal to revert. This can theoretically be repeated forever, causing the owner of the dNft to never be able to withdraw their funds. In reality this attack would cost gas fees, so this attack would be costly to be conducted for a long time.
Manual review
One solution to this issue is to only allow a dNft owner to deposit assets linked to their own dNft. This would ensure that the idToBlockOfLastDeposit[id]
can only be activated by the dNft owner.
DoS
#0 - c4-pre-sort
2024-04-27T11:55:35Z
JustDravee marked the issue as duplicate of #489
#1 - c4-pre-sort
2024-04-29T09:25:37Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T20:41:18Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-05T20:42:04Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2024-05-05T20:45:37Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#5 - c4-judge
2024-05-05T21:55:06Z
koolexcrypto marked the issue as nullified
#6 - c4-judge
2024-05-05T21:55:09Z
koolexcrypto marked the issue as not nullified
#7 - c4-judge
2024-05-08T15:26:34Z
koolexcrypto marked the issue as duplicate of #1001
#8 - c4-judge
2024-05-11T19:48:47Z
koolexcrypto marked the issue as satisfactory
#9 - c4-judge
2024-05-13T18:34:30Z
koolexcrypto changed the severity to 3 (High Risk)
🌟 Selected for report: TheSavageTeddy
Also found by: 0x175, 0x486776, 0xnev, AamirMK, AlexCzm, ArmedGoose, BiasedMerc, CaeraDenoir, Egis_Security, Jorgect, KYP, MrPotatoMagic, PoeAudits, SBSecurity, SovaSlava, VAD37, adam-idarrha, alix40, carrotsmuggler, d_tony7470, dimulski, grearlake, josephdara, ljj, n0kto, okolicodes, sashik_eth, sil3th, turvy_fuzz
7.3512 USDC - $7.35
https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L94-L104 https://github.com/code-423n4/2024-04-dyad/blob/main/src/core/VaultManagerV2.sol#L119-L131
For a vault to be removed from a Dnft, it needs to have no assets left within the specific vault tied to the Dnft id. However due to the fact that anyone can deposit funds for any Dnft ID, a malicious user can front-run an owners call to remove
and DOS the vault removal indefinitely.
A Dnft owner can remove added vaults by calling VaultManagerV2::remove():
function remove( uint id, address vault ) external isDNftOwner(id) { if (Vault(vault).id2asset(id) > 0) revert VaultHasAssets(); if (!vaults[id].remove(vault)) revert VaultNotAdded(); emit Removed(id, vault); }
The first check ensures that no assets are left within the vault that are linked to the Dnft ID. Therefore a normal function call flow to remove a Dnft would be for the owner to first call VaultManagerV2::withdraw() with the full balance and then to call VaultManagerV2::remove().
However, the deposit function allows ANY user to add funds (with no minimum amount) to any Dnft's vault. Meaning a malicious griefer can front-run the owners remove()
call and add a dust amount of funds. This will cause the call to revert as there will be leftover funds tied to the Dnft.
function deposit( uint id, address vault, uint amount ) external isValidDNft(id) { idToBlockOfLastDeposit[id] = block.number; Vault _vault = Vault(vault); _vault.asset().safeTransferFrom(msg.sender, address(vault), amount); _vault.deposit(id, amount); }
The only validation check is ensuring that the Dnft ID is valid, however there is no owner check.
Manual Review
Ensure only the Dnft owner can deposit funds, this will give the owner full control of the Dnft and vault balance tied to the Dnft. This also fixes a different bug related to withdrawal DOS.
DoS
#0 - c4-pre-sort
2024-04-29T07:44:14Z
JustDravee marked the issue as duplicate of #489
#1 - c4-pre-sort
2024-04-29T09:25:39Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-05T20:39:25Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#3 - c4-judge
2024-05-05T20:39:59Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2024-05-05T20:45:34Z
koolexcrypto marked the issue as unsatisfactory: Invalid
#5 - c4-judge
2024-05-05T21:45:27Z
koolexcrypto marked the issue as nullified
#6 - c4-judge
2024-05-05T21:45:31Z
koolexcrypto marked the issue as not nullified
#7 - c4-judge
2024-05-05T21:45:36Z
koolexcrypto marked the issue as not a duplicate
#8 - c4-judge
2024-05-06T08:53:48Z
koolexcrypto marked the issue as duplicate of #118
#9 - c4-judge
2024-05-11T12:23:56Z
koolexcrypto marked the issue as satisfactory