Platform: Code4rena
Start Date: 16/01/2024
Pot Size: $80,000 USDC
Total HM: 37
Participants: 178
Period: 14 days
Judge: Picodes
Total Solo HM: 4
Id: 320
League: ETH
Rank: 47/178
Findings: 3
Award: $272.92
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 00xSEV
Also found by: Banditx0x, CongZhang-CertiK, J4X, Myrault, OMEN, jesjupyter, linmiaomiao, miaowu, n1punp
264.1611 USDC - $264.16
https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/CoreSaltyFeed.sol#L32
In the src/price_feed/PriceAggregator.sol
file, in the function _aggregatePrices
:
https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/PriceAggregator.sol#L108-L146
if ( ( diff12 <= diff13 ) && ( diff12 <= diff23 ) ) (priceA, priceB) = (price1, price2); else if ( ( diff13 <= diff12 ) && ( diff13 <= diff23 ) ) (priceA, priceB) = (price1, price3); else if ( ( diff23 <= diff12 ) && ( diff23 <= diff13 ) ) (priceA, priceB) = (price2, price3); uint256 averagePrice = ( priceA + priceB ) / 2;
averagePrice calculates the price by returning the average of the two with the smallest price difference among the three oracles (CoreChainlinkFeed, CoreSaltyFeed, CoreUniswapFeed).
However, in the oracle src/price_feed/CoreSaltyFeed.sol
, the getPriceBTC function and getPriceETH use instantaneous prices, so attackers can manipulate the price of this oracle through flash loans and profit from it:
Chainlink
melts down (Chainlink has blown down several times in history, so this is a possibility), only the average of CoreSaltyFeed
and CoreUniswapFeed
prices can be takenCoreSaltyFeed
through flash loans//CoreSaltyFeed.sol function getPriceBTC() external view returns (uint256) { (uint256 reservesWBTC, uint256 reservesUSDS) = pools.getPoolReserves(wbtc, usds); if ( ( reservesWBTC < PoolUtils.DUST ) || ( reservesUSDS < PoolUtils.DUST ) ) return 0; // reservesWBTC has 8 decimals, keep the 18 decimals of reservesUSDS return ( reservesUSDS * 10**8 ) / reservesWBTC; } //Utils.sol function corePrices(IPools pools, IExchangeConfig exchangeConfig, IPriceAggregator priceAggregator) public view returns (uint256 btcPrice, uint256 ethPrice, uint256 saltPrice, uint256 usdsPrice) { btcPrice = priceAggregator.getPriceBTC(); ... }
Manual review
For market price oracles such as Uniswap or OasisDEX, due to the possibility of lightning attacks, you cannot use the current median market price as a price feed under any circumstances. An attacker can easily and significantly change the median market price with just one transaction, causing the oracle to malfunction. The best solution to this is to calculate the weighted average of the last batch of X blocks via Time Weighted Average Price (TWAP) or Volume Weighted Average Price (VWAP).
Oracle
#0 - c4-judge
2024-02-02T14:19:36Z
Picodes marked the issue as duplicate of #609
#1 - c4-judge
2024-02-19T11:04:46Z
Picodes marked the issue as satisfactory
🌟 Selected for report: juancito
Also found by: 0x3b, 0xAlix2, 0xAsen, 0xPluto, 0xpiken, Limbooo, Myrault, PENGUN, Ward, a3yip6, b0g0, falconhoof, gkrastenov, haxatron, israeladelaja, jasonxiale, linmiaomiao, memforvik, miaowu, nonseodion, t0x1c, y4y
8.7582 USDC - $8.76
https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L103
There is a problem with the proposal naming rules, and there is a possibility of duplication of proposal names at different stages. When you want to add the suffix "_confirm" to a confirmed proposal, it will be checked to see if a proposal with the same name already exists. If there is a proposal with the same name, it cannot be created. This allows an attacker to create a proposal with ballotName + "_confirm" so that the proposal can never advance to the next stage.
Manual review
When creating and naming files, it is forbidden to use names with the suffix "_confirm". In this way, there is no possibility of duplication of proposal titles at different stages.
// Make sure that a proposal of the same name is not already open for the ballot require( openBallotsByName[ballotName] == 0, "Cannot create a proposal similar to a ballot that is still open" ); require( openBallotsByName[ string.concat(ballotName, "_confirm")] == 0, "Cannot create a proposal for a ballot with a secondary confirmation" );
DoS
#0 - c4-judge
2024-02-01T15:59:42Z
Picodes marked the issue as duplicate of #620
#1 - c4-judge
2024-02-19T16:39:29Z
Picodes changed the severity to 2 (Med Risk)
#2 - c4-judge
2024-02-19T16:44:05Z
Picodes changed the severity to QA (Quality Assurance)
#3 - c4-judge
2024-02-19T16:44:35Z
This previously downgraded issue has been upgraded by Picodes
#4 - c4-judge
2024-02-19T16:47:56Z
Picodes marked the issue as satisfactory