Closed c4-bot-7 closed 8 months ago
Picodes marked the issue as primary issue
othernet-global (sponsor) disputed
The calculations work as expected.
Note the line:
if ( ! weth_usdcFlipped ) uniswapWETH_USDC = 10**36 / uniswapWETH_USDC;
...which essentially results in us using USDS/WETH as the value.
So then the final calculation: ( uniswapWETH_USDC * 10**18) / uniswapWBTC_WETH;
is really ( USDC/WETH) / (WBTC/WETH).
Note that the foundry test is incorrect because the forced values need to be flipped if the pairs are flipped. uint256 forcedTWAP_WBTC_WETH = 2 ether; // BTC should be worth 2 ETH uint256 forcedTWAP_WETH_USDC = 3 ether;
Picodes marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/price_feed/CoreUniswapFeed.sol#L112
Vulnerability details
Impact
The internal oracle price system uses three different price feeds and aggregates them into one by taking the average price of the two closest prices.
The three different price feeds are chainlink, uniswap TWAP, and the internal balances and these are used to calculate the price of ETH and BTC in USD. Inside the uniswap TWAP calculation to get the BTC price a wrong calculation leads to a very low BTC price. Here we can see this function:
As we can see it uses two TWAP prices from uniswap. The WETH / USDC and the WBTC / WETH pool. The reason for that is that the TVL in these pools is bigger than the TVL in the WBTC / USDC pool. The last calculation is the problem. It divides the WETH in USDC price by the WBTC in WETH price while it should be multiplied instead.
Here we can see a calculation that showcases the problem:
Therefore manipulating the oracle price becomes way easier as the price aggregator will always use the average of the chainlink price and the internal balance because the uniswap TWAP price of BTC will always be very off. Therefore the attacker just needs to manipulate the reserves of the salty pool to manipulate the whole oracle price and this should be possible with a flash loan attack.
Also, the price aggregator reverts if two different price feeds return 0, but with this bug, it will revert if only one of the price feeds returns 0 for BTC as it will take the average of the very small BTC price and the 0 price instead of the two not 0 prices. This will always revert because of this check
(_absoluteDifference(priceA, priceB) * 100000) / averagePrice > maximumPriceFeedPercentDifferenceTimes1000 ).
Therefore if any price feed returns 0 a DoS of the BTC oracle system occurs.Proof of Concept
The following POC can be implemented in the
CoreUniswapFeed.t.sol
test file:Tools Used
Manual Review
Recommended Mitigation Steps
Change the last calculation inside the
getTwapWBTC
function to multiply instead of divide.Assessed type
Math