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

Lack of Verification of Storage Slot Proofs

Summary

In _extractParametersFromProof(), the contract does not properly verify that the extracted slot values correspond to the expected storage slots in scrvUSD. The function assumes that PARAM_SLOTS[i] matches the proof order, but a malicious attacker could reorder the proof elements, leading to incorrect parameter extraction.

Exploitable Attack Scenario

  1. The attacker crafts a malicious proof with manipulated slot order.

  2. _extractParametersFromProof() iterates over proofs[i].toList() without checking if PARAM_SLOTS[i] actually corresponds to the extracted slot.

  3. The attacker could inject arbitrary values into sensitive parameters like total_debt, balanceOf(self), or profit_unlocking_rate.

  4. The manipulated values are then sent to IScrvusdOracle.update_price(), leading to incorrect price updates and potential financial loss.

** **

Vulnerability Details

https://github.com/CodeHawks-Contests/2025-03-curve/blob/main/contracts/scrvusd/verifiers/ScrvusdVerifierV1.sol#L83

Impact

Tools Used

Recommendations

Enforce Slot Verification

Modify _extractParametersFromProof() to explicitly check the extracted slot key before assigning the value:

function _extractParametersFromProof( bytes32 stateRoot, bytes memory proofRlp ) internal view returns (uint256[PARAM_CNT] memory)

{

RLPReader.RLPItem[] memory proofs = proofRlp.toRlpItem().toList();

require(proofs.length == PROOF_CNT, "Invalid number of proofs");

// Extract account proof Verifier.Account memory account = Verifier.extractAccountFromProof( SCRVUSD_HASH, stateRoot, proofs[0].toList() );

require(account.exists, "scrvUSD account does not exist");

// Extract slot values securely uint256[PARAM_CNT] memory params;

for (uint256 i = 1; i < PROOF_CNT; i++) { bytes32 expectedSlotKey = keccak256(abi.encode(PARAM_SLOTS[i]));

// Correct slot key Verifier.SlotValue memory slot = Verifier.extractSlotValueFromProof( expectedSlotKey, account.storageRoot, proofs[i].toList() ); // **CRITICAL FIX**: Ensure slot.key matches expected storage slot require(slot.key == expectedSlotKey, "Storage slot mismatch"); params[i - 1] = slot.value;

} return params;

}

Updates

Lead Judging Commences

0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope
Assigned finding tags:

[invalid] finding-missing-proof-content-validation

- 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

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.