The verifier relies on an external blockhash oracle to validate proofs for state updates. If the blockhash oracle returns stale data—due, for example, to latency—attackers may submit valid proofs corresponding to older blocks.
This can result in the oracle adopting historical vault parameters (such as manipulated values of total_supply
, total_idle
, or balance_of_self
), thereby freezing price updates or causing incorrect pricing.
The verifyScrvusdByBlockHash
function validates a state proof by comparing the blockhash from a provided block header with the blockhash retrieved from an external oracle. The assumption is that the blockhash oracle delivers fresh data that reflects the current chain state.
The verifier trusts that the blockhash oracle always returns recent blockhashes, within a maximum acceptable age (e.g., no older than 30 minutes as per project specs). However, if the oracle suffers delays or latency, it may return blockhashes from older blocks.
An attacker can exploit this by submitting valid proofs that reference these older blocks. If, during those historical blocks, vault parameters (like total_supply
or balance_of_self
) were manipulated or abnormal, the oracle will update its state based on stale data rather than the current state.
The use of outdated parameters leads to incorrect calculation of the raw_price
. Because the smoothing mechanism only moderates per-update changes, basing an update on historical data can cause abrupt price deviations, undermining the integrity of the price feed.
The oracle may report prices that do not reflect current market conditions, creating opportunities for arbitrage.
Although the vulnerability hinges on the update frequency and freshness of the blockhash oracle, any delay that allows stale data to be used for state updates can lead to significant economic risks.
Manual Review
Modify the verifier to reject proofs associated with block numbers that exceed a predefined age limit (e.g., 30 minutes or a specific number of blocks).
Incorporate checks that compare the block timestamp from the block header with the current time, ensuring that proofs based on outdated timestamps are rejected.
- Anything related to the output by the `BLOCK_HASH_ORACLE` is OOS per \[docs here]\(<https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle>). - The PoC utilizes a mock `BLOCK_HASH_ORACLE`which is not representative of the one used by the protocol - Even when block hash returned is incorrect, the assumption is already explicitly made known in the docs, and the contract allows a subsequent update within the same block to update and correct prices - All state roots and proofs must be verified by the OOS `StateProofVerifier` inherited as `Verifier`, so there is no proof that manipulating block timestamp/block number/inputs can affect a price update - There seems to be a lot of confusion on the block hash check. The block hash check is a unique identifier of a block and has nothing to do with the state root. All value verifications is performed by the OOS Verifier contract as mentioned above
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.