Platform: Code4rena
Start Date: 02/06/2023
Pot Size: $100,000 USDC
Total HM: 15
Participants: 75
Period: 7 days
Judge: Picodes
Total Solo HM: 5
Id: 249
League: ETH
Rank: 52/75
Findings: 1
Award: $31.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Madalad
Also found by: Aymen0909, Bauchibred, Breeje, DadeKuma, Hama, LaScaloneta, Madalad, MohammedRizwan, bin2chen, dwward3n, erictee, etherhood, kutugu, peanuts, piyushshukla, rvierdiiev, saneryee, tallo, turvy_fuzz, whimints
31.7954 USDC - $31.80
The StaderOracle
contract uses the Chainlink AggregatorV3
interface to fetch the total ETH balance as well as the total ETHx balance through the latestRoundData
function. However, this function may return stale data if Chainlink node consensus is blocked or any other system failure (or manipulation) occurs.
To make these cases detectable, latestRoundData
returns multiple values that need to be validated:
uint80 roundId
int256 answer
uint256 startedAt
uint256 updatedAt
uint80 answeredInRound
None of this data is validated and only the data is returned, posing a high risk for stale or invalid oracle data entering the system.
The lack of validation is present here:
{ (, int256 totalETHBalanceInInt, , , ) = AggregatorV3Interface(staderConfig.getETHBalancePORFeedProxy()) .latestRoundData(); (, int256 totalETHXSupplyInInt, , , ) = AggregatorV3Interface(staderConfig.getETHXSupplyPORFeedProxy()) .latestRoundData(); return (uint256(totalETHBalanceInInt), uint256(totalETHXSupplyInInt), block.number); }
None.
If the underlying Chainlink aggregator data is stale, the round ID will stay the same. Furthermore, the startedAt
timestamp should remain the same as in the previously fetched round. answeredInRound
is the round ID where the answer was computed, so it will stay the same as the last round's information as well.
Please refer to the Chainlink documentation regarding the specific properties and how to best leverage them to avoid stale data.
Oracle
#0 - c4-judge
2023-06-10T14:42:54Z
Picodes marked the issue as duplicate of #15
#1 - c4-judge
2023-06-10T14:45:59Z
Picodes changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-07-02T10:49:47Z
Picodes marked the issue as satisfactory
🌟 Selected for report: Madalad
Also found by: Aymen0909, Bauchibred, Breeje, DadeKuma, Hama, LaScaloneta, Madalad, MohammedRizwan, bin2chen, dwward3n, erictee, etherhood, kutugu, peanuts, piyushshukla, rvierdiiev, saneryee, tallo, turvy_fuzz, whimints
31.7954 USDC - $31.80
The POR feed is used to update the exchange rate data through the StaderOracle
contract. For the system to calculate the exchange rate correctly, it is paramount that the oracle-provided data is recent. However, the getPORFeedData
function always returns block.number
as its reportingBlockNumber
.
This is insecure as the Chainlink aggregator may return stale data, which would be presented by this function as recent and valid.
The line always returns block.number
:
The function consuming the block number and updating the price data based on it:
None.
I recommend either refactoring the price data system to work with time stamps and propagating the Chainlink Aggregator-provided time stamps, or adding general validation mechanisms for the Aggregator-integration that prevents stale data from bubbling up into the system, e.g. by reverting if a stale or invalid update would be executed.
Oracle
#0 - c4-judge
2023-06-10T14:43:19Z
Picodes marked the issue as duplicate of #15
#1 - c4-judge
2023-06-10T14:46:01Z
Picodes changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-07-02T10:49:46Z
Picodes marked the issue as satisfactory