Oracle Manipulation
Overview
- Severity: High
- Confidence: Medium
- Affected Versions: All
What is Oracle Manipulation?
Oracle manipulation occurs when a smart contract relies on price feeds or other external data without proper validation mechanisms. This vulnerability can allow attackers to manipulate contract behavior through price manipulation, stale data usage, or flash loan attacks on price sources.
Why is Oracle Manipulation Dangerous?
Oracle manipulation vulnerabilities can be extremely dangerous because:
- They can lead to significant financial losses through price manipulation
- Stale data can cause contracts to operate with outdated information
- Flash loan attacks can temporarily manipulate prices to exploit contracts
- Unprotected oracle updates can allow direct price manipulation
- Lack of access controls can let unauthorized parties modify critical data
Technical Example of Vulnerable Code
pragma solidity ^0.8.0;
contract VulnerableOracleConsumer {
AggregatorV3Interface public priceFeed;
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
}
// VULNERABLE: No staleness check, no price validation
function getPrice() external view returns (int) {
(, int price,,,) = priceFeed.latestRoundData();
return price;
}
// VULNERABLE: No access control, no validation
function setPriceFeed(address _newPriceFeed) external {
priceFeed = AggregatorV3Interface(_newPriceFeed);
}
// VULNERABLE: Direct price usage without checks
function calculateValue(uint256 amount) external view returns (uint256) {
(, int price,,,) = priceFeed.latestRoundData();
return amount * uint256(price);
}
}
In this example, the contract uses oracle data without any validation. It doesn't check for stale data, validate price values, or protect against manipulation. The price feed can also be changed by any address.
Technical Example of Fixed Code
pragma solidity ^0.8.0;
contract SecureOracleConsumer {
AggregatorV3Interface public priceFeed;
uint256 public constant STALENESS_PERIOD = 1 hours;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
owner = msg.sender;
}
function getValidatedPrice() public view returns (int) {
(
uint80 roundId,
int price,
,
uint256 updatedAt,
uint80 answeredInRound
) = priceFeed.latestRoundData();
// Freshness check
require(block.timestamp - updatedAt <= STALENESS_PERIOD, "Stale price");
// Round validation
require(answeredInRound >= roundId, "Price rounds mismatch");
// Sanity check
require(price > 0, "Invalid price");
return price;
}
function setPriceFeed(address _newPriceFeed) external onlyOwner {
require(_newPriceFeed != address(0), "Invalid address");
priceFeed = AggregatorV3Interface(_newPriceFeed);
}
function calculateValue(uint256 amount) external view returns (uint256) {
int validatedPrice = getValidatedPrice();
return amount * uint256(validatedPrice);
}
}
The improved version includes comprehensive oracle validation:
- Staleness checks to prevent usage of outdated data
- Round number validation to ensure price consistency
- Sanity checks on price values
- Access controls for oracle updates
- Input validation for new price feed addresses