Open code423n4 opened 2 years ago
Duplicate of #601
0xean marked the issue as duplicate
0xean marked the issue as selected for report
Simon-Busch marked the issue as not a duplicate
Simon-Busch marked the issue as primary issue
08xmt marked the issue as sponsor confirmed
Lines of code
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L78-L105 https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L112-L144 https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L344-L347 https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L323-L327 https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L353-L363
Vulnerability details
Impact
Calling the
Oracle
contract'sviewPrice
orgetPrice
function executesuint price = feeds[token].feed.latestAnswer()
andrequire(price > 0, "Invalid feed price")
. Besides that Chainlink'slatestAnswer
function is deprecated, only verifying thatprice > 0
is true is also not enough to guarantee that the returnedprice
is not stale. Using a staleprice
can cause the calculations for the credit and withdrawal limits to be inaccurate, which, for example, can mistakenly consider a user's debt to be under water and unexpectedly allow the user's debt to be liquidated.To avoid using a stale answer returned by the Chainlink oracle data feed, according to Chainlink's documentation:
latestRoundData
function can be used instead of the deprecatedlatestAnswer
function.roundId
andansweredInRound
are also returned. "You can checkansweredInRound
against the currentroundId
. IfansweredInRound
is less thanroundId
, the answer is being carried over. IfansweredInRound
is equal toroundId
, then the answer is fresh."https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L78-L105
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L112-L144
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L344-L347
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L323-L327
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Market.sol#L353-L363
Proof of Concept
The following steps can occur for the described scenario.
depositAndBorrow
function to deposit some WETH as the collateral and borrows some DOLA against the collateral.liquidate
function for trying to liquidate Alice's debt. Because the Chainlink oracle data feed returns an up-to-date price at this moment, thegetCreditLimitInternal
function calculates Alice's credit limit accurately, which does not cause Alice's debt to be under water. Hence, Bob'sliquidate
transaction reverts.liquidate
function again for trying to liquidate Alice's debt. This time, because the Chainlink oracle data feed returns a positive but stale price, thegetCreditLimitInternal
function calculates Alice's credit limit inaccurately, which mistakenly causes Alice's debt to be under water.liquidate
transaction is executed successfully so he gains some of Alice's WETH collateral. Alice loses such WETH collateral amount unexpectedly because her debt should not be considered as under water if the stale price was not used.Tools Used
VSCode
Recommended Mitigation Steps
https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L82-L83 and https://github.com/code-423n4/2022-10-inverse/blob/main/src/Oracle.sol#L116-L117 can be updated to the following code.