DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Improper Handling of Chainlink Oracle Return Values in KeeperProxy._check()

Summary

The function KeeperProxy._check() (lines 188-198 in KeeperProxy.sol) fails to properly handle return values from Chainlink's latestRoundData() function. Specifically, it ignores key return values (answeredInRound, roundID), which can lead to oracle manipulation attacks or the use of stale price data. This creates a critical security risk, potentially enabling attackers to exploit outdated or manipulated price feeds.

Vulnerability Details

Affected Function:

function _check(address token, uint256 price) internal view {
(, int256 chainLinkPrice, , uint256 updatedAt, ) = AggregatorV2V3Interface(dataFeed[token]).latestRoundData();
require(updatedAt > block.timestamp - maxTimeWindow[token], "stale price feed");
uint256 decimals = 30 - IERC20Meta(token).decimals();
price = price / 10 ** (decimals - 8); // Chainlink price decimals is always 8.
require(
_absDiff(price, chainLinkPrice.toUint256()) * BPS / chainLinkPrice.toUint256() < priceDiffThreshold[token],
"price offset too big"
);
}

Key Issues:

  1. Failure to Check answeredInRound:

    • The function does not validate answeredInRound, which is essential to confirm that the returned price is from a valid and latest round.

    • An attacker could force the contract to use an older price round, causing incorrect liquidations or price calculations.

  2. Potential Use of Stale Data:

    • Although updatedAt is checked, there is no verification that answeredInRound >= roundID, allowing for potential oracle exploitation if the price feed is delayed or manipulated.

    • This could allow an attacker to replay an old, favorable price round.

  3. Risk of Using an Invalid Price:

    • The function does not validate that chainLinkPrice is a valid, non-zero price.

    • A manipulated oracle feed could return 0 or a negative value, breaking the contract's logic.


Impact

  1. Manipulation of Price Feeds

    • Attackers could force the contract to use outdated price rounds, leading to potential incorrect liquidations or price arbitrage attacks.

  2. Financial Loss to Users

    • If stale prices are used, liquidations may happen unfairly, causing loss of funds for users holding leveraged positions.

  3. Contract May Rely on Invalid Data

    • Without checking answeredInRound, the system may continue operating on incorrect or outdated oracle data, exposing it to market manipulation risks.

Tools Used

Vs code

Recommendations

1. Validate answeredInRound to Ensure the Price Data is from a Valid Round

require(answeredInRound >= roundID, "Invalid price round detected");

2. Check updatedAt Against a Maximum Age Window

require(updatedAt > block.timestamp - maxTimeWindow[token], "Stale price feed");

3. Ensure chainLinkPrice is Not Zero or Negative

require(chainLinkPrice > 0, "Invalid Chainlink price");

Fixed _check() Implementation

function _check(address token, uint256 price) internal view {
(, int256 chainLinkPrice, , uint256 updatedAt, uint80 answeredInRound) =
AggregatorV2V3Interface(dataFeed[token]).latestRoundData();
require(updatedAt > block.timestamp - maxTimeWindow[token], "Stale price feed");
require(answeredInRound >= roundID, "Invalid price round detected");
require(chainLinkPrice > 0, "Invalid Chainlink price");
uint256 decimals = 30 - IERC20Meta(token).decimals();
price = price / 10 ** (decimals - 8); // Chainlink price decimals are always 8.
require(
_absDiff(price, uint256(chainLinkPrice)) * BPS / uint256(chainLinkPrice) < priceDiffThreshold[token],
"Price offset too big"
);
}
Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.