DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: medium
Invalid

Lack of Timestamp Validation in ScrvusdVerifier Risks Stale State Updates

Summary

The ScrvusdVerifierV1 and ScrvusdVerifierV2 contracts do not enforce that the timestamp of a submitted block header is newer than the last recorded state update. These contracts rely solely on a block number check (last_block_number <= _block_number) to ensure data progression, omitting a direct timestamp comparison. This omission could allow the acceptance of stale data in rare scenarios, such as blockchain reorganizations or timestamp anomalies, potentially leading to outdated state updates in the scrvUSD oracle.

Vulnerability Details

The contracts process state proofs to update the scrvUSD oracle’s parameters via functions like ScrvusdVerifierV1::verifyScrvusdByBlockHash and function ScrvusdVerifierV2::verifyPeriodByBlockHash. They verify that the submitted block number is not older than the last recorded block number but do not check the block header’s timestamp against last updated timestamp. This leaves a gap where data from a block with a higher number but an earlier timestamp could be accepted, compromising the oracle’s temporal consistency.

Impact

An attacker submits a valid proof for an older block with a timestamp predating the current state, bypassing the block number check.

Tools Used

Recommendations

Add a Timestamp State Variable:

uint256 public lastUpdateTimestamp;

Example Fix for verifyScrvusdByBlockHash

function verifyScrvusdByBlockHash(bytes memory _block_header_rlp, bytes memory _proof_rlp) external returns (uint256) {
Verifier.BlockHeader memory block_header = Verifier.parseBlockHeader(_block_header_rlp);
require(block_header.hash != bytes32(0), "Invalid blockhash");
require(block_header.hash == IBlockHashOracle(BLOCK_HASH_ORACLE).get_block_hash(block_header.number), "Blockhash mismatch");
+ require(block_header.timestamp > lastUpdateTimestamp, "stale data"); // Added check
uint256[PARAM_CNT] memory params = _extractParametersFromProof(block_header.stateRootHash, _proof_rlp);
- return _updatePrice(params, block_header.timestamp, block_header.number);
+ uint256 result = _updatePrice(params, block_header.timestamp, block_header.number);
+ lastUpdateTimestamp = block_header.timestamp; // Update timestamp
+ return result;
}
Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

[invalid] finding-missing-proof-content-validation

- See [here]([https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle)](https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle) on how it is used to verify storage variable - All state roots and proofs must be verified by the OOS `StateProofVerifier` inherited as `Verifier` (where the price values and params are extracted), so there is no proof that manipulating timestamp/inputs can affect a price update - It is assumed that the OOS prover will provide accurate data and the OOS verifier will verify the prices/max unlock time to be within an appropriate bound/values - There is a account existance check in L96 of `ScrvusdVerifierV1.sol`, in which the params for price updates are extracted from

Support

FAQs

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