Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clever Mocha Pheasant - Unprotected Price Oracle Reading Enables Single-Block Price Manipulation #61

Open
sherlock-admin2 opened this issue Dec 10, 2024 · 0 comments

Comments

@sherlock-admin2
Copy link

Clever Mocha Pheasant

Medium

Unprotected Price Oracle Reading Enables Single-Block Price Manipulation

Summary

The UniswapPricingLibrary contains a critical vulnerability in its price fetching mechanism where it allows direct access to the current pool price through slot0 when twapInterval is set to zero. The core issue lies in the getSqrtTwapX96 function which bypasses Uniswap V3's time-weighted average price (TWAP) protection when twapInterval equals zero, making it susceptible to flash loan attacks and price manipulation within a single block.

This vulnerability stems from the assumption that instantaneous price readings from slot0 are acceptable alternatives to TWAP. However, in the context of Uniswap V3's architecture, slot0 prices can be manipulated through large swaps within a single transaction, especially using flash loans. The impact is severe because any protocol relying on this library for price feeds could be exploited through price manipulation, potentially leading to significant financial losses through oracle attacks on lending, derivatives, or other DeFi protocols.

The vulnerability manifests in the following code path:

https://github.com/sherlock-audit/2024-11-teller-finance-update/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/libraries/UniswapPricingLibrary.sol#L105

function getSqrtTwapX96(address uniswapV3Pool, uint32 twapInterval)
    internal
    view
    returns (uint160 sqrtPriceX96)
{
    if (twapInterval == 0) {
        (sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapV3Pool).slot0();
    }
    // ...
}

An attacker can exploit this by executing the following attack flow in a single transaction:

  1. Obtain a flash loan
  2. Execute a large swap to manipulate the pool's current price
  3. Take advantage of protocols using the vulnerable price reading
  4. Profit from the manipulation
  5. Repay the flash loan

Recommended mitigation steps

The vulnerability should be addressed through a comprehensive overhaul of the price reading mechanism. Instead of allowing direct slot0 access, the library should implement a robust oracle system that ensures price reliability.

The fix involves modifying the getSqrtTwapX96 function to enforce TWAP usage in all scenarios. This can be achieved by removing the twapInterval=0 case entirely and implementing a minimum TWAP interval requirement:

function getSqrtTwapX96(address uniswapV3Pool, uint32 twapInterval)
    internal
    view
    returns (uint160 sqrtPriceX96)
{
    // Enforce minimum TWAP interval
    uint32 effectiveInterval = Math.max(twapInterval, MIN_TWAP_PERIOD);
    
    uint32[] memory secondsAgos = new uint32[](2);
    secondsAgos[0] = effectiveInterval + 1;
    secondsAgos[1] = 1;
    
    (int56[] memory tickCumulatives, ) = IUniswapV3Pool(uniswapV3Pool)
        .observe(secondsAgos);
        
    // Calculate TWAP price
    sqrtPriceX96 = TickMath.getSqrtRatioAtTick(
        int24((tickCumulatives[1] - tickCumulatives[0]) / 
        int32(effectiveInterval))
    );
}

For cases requiring more recent price data, implement a separate function that combines TWAP with additional safety checks:

function getRecentPrice(address uniswapV3Pool)
    internal
    view
    returns (uint160 sqrtPriceX96)
{
    // Get current price
    (uint160 currentPrice, , , , , , ) = IUniswapV3Pool(uniswapV3Pool).slot0();
    
    // Get short TWAP for comparison
    uint160 twapPrice = getSqrtTwapX96(uniswapV3Pool, SHORT_TWAP_PERIOD);
    
    // Ensure current price hasn't deviated significantly from TWAP
    require(
        calculateDeviation(currentPrice, twapPrice) <= MAX_DEVIATION,
        "Price deviation too high"
    );
    
    return currentPrice;
}

This solution provides strong protection against price manipulation while maintaining the ability to access recent price data when needed. The implementation should be accompanied by thorough testing across various market conditions and proper documentation of the security assumptions and limitations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant