Open sherlock-admin2 opened 5 months ago
1 comment(s) were left on this issue during the judging contest.
takarez commented:
invalid
I don't believe this is an issue due to the check for price freshness here. However we can probably implement the suggestion for more protection.
@rashtrakoff If I understand correctly, as long as freshness of price does not exceed maxAge
, it is an accepted updated price, if not it will revert, hence making this issue invalid.
Hi @rashtrakoff here is LSW comments, which seems valid to me.
The check at Line 111 basically checks for deviation between the on-chain and off-chain price deviation. If the two prices deviate by a certain percentage
maxDiffPercent
(I recall the test or deployment script set it as 5%), the TX will revert.However, it is incorrect that this check will prevent this issue. I expected that the sponsor might assume that this check might prevent this issue during the audit. Thus, in the report (at the end of the "Vulnerability Detail"), I have documented the reasons why this oracle check would not help to prevent this issue, and the example and numbers in the report are specially selected to work within the 5% price deviation 🙂
Sidenote: The oracle's maxDiffPercent check will not guard against this attack effectively. For instance, in the above example, if the Chainlink price is $975 and the maxDiffPercent is 5%, the Pyth price of $950 or $1000 still falls within the acceptable range. If the maxDiffPercent is reduced to a smaller margin, it will potentially lead to a more serious issue where all the transactions get reverted when fetching the price, breaking the entire protocol.
The protocol team fixed this issue in PR/commit https://github.com/dhedge/flatcoin-v1/pull/277.
Escalate
Low (med at best)
Oracle's prices discrepancy should be within trader fee charged.
Escalate
Low (med at best)
Oracle's prices discrepancy should be within trader fee charged.
You've created a valid escalation!
To remove the escalation from consideration: Delete your comment.
You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.
Escalate
Low (med at best)
Oracle's prices discrepancy should be within trader fee charged.
The escalation is invalid.
The report's sidenote and my comment shared by the lead judge have explained why the existing price deviation control does not prevent this attack.
I agree with @xiaoming9090, I don't see a valid point in the escalation.
Planning to reject the escalation and leave the issue as is.
Agree with @xiaoming9090 and @Czar102
Result: High Has duplicates
We have a PR ready for this but just wanted to touch upon this comment. While I agree that the price deviation check can be by-passed, the price staleness check that my comment refers to cannot be by-passed as per my understanding of Pyth price update function. Could you confirm if what I commented makes sense @xiaoming9090 @nevillehuang ?
xiaoming90
high
Malicious keepers can manipulate the price when executing an order
Summary
Malicious keepers can manipulate the price when executing an order by selecting a price in favor of either the LPs or long traders, leading to a loss of assets to the victim's party.
Vulnerability Detail
When the keeper executes an order, it was understood from the protocol team that the protocol expects that the keeper must also update the Pyth price to the latest one available off-chain. In addition, the contest page mentioned that "an offchain price that is pulled by the keeper and pushed onchain at time of any order execution".
This requirement must be enforced to ensure that the latest price is always used.
https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/DelayedOrder.sol#L386
However, this requirement can be bypassed by malicious keepers. A keeper could skip or avoid the updating of the Pyth price by passing in an empty
priceUpdateData
array, which will pass the empty array to theOracleModule.updatePythPrice
function.https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/abstracts/OracleModifiers.sol#L12
When the Pyth's
Pyth.updatePriceFeeds
function is executed, theupdateData
parameter will be set to an empty array.https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/OracleModule.sol#L64
Inspecting the source code of Pyth's on-chain contract, the
Pyth.updatePriceFeeds
function will not perform any update since theupdateData.length
will be zero in this instance.https://goerli.basescan.org/address/0xf5bbe9558f4bf37f1eb82fb2cedb1c775fa56832#code#F24#L75
The keeper is permissionless, thus anyone can be a keeper and execute order on the protocol. If this requirement is not enforced, keepers who might also be LPs (or collude with LPs) can choose whether to update the Pyth price to the latest price or not, depending on whether the updated price is in favor of the LPs. For instance, if the existing on-chain price (\$1000 per ETH) is higher than the latest off-chain price (\$950 per ETH), malicious keepers will use the higher price of \$1000 to open the trader's long position so that its position's entry price will be set to a higher price of \$1000. When the latest price of \$950 gets updated, the longer position will immediately incur a loss of \$50. Since this is a zero-sum game, long traders' loss is LPs' gain.
Note that per the current design, when the open long position order is executed at $T2$, any price data with a timestamp between $T1$ and $T2$ is considered valid and can be used within the
executeOpen
function to execute an open order. Thus, when the malicious keeper uses an up-to-date price stored in Pyth's on-chain contract, it will not revert as long as its timestamp is on or after $T1$.Alternatively, it is also possible for the opposite scenario to happen where the keepers favor the long traders and choose to use a lower older price on-chain to execute the order instead of using the latest higher price. As such, the long trader's position will be immediately profitable after the price update. In this case, the LPs are on the losing end.
Sidenote: The oracle's
maxDiffPercent
check will not guard against this attack effectively. For instance, in the above example, if the Chainlink price is \$975 and themaxDiffPercent
is 5%, the Pyth price of \$950 or \$1000 still falls within the acceptable range. If themaxDiffPercent
is reduced to a smaller margin, it will potentially lead to a more serious issue where all the transactions get reverted when fetching the price, breaking the entire protocol.Impact
Loss of assets as shown in the scenario above.
Code Snippet
https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/DelayedOrder.sol#L386
https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/abstracts/OracleModifiers.sol#L12
https://github.com/sherlock-audit/2023-12-flatmoney/blob/main/flatcoin-v1/src/OracleModule.sol#L64
Tool used
Manual Review
Recommendation
Ensure that the keepers must update the Pyth price when executing an order. Perform additional checks against the
priceUpdateData
submitted by the keepers to ensure that it is not empty andpriceId
within thePriceInfo
matches the price ID of the collateral (rETH), so as to prevent malicious keeper from bypassing the price update by passing in an empty array or price update data that is not mapped to the collateral (rETH).