The ScrvusdVerifierV1 contract has a vulnerability related to insufficient validation of storage slot values extracted from state proofs. The contract forwards these unvalidated values to the price oracle without ensuring they exist or have reasonable values, which could lead to incorrect price calculations affecting cross-chain trading and potentially causing financial losses.
The ScrvusdVerifierV1 contract extracts storage slot values from Ethereum state proofs to obtain scrvUSD vault parameters. While it does verify the account proof and checks that the blockhash is valid, it does not validate that the extracted storage slots exist or that their values are reasonable before passing them to the price oracle.
The comment "Slots might not exist, but typically we just read them" acknowledges this behavior, but the code never checks slot.exists
before using slot.value
. If a slot doesn't exist, its value will be zero, which could lead to critical issues in price calculations.
The missing validation of storage slot existence creates a direct path to price manipulation in the cross-chain oracle system. When invalid parameters are accepted, the oracle's price calculation function _raw_price
computes prices using potentially manipulated values. Since the calculation divides total assets by total supply (self._total_assets(parameters) * 10**18 // self._total_supply(parameters, ts)
), a zero or abnormally low value for total supply would either cause the transaction to revert or result in an artificially inflated price.
This manipulation affects the entire cross-chain ecosystem relying on this oracle. StableSwap-NG pools using the oracle for scrvUSD would price trades based on incorrect values, creating immediate arbitrage opportunities. An attacker could execute trades on chains with manipulated prices against chains with accurate prices, extracting value at the expense of liquidity providers. The severity increases with the liquidity depth of affected pools, as deeper pools allow for larger exploitative trades.
Additionally, since the oracle employs a smoothing mechanism to limit price movements, the effects of manipulation could persist for extended periods. Even after correct parameters are submitted, the price would only gradually converge to the accurate value, extending the window of exploitation. This is particularly concerning as the system is specifically designed to maintain consistent pricing across different blockchain networks, and this vulnerability undermines its core security assumption.
The vulnerability can be exploited when a proof with missing or invalid storage slots is processed. When the verifyScrvusdByBlockHash
or verifyScrvusdByStateRoot
function is called with a valid block header but crafted proof data, the contract extracts parameters without validation:
First, the contract obtains a valid block hash and state root:
Then it extracts parameters from the proof without validating their existence:
Within _extractParametersFromProof
, each slot value is read without checking if it exists:
These unvalidated parameters are then passed to the oracle:
The oracle calculates price using these parameters, where division by zero could occur:
A malicious actor with the PRICE_PARAMETERS_VERIFIER
role could specifically craft proofs where critical slots like total_supply
have zero or manipulated values, leading to incorrect prices being propagated across chains.
Validate Slot Existence:
Validate Critical Parameters:
- Looking at the OOS `StateProofVerifier` and `MerklePatriciaProofVerifier` contract that extracts the slot, the `exists` flag will be flagged as true as long as a non-zero length value is returned as seen [here](https://github.com/curvefi/curve-xdao/blob/3ff77bd2ccc9c88d50ee42d2a746fc7648c7ff2c/contracts/libs/StateProofVerifier.sol#L133C13-L136). From the `MerklePatriciaProofVerifier.extractProofValue`, the minimum length returned will be 1 as represenetd by `bytes(0)`. So this seems to be purely a sanity check that might not even be required. - A slot with zero values is only allowed when the proof provided by the prover correctly proofs that such values are included within the Merkle-Patricia-Tree. The values fetched from mainnet from the V3Vault stored in the merkle trie is likely checked before hand and aggregated into the MerkleTree.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.