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

ScrvusdVerifierV2.sol: Unimplemented enough checks leading to Gas Waste or DoS attack

Summary

The function verifyPeriodByStateRoot verifies an Ethereum state proof proof_rlp against a known state_root obtained from a block oracle. It extracts a period value from storage via _extractPeriodFromProof and updates an oracle contract with the extracted without any implemented valiadation to proof_rlp

Vulnerability Details

function verifyPeriodByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (bool) {
bytes32 state_root = IBlockHashOracle(ScrvusdVerifierV1.BLOCK_HASH_ORACLE).get_state_root(_block_number);
uint256 period = _extractPeriodFromProof(state_root, _proof_rlp);
return IScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period, _block_number);
}

Impact

Without any validation checks the function is vulnerable to:

  • Revert on Malformed RLP Proofs – If _proof_rlp is invalid, toRlpItem().toList() would revert, wasting gas and causing unexpected failures.

  • Oracle Update with Invalid Data – Without checking period > 0, the contract could push incorrect values to the oracle, affecting downstream logic.

  • Invalid state_root Usage – If state_root == bytes32(0), the proof extraction would fail unpredictably, leading to unnecessary gas consumption.

  • Gas-Wasting Attacks – Attackers could spam invalid proofs, forcing expensive revert operations and potentially disrupting oracle updates and also causing DoS attack.

Tools Used

Recommendations

Implement following checks:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
// Custom errors for better gas efficiency and debugging
error InvalidProof();
error InvalidStateRoot();
error InvalidPeriod();
function verifyPeriodByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (bool) {
if (_proof_rlp.length == 0) revert InvalidProof();
// Ensure the proof can be safely decoded
RLPReader.RLPItem memory rlpItem = _proof_rlp.toRlpItem();
if (!rlpItem.isList()) revert InvalidProof();
// Extract state root from block oracle
bytes32 state_root = IBlockHashOracle(BLOCK_HASH_ORACLE).get_state_root(_block_number);
if (state_root == bytes32(0)) revert InvalidStateRoot();
// Extract the period from the proof
uint256 period = _extractPeriodFromProof(state_root, _proof_rlp);
if (period == 0) revert InvalidPeriod();
// Update oracle with extracted period
return IScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period, _block_number);
}
Updates

Lead Judging Commences

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

[invalid] finding-replay-proof-lack-nonce

- All proof generated within `_proof_rlp` is generated via the off-chain prover, so there is no concrete proof that this proofs are non-unique. - All state roots and proofs must be verified by the OOS `StateProofVerifier` inherited as `Verifier`, so there is no proof that manipulating proofs can successfully pass a price update

Support

FAQs

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