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: 114/183
Findings: 2
Award: $7.54
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Circolors
Also found by: 0x175, 0x486776, 0xAlix2, 0xSecuri, 0xShitgem, 0xfox, 0xlemon, 0xnilay, 3th, 4rdiii, Aamir, Al-Qa-qa, AlexCzm, Egis_Security, Evo, Honour, Infect3d, Josh4324, Limbooo, Mahmud, SBSecurity, TheSchnilch, ahmedaghadi, alix40, amaron, bbl4de, bhilare_, btk, carrotsmuggler, cinderblock, d3e4, dimulski, dinkras, ducanh2706, iamandreiski, itsabinashb, ke1caM, ljj, sashik_eth, shaflow2, steadyman, web3km, y4y
3.8221 USDC - $3.82
In VaultManagerV2::withdraw()
function, to calculate the value of assets to withdraw _vault.oracle().decimals()
is called:
uint value = (amount * _vault.assetPrice() * 1e18) / @> 10 ** _vault.oracle().decimals() / 10 ** _vault.asset().decimals();
However, the issue is that neither bounded or unbounded kerosene vaults have an oracle. Considering that Vault.kerosine.unbounded::withdraw
is protected by the onlyVaultManager
modifier, it becomes impossible to withdraw your unbounded kerosene as any call will revert.
To showcase this evm revert create a ./test/BaseTestV2.t.sol
test file and use the code from this gist where the new VaultManagerV2 was deployed together with Mock kerosene and vaults. To run the test:
forge test --mt test_withdrawFails -vvv
Manual review
One possible solution is to add another withdraw function in VaultManagerV2
assigned strictly to kerosene vaults, that would automatically input the required decimals. Careful reconsideration of the value
calculation method is necessary.
Other
#0 - c4-pre-sort
2024-04-26T21:47:14Z
JustDravee marked the issue as duplicate of #1048
#1 - c4-pre-sort
2024-04-28T18:38:38Z
JustDravee marked the issue as duplicate of #830
#2 - c4-pre-sort
2024-04-29T08:44:32Z
JustDravee marked the issue as sufficient quality report
#3 - c4-judge
2024-05-11T20:04:38Z
koolexcrypto marked the issue as satisfactory
🌟 Selected for report: carrotsmuggler
Also found by: 0xAlix2, 0xSecuri, 0xleadwizard, 0xlemon, 0xtankr, 3th, Aamir, Abdessamed, Bauchibred, Circolors, Egis_Security, Evo, Hueber, Mahmud, SBSecurity, TheSavageTeddy, TheSchnilch, Tychai0s, alix40, bbl4de, btk, d3e4, ducanh2706, falconhoof, itsabinashb, ke1caM, lian886, n4nika, oakcobalt, pontifex, sashik_eth, steadyman, tchkvsky, zhuying
3.7207 USDC - $3.72
VaultManagerV2::addKerosene()
requires the vault
to be licensed in the keroseneManager
:
if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed();
This however, breaks adding kerosene vaults functionality completely. Licensed vaults in kerosineManager
are simply ones that are in the KerosineManager::vaults
address set, which is used to calculate kerosene price based on EXOGENOUS collateral.
Specifically every call to VaultManagerV2::getKeroseneValue
will fail, because of the following line:
if (keroseneManager.isLicensed(address(vault))) { usdValue = vault.getUsdValue(id); }
where KerosineVault::getUsdValue()
calls assetPrice()
, which finally attempts to call vault.oracle()
function which is impossible for kerosene vaults. This vulnerability does not cause incorrect calculations, but rather blocks entirely any function in VaultManagerV2
that internally calls collatRatio()
: mintDyad()
and liquidate()
due to an evm error revert.
In short, either kerosene vaults would be impossible to add to your Note NFT - because of the incorrect licensing check in addKerosene()
, or if they were somehow added - any user could block liquidation functionality at will, protecting themselves from the price action of other collateral assets.
To showcase this evm revert create a ./test/BaseTestV2.t.sol
test file and use the code from this gist where the new VaultManagerV2 was deployed together with mock kerosene and vaults. To run the test:
forge test --mt test_keroseneVaults
Detailed thought process is included in the comments.
Manual review
To mitigate this issue. calling addKerosene()
should not be limited by whether the vault is licensed in the kerosene manager. However, to keep the licensing for kerosene vaults as well, consider the following change in addKerosene()
:
function addKerosene(uint id, address vault) external isDNftOwner(id) { if (vaultsKerosene[id].length() >= MAX_VAULTS_KEROSENE) revert TooManyVaults(); - if (!keroseneManager.isLicensed(vault)) revert VaultNotLicensed(); + if (!vaultManager.isLicensed(vault)) revert VaultNotLicensed(); if (!vaultsKerosene[id].add(vault)) revert VaultAlreadyAdded(); emit Added(id, vault); }
and simply use licensing from the Licenser
contract to ensure vaults that are available for the users are secure.
Other
#0 - c4-pre-sort
2024-04-29T05:16:41Z
JustDravee marked the issue as duplicate of #70
#1 - c4-pre-sort
2024-04-29T09:36:55Z
JustDravee marked the issue as sufficient quality report
#2 - c4-judge
2024-05-11T19:58:48Z
koolexcrypto marked the issue as satisfactory
#3 - c4-judge
2024-05-13T18:36:27Z
koolexcrypto changed the severity to 2 (Med Risk)