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

The malicious first liquidity provider can steal user's fund or prevent others from providing liquidity #123

Closed
code423n4 opened this issue Dec 16, 2022 · 2 comments
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working duplicate-442 edited-by-warden satisfactory satisfies C4 submission criteria; eligible for awards

Comments

@code423n4
Copy link
Contributor

code423n4 commented Dec 16, 2022

Lines of code

https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L421

Vulnerability details

Impact

If a malicious user is the first person who is interacting with the pair, he can manipulate the reserve so that stealing fund from users if minLpTokenAmount == 0 or preventing users from adding liquidity if minLpTokenAmount != 0.

Proof of Concept

function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount) public view returns (uint256) {
        uint256 lpTokenSupply = lpToken.totalSupply();
        if (lpTokenSupply > 0) {
            // calculate amount of lp tokens as a fraction of existing reserves
            uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
            uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves();
            return Math.min(baseTokenShare, fractionalTokenShare);
        } else {
            // if there is no liquidity then init
            return Math.sqrt(baseTokenAmount * fractionalTokenAmount);
        }
    }

https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L417
https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L90

  • Now Bob has the control of the pair and can apply two kinds of exploit:
    • First: if a user is going to add for example 1000 USDC and some fractional token with minLpTokenAmount parameter equal to zero, Bob front-runs the user and transfers directly to the pair 1000 USDC. By doing so, baseTokenReserves() = 1 + 1000 and total lpToken minted to the user will be equal to zero because of 1000 * 1 /(1 + 1000) = 0:
      uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
      https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L421
      Although, the user provided 1000 USDC, but zero lpToken is minted for the user. So, totalSupply = 1 and baseTokenReserves = 1 + 1000 + 1000. Now, Bob can remove his liquidity by burning his only lpToken, and receive 2001 USDC (1001 belongs to Bob, and 1000 USDC is user's fund). So, Bob could steal 1000 USDC from the user.
    • Second: if a user is going to add for example 1000 USDC and some fractional token with minLpTokenAmount parameter equal to nonzero X, Bob front-runs the user and transfers directly to the pair 1000/X USDC. By doing so, total lpToken minted to the user will be less than X because 1000 * 1 /(1 + 1000/X) = 1000 * X / (X + 1000) < X:
      uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
      https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L421
      As a result the user's transaction will be reverted because of:
      require(lpTokenAmount >= minLpTokenAmount, "Slippage: lp token amount out");
      https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L80
      So, Bob could prevent any user from adding liquidity with nonzero minLpTokenAmount . Now, Bob can remove his liquidity by burning his only lpToken, and receive 1 + 1000/X USDC that belongs to him.

Tools Used

Recommended Mitigation Steps

  • First Solution: The first person who is adding liquidity must add at least 100 USDC. So, totalSupply = 100 and baseTokenReserves() =100. By doing so, Bob (as the first person) must add 100 USDC and in return he receives 100 lpToken. To apply the attack when a user is adding 1000 USDC, Bob must transfer directly 99.9k + 1 USDC to be able to steal the user's 1000 USDC, because 1000 * 100 / (100 + Bob's direct transfer). So, Bob's attack requires a large amount of fund which makes it financially unreasonable.
function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount) public view returns (uint256) {
        uint256 lpTokenSupply = lpToken.totalSupply();
        if (lpTokenSupply > 0) {
            // calculate amount of lp tokens as a fraction of existing reserves
            uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
            uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves();
            return Math.min(baseTokenShare, fractionalTokenShare);
        } else {
            require(baseTokenAmount >= 100 * 10**6);
            // if there is no liquidity then init
            return Math.sqrt(baseTokenAmount * fractionalTokenAmount);
        }
    }
  • Second Solution: It is recommended that when the pair is created a large amount of lpToken be minted to the pair. For example, if the protocol mints 1000 lpToken to the pair, Bob must provide almost 1,001,000 USDC to make this 1000 * (1000 + 1)/ (1 + Bob's direct transfer) equal to zero (after rounded down by EVM), which is almost impossible for Bob.
@code423n4 code423n4 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Dec 16, 2022
code423n4 added a commit that referenced this issue Dec 16, 2022
@code423n4 code423n4 changed the title Stealing fund from users or preventing users from adding liquidity by manipulating the reserve Stealing fund from users or preventing users from adding liquidity by being the first liquidity provider Dec 19, 2022
@code423n4 code423n4 changed the title Stealing fund from users or preventing users from adding liquidity by being the first liquidity provider The malicious first liquidity provider can steal user's fund or prevent others from providing liquidity Dec 19, 2022
@c4-judge
Copy link
Contributor

berndartmueller marked the issue as duplicate of #442

@c4-judge
Copy link
Contributor

berndartmueller marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Jan 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working duplicate-442 edited-by-warden satisfactory satisfies C4 submission criteria; eligible for awards
Projects
None yet
Development

No branches or pull requests

2 participants