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

Cross-Chain Proof Replay Vulnerability

Summary

The Curve Storage Proofs protocol contains a critical vulnerability where proofs submitted to one chain can be successfully replayed on another chain. The verification process lacks chain-specific binding or cross-chain usage tracking, allowing attackers to reuse proofs across multiple deployments. This contradicts the basic security principle of proof uniqueness across networks and creates significant economic attack vectors when market conditions diverge across chains.

Vulnerability Details

The root of this vulnerability lies in three interrelated factors:

  1. Absence of Chain-Specific Binding: The verification functions in ScrvusdVerifierV1.sol and ScrvusdVerifierV2.sol don't incorporate the current chain's identifier in the verification process:

// ScrvusdVerifierV1.sol:52-59
function verifyScrvusdByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp
) external returns (uint256) {
// No chain ID check or binding
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"
);
// Continue with verification...
}
  1. Independent Per-Chain State Tracking: Each chain's oracle independently tracks the last processed block number without cross-chain coordination:

# ScrvusdOracleV2.vy:289-290
assert self.last_block_number <= _block_number, "Outdated"
self.last_block_number = _block_number
  1. Multi-Chain Deployment with Identical Interfaces: The protocol is explicitly designed for cross-chain deployment, as evidenced by the keeper script:

# scripts/scrvusd/scrvusd_keeper.py:22-36
B_ORACLE, S_ORACLE, PROVER = {
"optimism": (...),
"base": (...),
"fraxtal": (...),
"mantle": (...),
# Other chains...
}[CHAIN]

This creates a scenario where an attacker can:

  1. Wait for a legitimate proof to be submitted on Chain A

  2. Capture the same proof parameters (block header and proof RLP)

  3. Submit identical parameters to the verifier on Chain B

  4. Successfully update Chain B's oracle with parameters intended for Chain A

Root Cause:
The fundamental design flaw is the lack of destination chain binding in the proof verification process. Proofs are authenticated against their source (via blockhash verification) but not against their intended destination.

Exploitation Conditions:

  • Cross-chain deployment of the protocol

  • Access to submitted proof data (which is publicly visible on-chain)

  • Ability to send transactions to multiple chains

Impact

Economic Impact:
This vulnerability enables several attack vectors:

  1. Cross-Chain Arbitrage: When market conditions diverge between chains, an attacker can selectively replay proofs that create profitable price discrepancies.

  2. Denial of Service: Replaying outdated proofs from one chain to another could prevent legitimate updates, especially if block numbers are significantly behind on the target chain.

  3. Price Manipulation: By replaying proofs from a chain with favorable parameters to chains with different market conditions, an attacker could manipulate prices for profit.

In a real-world scenario with $10M in cross-chain liquidity:

  • A 0.5% price divergence between Ethereum and destination chains creates ~$50,000 arbitrage opportunities

  • Such opportunities could be repeatedly exploited whenever new proofs are submitted

  • Annual impact could exceed $500,000 in extracted value

Technical Impact:

  • Breaks the assumption that each chain's oracle reflects its own legitimate update history

  • Creates inconsistent state across the protocol's cross-chain deployments

  • Undermines the core security model of cross-chain state verification

User Impact:

  • Traders on affected chains experience manipulated prices

  • Liquidity providers suffer from systematic value extraction

  • Protocol users face uncertainty about the reliability of cross-chain oracle data

This vulnerability is classified as HIGH severity because:

  1. It enables unauthorized state changes across chains

  2. It creates direct economic loss potential

  3. It fundamentally breaks the security model of cross-chain proof verification

  4. It requires minimal resources to exploit with significant profit potential

Tools Used

  • Manual code review of verification logic

  • Cross-chain deployment analysis

  • Simulated proof replay across multiple networks

  • Economic impact modeling of cross-chain arbitrage scenarios

  • Cross-reference analysis of transaction patterns across deployments

Recommendations

Immediate Mitigations:

  1. Add chain ID binding to all proof verification functions:

// Add to both verifier contracts
function verifyScrvusdByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp,
uint256 _destinationChainId // Add destination parameter
) external returns (uint256) {
require(_destinationChainId == block.chainid, "Invalid destination chain");
// Rest of the function remains unchanged
}
  1. Implement proof uniqueness tracking through a nonce or hash-based mechanism:

// Add to verifier contracts
mapping(bytes32 => bool) private usedProofs;
function verifyScrvusdByBlockHash(
bytes memory _block_header_rlp,
bytes memory _proof_rlp
) external returns (uint256) {
// Calculate unique proof hash including chain ID
bytes32 proofHash = keccak256(abi.encodePacked(block.chainid, _block_header_rlp, _proof_rlp));
require(!usedProofs[proofHash], "Proof already used");
usedProofs[proofHash] = true;
// Rest of the function
}

Long-term Fixes:

  1. Implement a cross-chain proof registry that tracks which proofs have been used on which chains.

  2. Add explicit destination chain parameters to the proof generation process.

  3. Consider implementing a more robust cross-chain communication protocol that inherently prevents replay attacks.

  4. Design a proof format that includes the destination chain ID as part of the Merkle proof construction.

By implementing these measures, the protocol can ensure that proofs are only valid for their intended destination chain, preventing cross-chain replay attacks and maintaining the integrity of the oracle system across multiple networks.

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.