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

Replay attack

Summary

There is no explicit mechanism to ensure that the same proof cannot be replayed or that proofs are processed in order. Contract ScrusdVerifierV1

Vulnerability Details

If an attacker is able to resubmit an old but valid proof, it might affect the pricing update

Impact

an attacker could repeatedly trigger oracle updates using the same valid proof. This may cause incorrect or manipulated pricing data to be submitted to the oracle, potentially disrupting dependent systems and harming the protocol's integrity.

Tools Used

Manual

Recommendations

Replay Protection: Use a mapping to store processed block numbers and ensure each proof is processed only once.

// Replay protection: Track processed block numbers to prevent replayed proofs.
mapping(uint256 => bool) private processedBlockNumbers;
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"
);
// Replay protection: prevent using a proof for a block number that was already processed.
require(!processedBlockNumbers[block_header.number], "Proof already processed");
processedBlockNumbers[block_header.number] = true;
uint256[PARAM_CNT] memory params = _extractParametersFromProof(block_header.stateRootHash, _proof_rlp);
return _updatePrice(params, block_header.timestamp, block_header.number);
}
function verifyScrvusdByStateRoot(
uint256 _block_number,
bytes memory _proof_rlp
) external returns (uint256) {
// Replay protection: prevent reusing proofs for the same block.
require(!processedBlockNumbers[_block_number], "Proof already processed");
processedBlockNumbers[_block_number] = true;
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);
}
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.