DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Valid

Incorrect Price Validation Struct References

Summary

Critical vulnerability in KeeperProxy._validatePrice() where incorrect price struct fields are used to validate long/short token prices, allowing price manipulation attacks and potential loss of user funds.

Vulnerability Details

function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
// ...existing code...
MarketProps memory marketData = reader.getMarket(market);
// Incorrect price validation
_check(marketData.indexToken, prices.indexTokenPrice.min);
_check(marketData.indexToken, prices.indexTokenPrice.max);
_check(marketData.longToken, prices.indexTokenPrice.min); // VULNERABILITY: Using index price for long token
_check(marketData.longToken, prices.indexTokenPrice.max); // VULNERABILITY: Using index price for long token
_check(marketData.shortToken, prices.shortTokenPrice.min);
_check(marketData.shortToken, prices.shortTokenPrice.max);
}

Attack Scenario

  1. Attacker manipulates index token price on DEX

  2. Submits transaction with:

    • Manipulated indexTokenPrice

    • Actual longTokenPrice/shortTokenPrice

  3. Price validation passes because long token is checked against index price

  4. Malicious position can be executed at incorrect prices

Impact

Severity: HIGH

Reasons:

  • Direct financial loss possible

  • No existing mitigation

  • Easy to exploit

  • Affects core price validation

Potential losses:

  • Malicious liquidations

  • Below-market trades

  • Price manipulation attacks

Tools Used

  • Manual code review

  • Solidity static analysis

Recommendations

  1. Fix price validation mapping:

function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
// ...existing sequencer checks...
address market = IPerpetualVault(perpVault).market();
IVaultReader reader = IPerpetualVault(perpVault).vaultReader();
MarketProps memory marketData = reader.getMarket(market);
// Correct price validation mapping
_check(marketData.indexToken, prices.indexTokenPrice.min);
_check(marketData.indexToken, prices.indexTokenPrice.max);
_check(marketData.longToken, prices.longTokenPrice.min); // FIXED: Using correct long token price
_check(marketData.longToken, prices.longTokenPrice.max); // FIXED: Using correct long token price
_check(marketData.shortToken, prices.shortTokenPrice.min);
_check(marketData.shortToken, prices.shortTokenPrice.max);
}
  1. Add additional validations:

function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
require(
prices.longTokenPrice.min <= prices.longTokenPrice.max &&
prices.shortTokenPrice.min <= prices.shortTokenPrice.max &&
prices.indexTokenPrice.min <= prices.indexTokenPrice.max,
"Invalid price ranges"
);
// ...existing validation logic...
}
  1. Add events for monitoring:

event PriceValidation(
address indexed token,
uint256 providedPrice,
uint256 chainlinkPrice,
uint256 threshold
);

Test Cases

function testPriceValidation() public {
// Setup market prices
MarketPrices memory prices = MarketPrices({
indexTokenPrice: PriceProps(1000e8, 1010e8),
longTokenPrice: PriceProps(50e8, 51e8),
shortTokenPrice: PriceProps(1e8, 1.01e8)
});
// Should revert with incorrect price struct usage
vm.expectRevert("price offset too big");
keeperProxy.run(vault, true, true, prices, new bytes[](0));
}
Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_validatePrice_no_check_for_longTokenPrice

Likelihood: None/Very Low, everytime the keeper send a price via run/runNextAction (sent by the Gamma keeper). Impact: Medium/High, does not check the longTokenPrice, it could go out of range. Keep in mind indexToken == longToken, an error from the keeper could be considered informational.

Support

FAQs

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