The ScrvusdVerifierV1
contract relies on state proofs to verify and update the price of scrvUSD
. If the state proof verification process is not robust, an attacker could submit a malicious state proof to manipulate the scrvUSD
price or the profit_max_unlock_time
. Specifically:
Malicious State Proof: An attacker could craft a fake state proof that contains incorrect data (e.g., inflated or deflated prices, incorrect profit_max_unlock_time
).
Lack of Robust Verification: If the contract does not thoroughly validate the state proof, the malicious data could be accepted, leading to incorrect updates.
The root cause of the bug lies in the lack of validation of the state proof’s authenticity and integrity. Specifically:
No Verification of Proof Source:
The contract does not verify whether the state proof (proofRlp
) comes from a trusted or valid source.
An attacker could craft a malicious state proof that contains fake data (e.g., manipulated total_debt
, total_idle
, or profit_max_unlock_time
).
No Validation of Extracted Data:
The contract extracts slot values from the state proof but does not perform sanity checks on the extracted data.
For example, it does not ensure that the extracted values (e.g., total_debt
, total_idle
) fall within reasonable ranges or are consistent with historical data.
Reliance on Untrusted Input:
The proofRlp
parameter is provided by the caller, and the contract blindly trusts it without verifying its authenticity.
This makes the contract vulnerable to malicious inputs.
Bob is a malicious actor who wants to manipulate the scrvUSD
price for personal gain.
Alice is a liquidity provider who relies on the oracle for accurate price data.
The ScrvusdVerifierV1
contract is used to verify and update the scrvUSD
price based on state proofs.
Bob creates a fake state proof that contains manipulated data:
He sets total_debt
to an artificially high value (e.g., 1e30
).
He sets total_idle
to an artificially low value (e.g., 0
).
He sets profit_max_unlock_time
to an incorrect value (e.g., 0
to disable profit unlocking).
Bob encodes this malicious data into an RLP-encoded state proof (proofRlp
).
Bob calls the verifyScrvusdByBlockHash
or verifyScrvusdByStateRoot
function, passing the malicious state proof as an argument.
The contract does not verify the authenticity or integrity of the state proof, so it accepts the malicious data.
The _extractParametersFromProof
function extracts the manipulated parameters from the malicious state proof:
total_debt
is set to 1e30
.
total_idle
is set to 0
.
profit_max_unlock_time
is set to 0
.
The function does not perform any sanity checks on the extracted data, so the malicious values are accepted.
The _updatePrice
function updates the scrvUSD
price using the manipulated data:
The scrvUSD
price is artificially inflated due to the manipulated total_debt
and total_idle
values.
The profit_max_unlock_time
is set to 0
, disrupting the profit unlocking mechanism.
Bob uses the manipulated price to his advantage:
He sells his scrvUSD
holdings at the inflated price, making a profit.
He exploits the disrupted profit unlocking mechanism to withdraw funds prematurely or delay profit distribution.
Bob’s actions are at the expense of other users, including Alice.
Alice, as a liquidity provider, relies on the oracle for accurate price data.
Due to the manipulated price, Alice’s holdings are incorrectly valued:
If the price is inflated, Alice may overvalue her holdings and make poor financial decisions.
If the price is deflated, Alice may undervalue her holdings and suffer losses when trading or withdrawing funds.
The disrupted profit unlocking mechanism further exacerbates Alice’s losses, as she may not receive her expected profits on time.
Manipulate the price of scrvUSD
to drain funds from the protocol.
Set an incorrect profit_max_unlock_time
, disrupting the profit unlocking mechanism.
Manual Review of the code
Ensure that the state proof comes from a trusted source (e.g., a trusted oracle or consensus mechanism).
Cross-reference the state proof with multiple block headers or state roots to verify its authenticity
Implement sanity checks on the extracted parameters to ensure they fall within reasonable ranges. For example, check that total_debt
and total_idle
are not excessively high or low.
- See [here]([https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle)](https://github.com/CodeHawks-Contests/2025-03-curve?tab=readme-ov-file#blockhash-oracle) on how it is used to verify storage variable - All state roots and proofs must be verified by the OOS `StateProofVerifier` inherited as `Verifier` (where the price values and params are extracted), so there is no proof that manipulating timestamp/inputs can affect a price update - It is assumed that the OOS prover will provide accurate data and the OOS verifier will verify the prices/max unlock time to be within an appropriate bound/values - There is a account existance check in L96 of `ScrvusdVerifierV1.sol`, in which the params for price updates are extracted from
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.