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

Lack of Replay Protection in Verification Process

Summary

The ScrvusdVerifier contracts lack proper replay protection mechanisms, allowing potential attackers to resubmit previously used proofs to manipulate the oracle system, which could lead to price manipulation, denial of service, or other malicious outcomes.

Vulnerability Details

The verification functions in ScrvusdVerifierV1 and ScrvusdVerifierV2 do not implement any mechanism to prevent replay attacks:

function _updatePrice(
uint256[PARAM_CNT] memory params,
uint256 ts,
uint256 number
) internal returns (uint256) {
return IScrvusdOracle(SCRVUSD_ORACLE).update_price(params, ts, number);
}

The contract forwards all verification requests to the Oracle contract without:

  1. Tracking which proofs or block hashes have already been processed

  2. Implementing nonce-based protection

  3. Validating that newer information is being provided

  4. Checking if the same proof has been submitted multiple times

The only protection potentially provided would be in the oracle implementation itself, but this is not guaranteed by the verifier contract.

Similarly, in ScrvusdVerifierV2:

function verifyPeriodByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp
) external returns (bool) {
// ... validation code ...
uint256 period = _extractPeriodFromProof(block_header.stateRootHash, _proof_rlp);
return IScrvusdOracleV2(SCRVUSD_ORACLE).update_profit_max_unlock_time(period, block_header.number);
}

There is no mechanism to prevent a previously used block header and proof from being resubmitted.

Impact

The lack of replay protection could allow attackers to:

  1. Resubmit older, more favorable proofs to manipulate price data

  2. Force the system to revert to previous states by replaying old but valid proofs

  3. Create inconsistencies in oracle data by mixing current and past state information

  4. Execute denial-of-service attacks by continuously submitting the same valid proofs

  5. Potentially bypass time-sensitive restrictions by reusing proofs from specific time windows

The severity of this issue depends on how the Oracle contract handles updates, but it represents a fundamental security flaw in the verification process.

Recommendations

  1. Implement Block Number Tracking:

mapping(uint256 => bool) public processedBlocks;
function verifyScrvusdByBlockHash(bytes memory _block_header_rlp, bytes memory _proof_rlp) external returns (uint256) {
// ... existing validation code ...
require(!processedBlocks[block_header.number], "Block already processed");
processedBlocks[block_header.number] = true;
// ... remaining code ...
}
  1. Track Proof Hashes:

    • Store hashes of processed proofs to prevent reuse

    • Implement a time-based expiration for proof validity

  2. Enforce Monotonically Increasing Block Numbers:

    • Maintain a lastProcessedBlockNumber state variable

    • Require new submissions to use higher block numbers

  3. Nonce-Based Protection:

    • Implement a nonce system for proof submissions

    • Require each submission to use an incremented nonce

By implementing these recommendations, the contract would significantly reduce the risk of replay attacks and enhance the integrity of the oracle update process.

Updates

Lead Judging Commences

0xnevi Lead Judge 3 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.