Skip to content

Latest commit

 

History

History
120 lines (87 loc) · 4.67 KB

File metadata and controls

120 lines (87 loc) · 4.67 KB

Fast Khaki Raccoon

Medium

Even if oracles are marked as "bad" borrowing will still be possible

Summary

Even if oracles are marked as "bad" borrowing will still be possible, due to both highExchangeRate and lowExchangeRate being the same, and passing the bellow check as _deviation would be 0

        uint256 _deviation = (
            DEVIATION_PRECISION * (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)
        ) / _exchangeRateInfo.highExchangeRate;

        if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
            _isBorrowAllowed = true;
        }

Root Cause

When bad data is returned both _priceLow and _priceHigh are set to the same price one

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/fraxlend/src/contracts/oracles/dual-oracles/DualOracleChainlinkUniV3.sol#L138-L150

    function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) {
        address[] memory _pools = new address[](1);
        _pools[0] = UNI_V3_PAIR_ADDRESS;
        uint256 _price1 = IStaticOracle(0xB210CE856631EeEB767eFa666EC7C1C57738d438).quoteSpecificPoolsWithTimePeriod(
            ORACLE_PRECISION, BASE_TOKEN, QUOTE_TOKEN, _pools, TWAP_DURATION
        );
        uint256 _price2;
        (_isBadData, _price2) = _getChainlinkPrice();

        // If bad data return price1 for both, else set high to higher price and low to lower price
        _priceLow = _isBadData || _price1 < _price2 ? _price1 : _price2;
        _priceHigh = _isBadData || _price1 > _price2 ? _price1 : _price2;
    }

This can be if one of the oracles reports late:

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/fraxlend/src/contracts/oracles/dual-oracles/DualOracleChainlinkUniV3.sol#L106-L127

        if (CHAINLINK_MULTIPLY_ADDRESS != address(0)) {
            (, int256 _answer,, uint256 _updatedAt,) =
                AggregatorV3Interface(CHAINLINK_MULTIPLY_ADDRESS).latestRoundData();

            // If data is stale or negative, set bad data to true and return
            if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) {
                _isBadData = true;
                return (_isBadData, _price);
            }
            _price = _price * uint256(_answer);
        }

        if (CHAINLINK_DIVIDE_ADDRESS != address(0)) {
            (, int256 _answer,, uint256 _updatedAt,) = AggregatorV3Interface(CHAINLINK_DIVIDE_ADDRESS).latestRoundData();

            // If data is stale or negative, set bad data to true and return
            if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) {
                _isBadData = true;
                return (_isBadData, _price);
            }
            _price = _price / uint256(_answer);
        }

Where since both highExchangeRate and lowExchangeRate are gonna be the same - TWAP there won't be any deviation, meaning borrowing is allowed:

https://github.com/sherlock-audit/2025-01-peapods-finance/blob/main/fraxlend/src/contracts/FraxlendPairCore.sol#L552-L557

        uint256 _deviation = (
            DEVIATION_PRECISION * (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)
        ) / _exchangeRateInfo.highExchangeRate;

        if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
            _isBorrowAllowed = true;
        }

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

The system will not work in general and the attack can happen without the intention or knowledge on the side of the borrower

  1. Price decreases by 20% in the span of 3min due to a sudden crash/hack/etc...
  2. Chainlink is stopped for some reason - happened in the luna crash or with any rugged/hacked tokens when their prices drop 90% or more
  3. User borrows against that token
  4. TWAP is set to 20min, so price is barely impacted
  5. This causes bad debt or insolvency

Impact

Borrowing is allowed when it shouldn't be as there is no available comparison to calculate _deviation. Even worse, a there is a "bad" oracle it may mean that the price is unstable and or rapidly decreasing, making the TWAP dangerous in this scenario as if the price drops 20% in 5 min (pretty common in crypto) a TWAP of 10min would record a slight drop and a one with 20 or 30min will be barely impacted.

PoC

No response

Mitigation

Allow liquidations and repayments, but make sure to disable borrowing when the oracle is bad.

-       if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
+       if (_deviation > 0 && _deviation <= _exchangeRateInfo.maxOracleDeviation) {
            _isBorrowAllowed = true;
        }