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

StraawHaat - Lack of Slippage Protection for Reserve during swaps #97

Closed
sherlock-admin3 opened this issue Sep 10, 2024 · 12 comments
Closed
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label Escalation Resolved This issue's escalations have been approved/rejected High A High severity issue. Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Won't Fix The sponsor confirmed this issue will not be fixed

Comments

@sherlock-admin3
Copy link

sherlock-admin3 commented Sep 10, 2024

StraawHaat

High

Lack of Slippage Protection for Reserve during swaps

Summary

The swapRaforDs() function allows users to swap Redemption Asset (RA) for Depeg Swap (DS) tokens:

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L98-L138
swapRaforDs() -> _swapRaforDs():

    function _swapRaforDs(
        ReserveState storage self,
        AssetPair storage assetPair,
        Id reserveId,
        uint256 dsId,
        uint256 amount,
        uint256 amountOutMin
    ) internal returns (uint256 amountOut) {
        uint256 borrowedAmount;

        // calculate the amount of DS tokens attributed
        (amountOut, borrowedAmount,) = assetPair.getAmountOutBuyDS(amount);

        // calculate the amount of DS tokens that will be sold from LV reserve
        uint256 amountSellFromReserve =
            amountOut - MathHelper.calculatePrecentageFee(self.reserveSellPressurePrecentage, amountOut);
        // sell all tokens if the sell amount is higher than the available reserve
        amountSellFromReserve = assetPair.reserve < amountSellFromReserve ? assetPair.reserve : amountSellFromReserve;

        // sell the DS tokens from the reserve if there's any
        if (amountSellFromReserve != 0) {
            // decrement reserve
            assetPair.reserve -= amountSellFromReserve;

            // sell the DS tokens from the reserve and accrue value to LV holders
            uint256 vaultRa = __swapDsforRa(assetPair, reserveId, dsId, amountSellFromReserve, 0); //@audit - no slippage
            IVault(owner()).provideLiquidityWithFlashSwapFee(reserveId, vaultRa);

  
            // recalculate the amount of DS tokens attributed, since we sold some from the reserve
            (amountOut, borrowedAmount,) = assetPair.getAmountOutBuyDS(amount);
        }

        if (amountOut < amountOutMin) {
            revert InsufficientOutputAmount();
        }

        // trigger flash swaps and send the attributed DS tokens to the user

        __flashSwap(assetPair, assetPair.pair, borrowedAmount, 0, dsId, reserveId, true, amountOut);
    }

If a user wants to have slippage protection, he can set it as a parameter, and at the end of the function, we have a check.

https://github.com/sherlock-audit/2024-08-cork-protocol/blob/main/Depeg-swap/contracts/core/flash-swaps/FlashSwapRouter.sol#L124

        if (amountOut < amountOutMin) {
            revert InsufficientOutputAmount();
        }

However, when the protocol executes the swap for its reserve, there is no slippage protection in place, exposing the protocol to unfavorable market conditions. Specifically, the reserve sells DS tokens without ensuring a minimum acceptable amount of RA in return, potentially resulting in significant financial losses for the protocol.

uint256 vaultRa = __swapDsforRa(assetPair, reserveId, dsId, amountSellFromReserve, 0);

In this line, the reserve executes the swap without any slippage protection (the slippage is set to 0), meaning the reserve will accept any amount of RA in return for the DS tokens it sells. This makes the protocol vulnerable to market volatility or manipulation.

There are three variants here:

  1. A regular user sets a slippage for him, but in the swap protocol loses tokens because there is no slippage protection for the reserve.
  2. A regular user decides he doesn't need slippage protection and leaves it at 0. The protocol takes a huge loss because of this user decision.
  3. Self-sandwich Attack. I will explain in more detail in the Attack Path.

Root Cause

The lack of slippage protection when the reserve sells its DS tokens in the _swapRaforDs() function:

uint256 vaultRa = __swapDsforRa(assetPair, reserveId, dsId, amountSellFromReserve, 0);

Internal pre-conditions

