Summary
The critical point is when the contract calls back to the attacker "Before state updates" - this is where the vulnerability occurs, as the attacker can reenter the contract before the state changes are committed.
Vulnerability Details
The vulnerability lies in the update_price
function where state updates occur after external calls.
@external
def update_price(
_parameters: uint256[ALL_PARAM_CNT],
_ts: uint256,
_block_number: uint256
) -> uint256:
self.price_params = PriceParams(...)
self.last_block_number = _block_number
self.last_update = block.timestamp
emit PriceUpdate(new_price, _ts, _block_number)
return price_change
Impact
Can lead to minipulation of critical price parameters.
Allows atackers to influence oracle price calculations.
Potentially impacts user funds accross the ecosystem.
Tools Used
Manual review
Recommendations
Security enhancements and State protection
@view
def _check_price_validity(new_price: uint256, current_price: uint256) -> bool:
max_change: uint256 = self.max_price_increment * (block.timestamp - self.last_update) * current_price // 10**18
return abs(new_price - current_price) <= max_change
@external
def update_price(
_parameters: uint256[ALL_PARAM_CNT],
_ts: uint256,
_block_number: uint256
) -> uint256:
require(self.last_block_number <= _block_number, "Outdated")
require(_check_price_validity(new_price, current_price), "Invalid price change")
@external
def update_profit_max_unlock_time(
_profit_max_unlock_time: uint256,
_block_number: uint256
) -> bool:
require(self.last_block_number <= _block_number, "Outdated")
require(_profit_max_unlock_time >= 7 * 86400, "Unlock time too short")