Open c4-bot-4 opened 7 months ago
Picodes marked the issue as primary issue
othernet-global (sponsor) acknowledged
Note: the overcollateralized stablecoin mechanism has been removed from the DEX.
https://github.com/othernet-global/salty-io/commit/f3ff64a21449feb60a60c0d60721cfe2c24151c1
Picodes marked the issue as satisfactory
Picodes marked the issue as selected for report
The stablecoin framework: /stablecoin, /price_feed, WBTC/WETH collateral, PriceAggregator, price feeds and USDS have been removed:
https://github.com/othernet-global/salty-io/commit/88b7fd1f3f5e037a155424a85275efd79f3e9bf9
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/price_feed/CoreSaltyFeed.sol#L32-L53
Vulnerability details
Summary
When the price moves, Chainlink instantly reports the spot price, while the TWAP slowly changes the price. The spot price of CoreSaltyFeed can be manipulated, allowing an attacker to move the price in a desired direction.
Vulnerability Details
The spot price of
CoreSaltyFeed
can be manipulated, even when considering automatic arbitrage. The cost of moving the price depends on the liquidity of the pools. While the protocol is small, it will be cheap to manipulate, but even as it grows, the cost won't become prohibitively expensive. If all the pools have2*1_000
ETH of value each, the attack will cost only ~0.0036 ETH to move a price by 3%, and ~0.0363 ETH to move it by 10%. Refer to the PoCs for the estimated cost of the attack.Assume the WBTC/USD price moves 3%, from $40,000 to $38,800. Chainlink updates instantly, but the TWAP takes some time. You can see my calculations of the TWAP price change here.
CoreSaltyFeed
WBTC/USDS price will be adjusted to match Chainlink's price by arbitrageurs.CoreSaltyFeed
returns $38,800, Chainlink returns $38,800, TWAP returns $40,000.The attacker moves the
CoreSaltyFeed
price ~3%, but less than the difference between TWAP and Chainlink, to $38,000.As shown in the PoC, it will cost the attacker only 0.0035 ETH if the pools have 1000 ETH of liquidity, but if they have 100 ETH, it will require only ~0.0004 ETH.
The difference between
CoreSaltyFeed
and Chainlink is $800, and from TWAP and Chainlink it's $1,200.The average price is set to ($38,000 + $38,800) / 2 = $38,400.
Now the attacker can liquidate pools that should not be liquidatable because the price from PriceAggregator is lower than the real price. The attacker can do it first and get the rewards (5%, up to $500 by default). See the relevant code here.
maxRewardValueForCallingLiquidation
is set to $500. Depending on Salty's pool liquidity, ETH price, and how many positions an attacker can liquidate, profitability will vary. I argue that before the protocol gains traction, liquidity will be low for some time, making the attack profitable.We should also consider that sometimes it will be profitable for the attacker to move the price slightly and be the first to call
liquidate
in order to receive the rewards.Other liquidators, who don't use this attack, will not be able to liquidate, which is unfair.
Note: WBTC and WETH movements of 3% are common and will happen often. For example, about a month ago, there was a 6.5% drop in 20 minutes as reported by Business Insider.
Variations
StableConfig.initialCollateralRatioPercent
and calculated using the outdated TWAP price along with the manipulatedCoreSaltyFeed
, would be ineffective as protection against this attack when the real price has already dropped below 100%.Impact
Proof of Concept
Put the code in
src/pools/tests/H2.t.sol
, runCOVERAGE="yes" forge test -f wss://ethereum-sepolia.publicnode.com -vvv --mc H2
(Optional) You can place this AWK script in
1e18.sh
, make it executable withchmod +x 1e18.sh
, and runCOVERAGE="yes" forge test -f wss://ethereum-sepolia.publicnode.com -vvv --mc M2 | ./1e18.sh
for a more readable output. This script will convert numbers in exponential notation to a floating point format with three decimal places. For example,1e17
will be printed as0.100
.Tools Used
Manual review
Recommended Mitigation Steps
Consider replacing
CoreSaltyFeed
with a different oracle that provides better protection against manipulation, like Band Protocol.Assessed type
Oracle