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

Potential Manipulation of Oracle Price through Invalid Blockhash Acceptance

Summary

The ScrvusdVerifierV1 and ScrvusdVerifierV2 contracts rely on a BlockHashOracle to verify Ethereum state proofs. However, as explicitly mentioned in the documentation, this oracle can "rarely provide an incorrect blockhash," creating a vulnerability where the system might accept manipulated state proofs, leading to incorrect price reporting for scrvUSD.

Vulnerability Details

The verifier contracts validate state proofs against blockhashes or state roots obtained from the BlockHashOracle:

// From ScrvusdVerifierV1.sol
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"
);
// ...
}

While the code validates that the provided blockhash matches what's reported by the oracle, it cannot detect if the oracle itself provides incorrect data. The README states:

It can rarely provide an incorrect blockhash, but not an incorrect block number.

This creates a trust assumption about the BlockHashOracle that, if violated, undermines the entire verification process. Since the blockhash/state root is the foundation for verifying all other data, an incorrect value here would allow validating incorrect scrvUSD parameters.

Impact

If the BlockHashOracle provides an incorrect blockhash:

  1. An attacker could craft state proofs against this incorrect state

  2. These proofs would pass verification

  3. Incorrect scrvUSD parameters would be set in the oracle

  4. This could lead to incorrect price reporting

While ScrvusdOracleV2 implements price smoothing that limits the rate of price changes:

def _smoothed_price(last_price: uint256, raw_price: uint256) -> uint256:
max_change: uint256 = (
self.max_price_increment * (block.timestamp - self.last_update) * last_price // 10**18
)
# ...

This only slows down the impact rather than preventing it. Over time, an attacker could manipulate the price enough to profit from arbitrage in stableswap pools using this oracle.

The impact is classified as Medium because:

  • Fund loss is possible but indirect

  • Existing safeguards limit the magnitude and speed of exploitation

  • The system allows for correction of bad blockhashes

Tools Used

Manual code review

Recommendations

  1. Implement Multi-Source Oracle Validation:

    function verifyScrvusdByBlockHash(
    bytes memory _block_header_rlp,
    bytes memory _proof_rlp
    ) external returns (uint256) {
    // ...
    require(
    block_header.hash == IBlockHashOracle(PRIMARY_ORACLE).get_block_hash(block_header.number) &&
    block_header.hash == IBlockHashOracle(SECONDARY_ORACLE).get_block_hash(block_header.number),
    "Blockhash mismatch"
    );
    // ...
    }
  2. Add Parameter Sanity Checks:

    function _updatePrice(
    uint256[PARAM_CNT] memory params,
    uint256 ts,
    uint256 number
    ) internal returns (uint256) {
    // Sanity check on parameters
    require(params[0] + params[1] > 0, "Invalid total assets");
    require(params[2] > 0, "Invalid total supply");
    // Calculate price change from previous values
    uint256 newPrice = calculatePrice(params);
    uint256 priceChange = calculatePriceChange(previousPrice, newPrice);
    require(priceChange <= MAX_ALLOWED_CHANGE, "Suspicious price change");
    return IScrvusdOracle(SCRVUSD_ORACLE).update_price(params, ts, number);
    }
  3. Implement a Timelock for Oracle Updates:
    Create a system where updates are proposed and only executed after a delay, allowing time for verification or challenges.

  4. Add More Restrictive Age Verification:

    require(block.timestamp - block_header.timestamp <= MAX_BLOCK_AGE, "Blockhash too old");
  5. Consider Medianizer Pattern:
    Implement a system that takes the median of multiple oracle updates rather than relying on a single source.

Updates

Lead Judging Commences

0xnevi Lead Judge
3 months ago
0xnevi Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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