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

Unlimited Global & User Withdrawal right after previous period ends and new period begins #283

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

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/WithdrawHook.sol#L59

Vulnerability details

Impact

Checks for Global and User Withdraw Limit Per Period are missing for the first withdrawal request right AFTER period length expires and a new period begins. First withdrawal request amount after period length expires can be way higher than globalAmountWithdrawnThisPeriod.

Since timestamp when a next period begins is known well in advance, a large whale can time his withdrawal exactly when a period ends. By dumping a large size of $preCT tokens, a whale can create serious arbitrage opportunities viz-a-viz Uniswap V3 pool. Since this can have a significant short term disruption in supply, I've marked it as HIGH risk

In the WithdrawHook contract line 59, when block.timestamp > lastGlobalPeriodReset + globalPeriodLength, two variables lastGlobalPeriodReset and globalAmountWithdrawnThisPeriod are being reset.

However, for the first withdrawal request when the two above variables are being reset, check if _amountBeforeFee exceeds globalWithdrawLimitPerPeriod is missing. This check is done for the next request in line 63 when lastGlobalPeriodReset is set to previous block timestamp

Same problem also exists for lastUserPeriodReset and userToAmountWithdrawnThisPeriod[_sender] variables in

If a user makes a large withdrawal request right after a period expires, that request will be honored in existing codebase. This is in direct violation of the designed logic that puts restriction on global withdrawal and user specific withdrawal in each period

Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

Suppose global withdrawal limit per period if 10m USDC and each period is for 100 seconds. Let's say current timestamp is 10000 -> Bob, a whale depositor times a withdrawal request at exactly 10100 sec for 50m USDC -> Bob sources this liquidity of preCT tokens by dumping Long/Short tokens and draining preCT from UniV3 pools. This can cause massive mispricing of these tokens in short term

Although it can create massive arbitrage opportunities-> in current bear markets with low liquidity, such arbitrage might not be quickly closed. This can result in user panic & loss of confidence in protocol.

Tools Used

Recommended Mitigation Steps

Add the check when period gets reset. Added checks to following code block

     //global limit check
      if (lastGlobalPeriodReset + globalPeriodLength < block.timestamp) {
      lastGlobalPeriodReset = block.timestamp; /
      require(_amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded");
      globalAmountWithdrawnThisPeriod = _amountBeforeFee; /
    } else {
      require(globalAmountWithdrawnThisPeriod + _amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded");
      globalAmountWithdrawnThisPeriod += _amountBeforeFee;
    }
    
    // user limit check
    if (lastUserPeriodReset + userPeriodLength < block.timestamp) {
      lastUserPeriodReset = block.timestamp;
      require(_amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded");
      userToAmountWithdrawnThisPeriod[_sender] = _amountBeforeFee; 
    } else {
      require(userToAmountWithdrawnThisPeriod[_sender] + _amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded");
      userToAmountWithdrawnThisPeriod[_sender] += _amountBeforeFee;
    }
@code423n4 code423n4 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Dec 12, 2022
code423n4 added a commit that referenced this issue Dec 12, 2022
@hansfriese
Copy link

duplicate of #310

@c4-judge
Copy link
Contributor

Picodes marked the issue as duplicate of #310

@c4-judge
Copy link
Contributor

c4-judge commented Jan 1, 2023

Picodes marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Jan 1, 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-310 satisfactory satisfies C4 submission criteria; eligible for awards
Projects
None yet
Development

No branches or pull requests

3 participants