Closed c4-bot-3 closed 7 months ago
0xRobocop marked the issue as duplicate of #245
3docSec changed the severity to QA (Quality Assurance)
3docSec marked the issue as grade-b
Hi @3docSec. Thank you for your judging! I saw your comment here:
The Oracle price is set by the team and does not follow normal market fluctuations - this significantly mitigates risk. However, the recommended mitigation of implementing a setter makes sense, so this group makes for a good QA finding.
The Oracle price is indeed set by the team. However, in the section Background on OUSG
of contest page documentation, we can read:
OUSG is backed by an off chain portfolio comprised of cash equivalents. The price of OUSG tracks
this portfolio's performance minus management fees. OUSG is supported in collateral-only mode
on the Flux Finance protocol.
So, the price of OUSG depends on the portfolio's performance. According to this, the Ondo team shouldn't set a price that differs from the portfolio's performance, even if this performance leads to a price decrease. In 245 sponsor wrote:
There would not be a situation in which we would want to allow instant mints or redemptoins
at a price lower than MINIMUM_OUSG_PRICE.
However, in this way, they could move against the market, and the market always wins. If the portfolio's performance goes bad, they have to choose if keeping to set a higher OUSG price unbounded from that performance, or DoS OusgInstantManager functionalities. We think this would have a reputation impact in any case. For these reasons, the function of the protocol or its availability could be impacted
and this finding should have medium severity.
Thanks in advance
Hi @niser93 I see what you mean.
If the portfolio's performance goes bad, they have to choose if keeping to set a higher OUSG price unbounded from that performance, or DoS OusgInstantManager functionalities.
This is a fairly accurate description, but entirely known, and more than that it's how the protocol is designed to be, that is rather centralized. It can therefore be best categorized as a systemic or centralization risk rather than a proper HM finding.
Thank you for your answer! I'm answering above this issue, but similar considerations are valid for #166 and #251.
1) According to documentation, oracle's price is bound to the portfolio's performance. This documentation should be the only source of truth. Even if the oracle works well, the problem still could happen: it is not a centralization risk 2) According to what the sponsor said, "There would not be a situation in which we would want to allow instant mints or redemptoins at a price lower than MINIMUM_OUSG_PRICE.". However, it goes against documentation. If the price goes down, this behavior seems like an indirect way to pause contract functionalities. 3) Yes, it seems a systemic issue. There is a wrong assumption: they assumed the portfolio's performance couldn't go bad. But why a systemic issue should not be a valid HM? Many issues in the past were found on the wrong market assumptions of developers. I think this is not different.
Furthermore, the severity categorization rules are based on the consequences of a vulnerability. As we said above the function of the protocol or its availability could be impacted
, and this happens when the oracle is working according to the documentation.
Roger that. This doesn't change that the oracle is set by the Ondo team, as stated in the README. This means that at each and every point in time, the protocol will behave like the team wants it to. That's not a vulnerability, it's a centralization issue. The decision on this escalation is final.
Lines of code
https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L479-L485 https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L63
Vulnerability details
Impact
The
MINIMUM_OUSG_PRICE
constant is defined in ousgInstantManager.sol#L63 with value105e18
. This constant is used in getOUSGPrice() function:So, the constant is used to protect against Oracle malfunction. However, the value of OUSG could genuinely decrease under
105e18
thanks to market fluctuations. In this case, several functionalities of OUSGInstantManager will be unavailable:Proof of Concept
In ousgInstantManager.sol#L63 is defined the
MINIMUM_OUSG_PRICE
constant:This constant is used just inside
ousgInstantManager.getOUSGPrice()
function:This function returns the price of OUSG using 18 decimals calling the
oracle.getPriceData()
function.As described inside the Discord channel, this is the oracle contract: RWAOracleExternalComparisonCheck. So, this is the
oracle.getPriceData()
function:So, the returned price is the value of the
rwaPrice
variable. It is set initially inside theconstructor
(L107):and then it can be modified just using the
setPrice()
function (L214)As we can see, inside the oracle there is no check about the OUSG's price. There are many conditions, described into
setPrice()
comments (L130-146):but none of these conditions state that the price of OUSG will not decrease under
105e18
. This means that the oracle could work well, and returns the right OUSG price, butousgInstantManager.getOUSGPrice()
still reverts.We don't think this is the wanted behavior. The intended goal of
MINIMUM_OUSG_PRICE
constant should be to protect against oracle malfunction. To date, the price of OUSG is about 105.56$, according to coingecko. It followed a constant increase in its value since it was created. However, it has suffered losses of value in the past. For example:In the case above, the
ousgInstantManager
contract would suffer a block of its functionalities, and this condition would have lasted for at least 24 hours because the oracle updates the price once a day. In this case, the user can't use the following functionalities, as we said above:OUSGInstantManager._mintRebasingOUSG() and OUSGInstantManager._mint()
OUSGInstantManager._mintRebasingOUSG()
is used to mint rOUSG for a given amount of USDC. It callsOUSGInstantManager._mint()
at line ousgInstantManager.sol#L263.OUSGInstantManager._mint()
callsousgInstantManager.getOUSGPrice()
at line ousgInstantManager.sol#L307. According to the above situation, it reverts. The user's transaction used to mint rOUSG at 104.87$ fails.OUSGInstantManager.redeemRebasingOUSG() and OUSGInstantManager._redeem()
OUSGInstantManager.redeemRebasingOUSG()
is used to redeem USDC for a given amount of rOUSG (even if the comment of the function is wrong). It callsOUSGInstantManager._redeem()
at line ousgInstantManager.sol#L379.OUSGInstantManager._redeem()
callsousgInstantManager.getOUSGPrice()
at line ousgInstantManager.sol#L399. According to the above situation, it reverts. The user's transaction used to redeem rOUSG at 104.87$ fails. In this case, if there is a drop in OUSG value, users can't redeem their rOUSG. This could cause a further drop in the value, and so on.Further considerations
We want to underline that Ondo's team accepts the possibility that the price of OUSG decrease. As described in the contest's C4 webpage, in the
Automated Findings / Publicly Known Issues
section:However, the problem described above isn't due to an extreme change in the price and doesn't describe a problem due to a “negative rebase”.
Moreover, in rOUSG.sol#L378-L380 there is a local version of
getOUSGPrice()
, but without any constant or validation check on the price value. We reported another issue about it and the need to protect against oracle malfunction using flexible behavior.Tools Used
Visual inspection
Recommended Mitigation Steps
The goal to protect against oracle malfunction is genuine. However, there should be the possibility to change the
MINIMUM_OUSG_PRICE
value:This proposal will guarantee to prevent DoS in case of a drop in OUSG's value. Furthermore, it permits to guarantee that ADMIN_ROLE cannot set a too stringent condition.
Assessed type
Invalid Validation