This repository has been archived by the owner on Jul 14, 2024. It is now read-only.
ilchovski - Admin can break the protocol logic irreversibly by adding the same collateral twice #145
Labels
Non-Reward
This issue will not receive a payout
ilchovski
medium
Admin can break the protocol logic irreversibly by adding the same collateral twice
Summary
UbiquityPoolFacet.sol:addCollateralToken or more specifically the library function it uses LibUbiquityPool:addCollateralToken allows the admin to add the same collateral token twice. This action would break view functions logic, allow users to mint Dollars with misspriced collateral, deposit more collateral than the specified pool ceiling and being able to redeem dollars even if there is no free collateral in the contract.
Vulnerability Detail
Adding a collateral token 2 times with
addCollateralToken
affects the protocol the following way:collateralUsdBalance
addCollateralToken updates the following storage variables:
collateralUsdBalance
should return the sum of all collateral value the protocol holds in USD by looping through allpoolStorage.collateralAddresses
.Since the collateral addresses can be duplicated inside the array and since inside the loop the function
freeCollateralBalance
usesIERC20(collateralAddress).balanceOf(address(this))
(can be seen here) this would makecollateralUsdBalance
return significantly higher sum of all collateral USD value than there is in reality.External services could rely on this information and the function would not be able to be fixed by the admin unless the pool facet implementation is upgraded.
collateralInformation
This function (link) would be able to return the data only of the lastly added duplicated collateral. The old collateral index data would not be accessible anymore because of the way the collateral is selected (link):
setCollateralChainLinkPriceFeed
This function will be able to update the oracle address and staleness threshold only for the newly added duplicated collateral address. If the oracle the collateral uses for pricing needs to be updated this would not be possible for the initial collateralIndex thus allowing the collateral to be valued at incorrect price or to thow error when this collateralIndex is used.
toggleCollateral
toggleCollateral
toggles collaterals by index, by 2 of the same collateral addresses would mean that when we disable collateral address at index A, this would also mean that we have disabled the usage of the collateral at index B. This is bad because after the admin have added a collateral twice he will not be able to stop users from using whichever version of the collateral they please. The decision would be to not use this collateral forever or to let users use whichever version they would like.What the user can do if the contract is in this state (the same collateral address is added 2 times as collateral)?
mintDollar
to provide collateral at possibly incorrect price by using the initial collateral index of the duplicated collateral asset (sinceupdateChainLinkCollateralPrice
that is used to update the price of the collateral before the minting uses the specified oracle for the collateral at the specified index link).This is possible since, as mentioned above in
setCollateralChainLinkPriceFeed
, we cannot update the oracle address or threshold at the old index if such change is necessary at any point. This means that the user can either harm himself or the protocol based on the price of the oracle by minting more or less Dollars he is supposed to. (getDollarInCollateral
decides how much collateral must be provided based on the specified Dollar amount and the collateral price link)The
pool ceiling check
insidemintDollar
(the one here) can be bypassed since freeCollateralBalance returns a value that is the balance of the collateral that the contract holds minus theunclaimedPoolCollateral
for the specific collateral index. Meaning if the unclaimedPoolCollateral for the duplicated collateral at index A is low and this does not allow the pool ceiling check to pass, the user can decide to mintDollar by using index B where the unclaimedPoolCollateral could be higher. This way the user would be able to mint Dollar tokens despite the collateral ceiling limit.The user is able to use redeemDollar even if there is not enough collateral for withdrawing (
contract collateral balance minus unclaimedAmount
- the unclaimed amount is from users that have used redeemDollar before but still have not used collectRedemption to take out their collateral) because of being able to bypass the Insufficient pool collateral check by using collateral index of the duplicated collateral where the unclaimed amount is lower compared with the other index (this is the same problem like the check inside mintDollar from point 2 explained above due to both checks usingpoolStorage.unclaimedPoolCollateral[collateralIndex]
). This means that a user that have already used redeemDollar but still haven't collected his collateral with collectRedemption could not be able to withdraw his collateral if another user does that before him by using the method described above. Having one real underlying balance of a collateral token by using IERC20(token).balanceOf(address(this)) and 2 separate unclaimedCollateral variables made for each collateral token index makes this issue possible.Impact
Break view functions logic, allow users to mint Dollars with miss-priced collateral, deposit more collateral than the specified pool ceiling and being able to redeem dollars even if there is no free collateral in the contract.
Code Snippet
Tool used
Manual Review
Recommendation
A mechanism must be used to not allow the adding of duplicated collateral addresses.
The simplest thing to do is to introduce a mapping which tracks if a collateral address have already been added in the array:
Duplicate of #27
The text was updated successfully, but these errors were encountered: