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

Bypassing the role ScrvusdOracleV2.vy::PRICE_PARAMETERS_VERIFIER

Summary

Verifier contract can bypass the role PRICE_PARAMETERS_VERIFIER (in the ScrvusdOracleV2.vy)

The code snippet in scrvUSD oracle (Vyper contract) shows:

@external
def update_price(...):
access_control._check_role(PRICE_PARAMETERS_VERIFIER, msg.sender)
...

Meaning that only addresses holding PRICE_PARAMETERS_VERIFIER can call update_price() directly.

Bear with me for sec....

Curve Docs say that

Also, it is worth noting that the oracle is controlled by a DAO and its parameters can be changed by a vote.

Typically, an admin (the DAO or a privileged address) would do something like:

# Granting the verifier contract the role
scrvUSDOracle.grantRole(PRICE_PARAMETERS_VERIFIER, verifierContractAddress)

The verifier contract itself is granted the PRICE_PARAMETERS_VERIFIER role, and that contract is open to calls from any address. Thus, the user doesn’t need the role; they simply trigger the verifier contract, which gets the role.

  • Once this grant happens, the verifier contract is recognized by the oracle as a valid caller.

  • Public Functions in the Verifier

    • ScrvusdVerifierV1 and ScrvusdVerifierV2 each expose external functions like verifyScrvusdByBlockHash() and verifyScrvusdByStateRoot without access control (and they call the update function... see below).

    • So any user can call verifierContract.verifyScrvusdByBlockHash(...).

  • Verifier Calls the Oracle

    • Inside verifyScrvusdByBlockHash(), the verifier contract eventually calls:

      IScrvusdOracle(SCRVUSD_ORACLE).update_price(params, ts, number);

Because the verifier contract holds PRICE_PARAMETERS_VERIFIER, the oracle checks msg.sender (the verifier contract) and sees a valid role holder.

It then updates the price !.

Vulnerability Details

The verifier contract is open to any external caller, then effectively anyone can indirectly trigger update_price()

POC:

  • DAO → Grants verifierContract the role PRICE_PARAMETERS_VERIFIER.

  • Malicious User → Calls verifierContract.verifyScrvusdByBlockHash(...) with a crafted _block_header_rlp and _proof_rlp (provides _block_header_rlp and _proof_rlp that references an older or partially manipulated state for scrvUSD... this is known issue in audit community)

  • Verifier Contract → Accepts the call (no role check) and calls the oracle’s update_price().

  • Oracle → Sees msg.sender == verifierContract, which holds the role, and updates the price.

Impact

Manipulation or triggers the price update at will, possibly with stale or incorrect data.

This leads to arbitrage or losses in any stableswap pool relying on that updated price.

Tools Used

Manual

Recommendations

The verifier contracts also need role checks (or access control) to ensure only a trusted Prover address can call them.

Updates

Lead Judging Commences

0xnevi Lead Judge
5 months ago
0xnevi Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[invalid] finding-centralization-risk

- Per [codehawks documentation](https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity#findings-that-may-be-invalid) - Parameter change is executed via the Dao per docs > Also, it is worth noting that the oracle is controlled by a DAO and its parameters can be changed by a vote.

Support

FAQs

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