Skip to content

Latest commit

 

History

History
77 lines (51 loc) · 3.51 KB

File metadata and controls

77 lines (51 loc) · 3.51 KB

Big Obsidian Crab

Medium

Missing Timestamp Validation for Pyth Oracle Price Feeds Leading to Stale Data Use

Summary

The _getLatestPrice retrieves price data from the Pyth oracle using the getPriceUnsafe function, which provides the price without any checks for recency or validity. This introduces a critical risk, as there is no validation of the publishTime of the price, meaning that outdated or stale prices could be used in critical operations such as collateral valuation, borrowing, and liquidation.

Root Cause

In PythOracle.sol: 93, the function _getLatestPrice retrieves price data from the Pyth oracle using getPriceUnsafe, which does not perform recency checks on the price data. Consequently, price data could be outdated or stale and could be used without verification of its freshness, which could lead to incorrect price of assets.

This is a valid issue because it is clearly stated in Pyth contract:

// @dev This function returns the most recent price update in this contract without any recency checks.
    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

https://github.com/pyth-network/pyth-sdk-solidity/blob/main/IPyth.sol

Using the price fetched without validation will lead to financial loss.

function _getLatestPrice(address token) internal view returns (uint256, uint256) {
        // Return 0 if price feed id is not set, reverts are handled by caller
        if (priceFeedIds[token] == bytes32(0)) return (0, 0);

        bytes32 priceFeedId = priceFeedIds[token];
        PythStructs.Price memory pythPrice = pyth.getPriceUnsafe(priceFeedId);

        uint256 price = uint256(uint64(pythPrice.price));
        uint256 expo = uint256(uint32(-pythPrice.expo));

        return (price, expo);
    }

https://github.com/sherlock-audit/2024-12-mach-finance/blob/main/contracts/src/Oracles/Pyth/PythOracle.sol#L93

Internal Pre-conditions

The protocol relies on Pyth oracle data to calculate token prices and perform operations such as collateral valuation, borrowing, and liquidation.

External Pre-conditions

The Pyth oracle provides price feeds that could potentially be delayed or stale, leading to inaccurate or outdated price data being returned if the oracle's data is not properly validated.

Attack Path

No response

Impact

Financial loss due to usage of stale price.

PoC

No response

Mitigation

Implement validation for the publishTime of price data retrieved from the Pyth oracle. Ensure that the price is recent enough to be trusted (e.g., reject prices older than a certain threshold, such as 15 minutes or 1 hour).

Example:

uint256 currentTime = block.timestamp;
require(currentTime - pythPrice.publishTime <= MAX_ALLOWED_STALE_TIME, "Price is too stale");