DeFiLayer 1Layer 2
14,723 OP
View results
Submission Details
Severity: low
Valid

Timestamp Surrogate Vulnerability in ScrvusdVerifierV1

Summary

In the ScrvusdVerifierV1 contract related to the use of an incorrect timestamp surrogate in the verifyScrvusdByStateRoot function. The function inappropriately uses the last_profit_update parameter from the SCRVUSD contract as a timestamp when updating the price in the oracle. This substitution creates a significant security risk that could potentially lead to price manipulation and compromise the integrity of the oracle's price feed.

Vulnerability Details

The vulnerability is located in the verifyScrvusdByStateRoot function:

function verifyScrvusdByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (uint256) {
bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
uint256[PARAM_CNT] memory params = _extractParametersFromProof(state_root, _proof_rlp);
// Use last_profit_update as the timestamp surrogate
return _updatePrice(params, params[5], _block_number);
}

The issue lies in the use of params[5] as the timestamp parameter when calling _updatePrice. According to the contract's configuration, params[5] corresponds to last_profit_update from the SCRVUSD contract storage, which is defined in the PARAM_SLOTS array:

uint256[PROOF_CNT] internal PARAM_SLOTS = [
uint256(0), // filler for account proof
uint256(21), // total_debt
uint256(22), // total_idle
uint256(20), // totalSupply
uint256(38), // full_profit_unlock_date
uint256(39), // profit_unlocking_rate
uint256(40), // last_profit_update
uint256(keccak256(abi.encode(18, SCRVUSD))) // balanceOf(self)
];

This is problematic because:

  1. last_profit_update is an internal state variable of the SCRVUSD contract that represents when profits were last updated, not when the block was created.

  2. This value may not align with the actual block timestamp and could be manipulated independently of the blockchain's progression.

  3. The function is inconsistent with verifyScrvusdByBlockHash, which correctly uses the actual block timestamp.

Impact

The impact of this vulnerability is severe:

  1. Price Manipulation: Attackers could potentially manipulate the oracle's price calculations by exploiting the incorrect timestamp, leading to mispriced assets in the ecosystem.

  2. Financial Loss: Since oracles are critical infrastructure for DeFi protocols, any manipulation could result in significant financial losses for users and liquidity providers.

  3. Arbitrage Opportunities: The discrepancy between the actual block timestamp and the last_profit_update value could create arbitrage opportunities that drain protocol resources.

  4. Ecosystem Instability: Inaccurate price reporting could lead to cascading failures in interconnected DeFi protocols that rely on this oracle.

  5. Temporal Inconsistency: Using different timestamp sources for different verification methods creates an inconsistent state that could lead to unpredictable behavior.

Tools Used

  • Manual code review

  • Semantic understanding of the contract's intended behavior

  • Comparison with similar oracle implementations

Recommendations

  1. Use Actual Block Timestamp: Modify the verifyScrvusdByStateRoot function to fetch the actual block timestamp from the block hash oracle or another reliable source:

function verifyScrvusdByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (uint256) {
bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
uint256 block_timestamp = IBlockHashOracle(BLOCK_HASH_ORACLE).get_block_timestamp(_block_number);
uint256[PARAM_CNT] memory params = _extractParametersFromProof(state_root, _proof_rlp);
return _updatePrice(params, block_timestamp, _block_number);
}
  1. Extend Block Hash Oracle Interface: If the block hash oracle doesn't provide a method to retrieve block timestamps, extend its interface to include this functionality:

interface IBlockHashOracle {
function get_block_hash(uint256 _number) external view returns (bytes32);
function get_state_root(uint256 _number) external view returns (bytes32);
function get_block_timestamp(uint256 _number) external view returns (uint256);
}
  1. Implement Timestamp Verification: Add checks to ensure that timestamps used in price updates are within reasonable bounds and consistent with the blockchain's progression.

  2. Standardize Verification Methods: Ensure that all verification methods use the same source for timestamps to maintain consistency across the system.

  3. Add Circuit Breakers: Implement circuit breakers that can halt price updates if anomalies are detected in the timestamp data.

  4. Add Comprehensive Testing: Develop specific test cases that verify the correct handling of timestamps in all oracle verification methods.

By implementing these recommendations, the contract can mitigate the risk of timestamp manipulation and ensure the integrity of the price feed for the scrvUSD oracle.

Updates

Lead Judging Commences

0xnevi Lead Judge
3 months ago
0xnevi Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[invalid] finding-params-5-wrong-extract

See primary comments in issue [228](https://codehawks.cyfrin.io/c/2025-03-curve/judging/228)

Appeal created

kweks Submitter
3 months ago
0xnevi Lead Judge
3 months ago
0xnevi Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-last_profit_update-used-instead-timestamp

- Sponsor Comments - State root oracles usually do not provide block.timestamp, so it's simply not available. That is why last_profit_update is intended. - In `update_price`, this value must be a future block, meaning this update is a state checked and allowed by the OOS verifier contracts. The impact is also increasingly limited given price is smoothen and any updates via the block hash `verifyScrvusdByBlockHash` can also update the prices appropriately, meaning the price will likely stay within safe arbitrage range aligning with protocol logic

Support

FAQs

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