Мalicious or normal users need to set amountOutMin to 0. In the Attack Path section, I have explained both variants.

  • If a normal user calls swapRaforDs() with slippage protection, the loss to the protocol is minimal - Low Severity.
  • If a normal user calls swapRaforDs() without slippage protection, the loss to the protocol is significant - High Severity.
  • Self-sandwich Attack - High Severity

External pre-conditions

No external pre-conditions

Attack Path

Normal Attack Path

    • A user calls the swapRaforDs() function to swap RA for DS tokens and does not want slippage protection for himself.
  • The function internally calls the _swapRaforDs() function.
  • Inside _swapRaforDs(), the protocol attempts to sell DS tokens from the reserve using the following code:
uint256 vaultRa = __swapDsforRa(assetPair, reserveId, dsId, amountSellFromReserve, 0);
  • The reserve is selling DS tokens with 0 slippage protection.
  • The reserve could suffer significant losses

Self-sandwich Attack Path

In this scenario, the attacker uses two accounts (let's call them Account A and Account B) to manipulate the protocol’s lack of slippage protection for personal profit.

  • The attacker controls two accounts: Account A and Account B.
  • Account A will make a trade that shifts the market price.
  • Account B will execute the swap using swapRaforDs() with zero slippage protection to exploit the price difference caused by Account A.
  • Account A places a transaction that manipulates the price of DS tokens, for example, by buying a large amount of DS or swapping another token to increase DS price or decrease liquidity. This transaction front-runs Account B’s swap, ensuring that when Account B's transaction executes, it does so at a manipulated and unfavorable price.
  • Since there is no slippage protection, the protocol will complete the trade at a much worse price than it should have, causing the reserve to lose value.
  • After Account B has executed its swap and the protocol has lost value, Account A back-runs the transaction by reversing the initial trade or taking advantage of the new price levels.
  • Account A sells DS tokens at the inflated price, securing a profit.

Impact

The protocol suffers losses when the reserve sells DS tokens without any slippage protection. In case of market volatility or manipulation, the protocol will receive less RA in return than the actual value of the DS tokens being sold.

PoC

No response

Mitigation

Introduce Slippage Protection for the Reserve. If a user decides they don't need slippage protection, then you can set a standard base percentage to protect the protocol.

Duplicate of #66

@github-actions github-actions bot added the High A High severity issue. label Sep 14, 2024
@sherlock-admin3 sherlock-admin3 added Sponsor Confirmed The sponsor acknowledged this issue is valid Will Fix The sponsor confirmed this issue will be fixed labels Sep 23, 2024
@ziankork
Copy link
Collaborator

The slippage protection for user is implemented(will provide the link later), and we need to fix the base slippage protection for the protocol(may also make sense to cancel the trade from the protocol if's below slippage protection)

@sherlock-admin3 sherlock-admin3 changed the title Proper Turquoise Goldfish - Lack of Slippage Protection for Reserve during swaps StraawHaat - Lack of Slippage Protection for Reserve during swaps Sep 25, 2024
@sherlock-admin3 sherlock-admin3 added the Reward A payout will be made for this issue label Sep 25, 2024
@SakshamGuruji3
Copy link

Escalate

According to readme ->

Any potential misbehaviour regarding DS/CT/FlashSwap mechanism that is not an issue right now but could pose risk in future integrations should be counted, but reported as Low issues.```

Since this is in flash swaps mechanism this has to be considered a LOW  

@sherlock-admin3
Copy link
Author

Escalate

According to readme ->

Any potential misbehaviour regarding DS/CT/FlashSwap mechanism that is not an issue right now but could pose risk in future integrations should be counted, but reported as Low issues.```

Since this is in flash swaps mechanism this has to be considered a LOW  

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.

@sherlock-admin4 sherlock-admin4 added the Escalated This issue contains a pending escalation label Sep 25, 2024
@AtanasDimulski
Copy link

I believe if this is considered to be valid, it should be dupped with #66, as it is about slippage protection.

@0xsimao
Copy link
Collaborator

0xsimao commented Sep 27, 2024

The readme message says 'which is not an issue right now', but this is an issue right now, so it does not apply. Additionally, it is a dup of #66 because it is the same root cause (no slippage protection) and conceptual mistake.

@StraawHaat
Copy link

The escalation is invalid. This is not a future integration issue; it is an issue now.

Regarding the comments, this is not a duplicate of #66.

Absolutely everything is different.

  • Different root cause.
  • Different attack path.
  • Different function.
  • Different fix.
  • Even an attack variant is shown which is missing in the other issue.

One of the most important differences is that this issue has slippage protection for the user but not for reserve. Meanwhile, #66 is about the user's slippage.

At least when you think it is a duplicate, provide quotes or show the rules on which points it should be duplicated. Let's look at them: https://docs.sherlock.xyz/audits/judging/judging#ix.-duplication-rules


IX. Duplication rules:

The duplication rules assume we have a "target issue", and the "potential duplicate" of that issue needs to meet the following requirements to be considered a duplicate.

  1. Identify the root cause
  2. Identify at least a Medium impact
  3. Identify a valid attack path or vulnerability path
  4. Fulfills other submission quality requirements (e.g. provides a PoC for categories that require one)

Only when the "potential duplicate" meets all four requirements will the "potential duplicate" be duplicated with the "target issue", and all duplicates will be awarded the highest severity identified among the duplicates.

Otherwise, if the "potential duplicate" doesn't meet all requirements, the "potential duplicate" will not be duplicated but could still be judged any other way (solo, a duplicate of another issue, invalid, or any other severity)


Let's start with the first point.

  1. Identify the root cause - The root cause is quite different. My issue is about the slippage of the reserve, and the other one is about the user slippage. In this issue, there is already a slippage on the user, while in the other, there is no slippage. To this, I would add a totally different contract and function.
  2. Identify at least a Medium impact - The only thing both issues have in common is the word slippage and that they have a High severity impact.
  3. Identify a valid attack path or vulnerability path - Here is a totally different attack path. Nothing in common with the other issue.
  4. Fulfills other submission quality requirements - Here, I've shown different types of attack paths, something that the other issue doesn't have. (it can't have it, of course. Here, it's about protecting the reserve, and the other is about protecting the user.)

Only when the "potential duplicate" meets all four requirements will the "potential duplicate" be duplicated with the "target issue".
Otherwise, if the "potential duplicate" doesn't meet all requirements, the "potential duplicate" will not be duplicated but could still be judged any other way(solo, a duplicate of another issue, invalid, or any other severity).

It can be seen that the common is severity. Are we duplicating all the issues by severity. No.


Moving on to the last part of the duplication rule: Root cause groupings

Here, we should not enter this category at all because the root cause is different. This is not about the user but about the reserve. There is already a user slippage implemented.

However, to avoid future disputes, I will also explain why it is not a duplicate under this rule.

Let's see the terms:

The exception to this would be if underlying code implementations OR impact OR the fixes are different, then they may be treated separately.

  • Code implementations - Totally different. Different contract and different function
  • Impact - Here, the impact is on the reserve. In the other issue, it is on the user.
  • Fixes are different - The fixes are different. Fixing one does not fix the issue of the other.

That means it does not differ on just one point, as noted, but on all points. These are the reasons why this issue is not a duplicate of the others.

@0xsimao
Copy link
Collaborator

0xsimao commented Sep 28, 2024

It swaps and sets a slippage of 0, it's the same issue.

Code implementations - Totally different. Different contract and different function

Based on this logic, if they had 1 contract for each uniswap, curve and so on pools, with different function names, we would have an infinite amount of issues.
The implements are the same, interacting with pools on functions that set a slippage.

Impact - Here, the impact is on the reserve. In the other issue, it is on the user.

So if we had 5 different roles, it would be 5 different issues.
The impact is the same, loss due to no slippage check, whether the user or the reserve takes the loss is not relevant.

Fixes are different - The fixes are different. Fixing one does not fix the issue of the other.

The fix is comparing to a twap or external oracle, the same for all.

@ziankork
Copy link
Collaborator

ziankork commented Sep 30, 2024

After working on this and using above scenario, one thing that i realized is that it's impractical to have slippage protection for reserve since :

  1. it makes no difference if we calculate the slippage tolerance(e.g amountOutMin) on the fly, since we'll be looking at the univ2 RA:CT reserve after the market shift

  2. if we decide to supply the slippage via params, this is really impractical to compute for user/contract since we have a custom percentage that can change anytime to determine how much DS we should sell from the reserve(e.g 50% means if user get 10 DS, the reserve sells 5 DS).

  3. the intent behind this selling DS process is to accrue yield for the LV holders AND to not dump the market. that's why we decide for this approach, since with this the router can "gradually" sell the DS without dumping the market.

conclusion :

  • impractical to add slippage protection for protocol
  • make sense and practical to add slippage protection for user
  • won't fix
                                      +---------------------+                                                                                                                                          
                                      |                     |                                                                                                                                          
                                      | shift market price  |<----+                                                                                                                                    
                                      |                     |     |                                                                                                                                    
                                      |                     |     |                                                                                                                                    
                                      +---------------------+     |<------------ user/contract must calculate slippage for itself AND protocol WITH custom selling percentage(not user friendly, yuck.)
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                                  |                                                                                                                                    
                                                      +-----------v----------+                                                                                                                         
                                                      |                      |                                                                                                                         
 it's useless if ---- ------------------------------->|       swapRaForDs()  <--------------+                                                                                                          
 we calculate the slippage here                       |                      |              |                                                                                                          
 since we'll be looking at the reserve                |                      |              |                                                                                                          
 after the tx that shift the price                    +----------------------+              |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                            |                                                                                                          
                                                                                 +----------v----------+                                                                                               
                                                                                 |                     |                                                                                               
                                                                                 |make favorable trades|                                                                                               
                                                                                 |                     |                                                                                               
                                                                                 |                     |                                                                                               
                                                                                 +---------------------+                                                                                               
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       

@sherlock-admin3 sherlock-admin3 added Won't Fix The sponsor confirmed this issue will not be fixed and removed Will Fix The sponsor confirmed this issue will be fixed labels Sep 30, 2024
@AtanasDimulski
Copy link

I think when it comes to slippage protection rules are pretty clear:

Root cause groupings

If the following issues appear in multiple places, even in different contracts. In that case, they may be considered to have the same root cause.

Issues with the same logic mistake.

  • Example: uint256 is cast to uint128 unsafely.

Issues with the same conceptual mistake.

  • Example: different untrusted external admins can steal funds.

Issues in the category

  • Slippage protection
  • Reentrancy
  • Access control
  • Front-run / sandwich ( issue A that identifies a front-run and issue B that identifies a sandwich can be duplicated )

@WangSecurity
Copy link

IIUC, this is indeed an issue right now, rather than a threat to external integrations. Hence, I agree that it's valid. Secondly, I agree that this should be a duplicate of #66:

  1. Root cause is the same -- missing slippage protection
  2. Rules specifically say that slippage protection is the same issue family.
  3. Fix is the same -- adding appropriate slippage protection.

Planning to reject the escalation since it's incorrect, but duplicate this with #66.

@WangSecurity WangSecurity added the Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label label Oct 8, 2024
@WangSecurity
Copy link

Result:
High
Duplicate of #66

@sherlock-admin2 sherlock-admin2 removed the Escalated This issue contains a pending escalation label Oct 8, 2024
@sherlock-admin3 sherlock-admin3 added the Escalation Resolved This issue's escalations have been approved/rejected label Oct 8, 2024
@sherlock-admin4
Copy link
Contributor

Escalations have been resolved successfully!

Escalation status:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label Escalation Resolved This issue's escalations have been approved/rejected High A High severity issue. Reward A payout will be made for this issue Sponsor Confirmed The sponsor acknowledged this issue is valid Won't Fix The sponsor confirmed this issue will not be fixed
Projects
None yet
Development

No branches or pull requests

9 participants