prePO contest - imare's results

Decentralized Exchange for Pre-IPO Stocks & Pre-IDO Tokens.

General Information

Platform: Code4rena

Start Date: 09/12/2022

Pot Size: $36,500 USDC

Total HM: 9

Participants: 69

Period: 3 days

Judge: Picodes

Total Solo HM: 2

Id: 190

League: ETH

prePO

Findings Distribution

Researcher Performance

Rank: 21/69

Findings: 2

Award: $401.74

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Trust

Also found by: 0Kage, Parth, aviggiano, ayeslick, bin2chen, cccz, chaduke, fs0c, hansfriese, imare, mert_eren, rvierdiiev

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-310

Awards

210.7761 USDC - $210.78

External Links

Lines of code

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

Vulnerability details

Impact

withdrawHook contract checks that inside a specified length of time only certain amount of withdrawal are possible per user and globally.

But on every period reset the allowed withdraw limit check is missing. And a user can withdraw more that is allowed.

Proof of Concept

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

Tools Used

Manual review

Enforce the limit check like this:

    if (lastGlobalPeriodReset + globalPeriodLength < block.timestamp) {
      lastGlobalPeriodReset = block.timestamp;
      globalAmountWithdrawnThisPeriod = _amountBeforeFee;
    } else {
-      require(globalAmountWithdrawnThisPeriod + _amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded");
      globalAmountWithdrawnThisPeriod += _amountBeforeFee;
    }
+
+   require(globalAmountWithdrawnThisPeriod + _amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded");
+    
    if (lastUserPeriodReset + userPeriodLength < block.timestamp) {
      lastUserPeriodReset = block.timestamp;
      userToAmountWithdrawnThisPeriod[_sender] = _amountBeforeFee;
    } else {
-      require(userToAmountWithdrawnThisPeriod[_sender] + _amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded");
      userToAmountWithdrawnThisPeriod[_sender] += _amountBeforeFee;
    }
+
+   require(userToAmountWithdrawnThisPeriod[_sender] + _amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded");

#0 - hansfriese

2022-12-14T17:45:48Z

duplicate of #310 although the mitigation is not correct

#1 - c4-judge

2022-12-17T21:08:12Z

Picodes marked the issue as duplicate of #310

#2 - c4-judge

2022-12-17T21:08:59Z

Picodes marked the issue as partial-50

#3 - Picodes

2022-12-17T21:09:09Z

Partial credit as the mitigation is incorrect

#4 - c4-judge

2023-01-01T17:20:29Z

Picodes marked the issue as satisfactory

#5 - c4-judge

2023-01-09T20:35:50Z

Picodes changed the severity to 3 (High Risk)

Findings Information

🌟 Selected for report: trustindistrust

Also found by: Trust, chaduke, fs0c, imare

Labels

bug
2 (Med Risk)
partial-50
duplicate-257

Awards

190.9616 USDC - $190.96

External Links

Lines of code

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

Vulnerability details

Impact

TokenSender contract is used to reimburse user fees. But if the balance of PPO token inside the contract is low it can happen that the user don't get any token even if there is some to be sent.

Proof of Concept

If the calculated token amount to send is more that is in the contract balance we simply don't send any

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

Tools Used

Manual review

At least two options

  • revert instead of just return; to signal that the TokenSender contract is low on balance. Admin should tip the contract so repeating call will not fail
  • return the difference of outputAmount and the token balance. Something like this:
    if (outputAmount == 0) return;
-   if (outputAmount > _outputToken.balanceOf(address(this))) return;
+    if (_outputToken.balanceOf(address(this) == 0) revert("need token refill"); // OR just return 0 but then the problem remains
+   if (outputAmount > _outputToken.balanceOf(address(this)))  {
+       outputAmount -= _outputToken.balanceOf(address(this); //send what you can      
+   }

    _outputToken.transfer(recipient, outputAmount);

#0 - c4-judge

2022-12-17T21:07:37Z

Picodes marked the issue as duplicate of #257

#1 - c4-judge

2023-01-07T15:14:53Z

Picodes marked the issue as partial-50

#2 - Picodes

2023-01-07T15:15:29Z

The impact does not explain why in this context this is an issue. As the rebate is optional the described behavior could make sense, so the explanation needs to be more detailed.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter