prePO contest - cccz'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: 11/69

Findings: 3

Award: $794.07

🌟 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

In WithdrawHook.hook, use globalWithdrawLimitPerPeriod and userWithdrawLimitPerPeriod to limit the number of tokens that can be withdrawn in a period.

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

However, there is no limit on the first withdrawal of a period, so that users can bypass the limit of globalWithdrawLimitPerPeriod and userWithdrawLimitPerPeriod.

Proof of Concept

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

Tools Used

None

Change to

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

#0 - hansfriese

2022-12-14T18:19:19Z

duplicate of #310

#1 - c4-judge

2022-12-19T09:48:55Z

Picodes marked the issue as duplicate of #310

#2 - c4-judge

2023-01-01T17:21:12Z

Picodes marked the issue as satisfactory

#3 - c4-judge

2023-01-09T20:34:53Z

Picodes changed the severity to 3 (High Risk)

Findings Information

🌟 Selected for report: hansfriese

Also found by: Trust, cccz, unforgiven

Labels

bug
2 (Med Risk)
satisfactory
duplicate-231

Awards

530.4488 USDC - $530.45

External Links

Lines of code

https://github.com/prepo-io/prepo-monorepo//blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/PrePOMarket.sol#L119-L124

Vulnerability details

Impact

PrePOMarket.setFinalLongPayout is used to determine the final price of the LongToken

  function setFinalLongPayout(uint256 _finalLongPayout) external override onlyOwner {
    require(_finalLongPayout >= floorLongPayout, "Payout cannot be below floor");
    require(_finalLongPayout <= ceilingLongPayout, "Payout cannot exceed ceiling");
    finalLongPayout = _finalLongPayout;
    emit FinalLongPayoutSet(_finalLongPayout);
  }

and after PrePOMarket.setFinalLongPayout is called, the user can call PrePOMarket.redeem to redeem the collateral, and the amount of collateral to be redeemed will be determined by the finalLongPayout.

  function redeem(uint256 _longAmount, uint256 _shortAmount) external override nonReentrant {
    require(longToken.balanceOf(msg.sender) >= _longAmount, "Insufficient long tokens");
    require(shortToken.balanceOf(msg.sender) >= _shortAmount, "Insufficient short tokens");

    uint256 _collateralAmount;
    if (finalLongPayout <= MAX_PAYOUT) {
      uint256 _shortPayout = MAX_PAYOUT - finalLongPayout;
      _collateralAmount = (finalLongPayout * _longAmount + _shortPayout * _shortAmount) / MAX_PAYOUT;

However, if PrePOMarket.setFinalLongPayout is called multiple times and different finalLongPayout is set, it will disrupt the user's redemption process.

Proof of Concept

https://github.com/prepo-io/prepo-monorepo//blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/PrePOMarket.sol#L119-L124

Tools Used

None

Change to

  function setFinalLongPayout(uint256 _finalLongPayout) external override onlyOwner {
+  require(finalLongPayout > MAX_PAYOUT, "Market ended");
    require(_finalLongPayout >= floorLongPayout, "Payout cannot be below floor");
    require(_finalLongPayout <= ceilingLongPayout, "Payout cannot exceed ceiling");
    finalLongPayout = _finalLongPayout;
    emit FinalLongPayoutSet(_finalLongPayout);
  }

#0 - hansfriese

2022-12-14T18:18:13Z

duplicate of #231

#1 - c4-judge

2022-12-19T09:48:27Z

Picodes marked the issue as duplicate of #231

#2 - c4-judge

2023-01-07T15:31:09Z

Picodes marked the issue as satisfactory

Findings Information

🌟 Selected for report: obront

Also found by: 8olidity, HE1M, Madalad, Trust, cccz, csanuragjain, deliriusz, hansfriese, hihen, joestakey, rvierdiiev, wait, zaskoh

Labels

bug
2 (Med Risk)
satisfactory
duplicate-254

Awards

52.8446 USDC - $52.84

External Links

Lines of code

https://github.com/prepo-io/prepo-monorepo//blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/Collateral.sol#L80-L83

Vulnerability details

Impact

MANAGER_WITHDRAW_ROLE can call Collateral.managerWithdraw to send baseToken to manager

  function managerWithdraw(uint256 _amount) external override onlyRole(MANAGER_WITHDRAW_ROLE) nonReentrant {
    if (address(managerWithdrawHook) != address(0)) managerWithdrawHook.hook(msg.sender, _amount, _amount);
    baseToken.transfer(manager, _amount);
  }

setManager is called by SET_MANAGER_ROLE

  function setManager(address _newManager) external override onlyRole(SET_MANAGER_ROLE) {
    manager = _newManager;
    emit ManagerChange(_newManager);
  }

The number of baseTokens that MANAGER_WITHDRAW_ROLE can take out is determined by ManagerWithdrawHook.minReservePercentage

function hook(address, uint256, uint256 _amountAfterFee) external view override { require(collateral.getReserve() - _amountAfterFee >= getMinReserve(), "reserve would fall below minimum"); } ... function getMinReserve() public view override returns (uint256) { return (depositRecord.getGlobalNetDepositAmount() * minReservePercentage) / PERCENT_DENOMINATOR; }

and setMinReservePercentage is called by SET_MIN_RESERVE_PERCENTAGE_ROLE

  function setMinReservePercentage(uint256 _newMinReservePercentage) external override onlyRole(SET_MIN_RESERVE_PERCENTAGE_ROLE) {
    require(_newMinReservePercentage <= PERCENT_DENOMINATOR, ">100%");
    minReservePercentage = _newMinReservePercentage;
    emit MinReservePercentageChange(_newMinReservePercentage);
  }

These roles can be directly granted by the owner, resulting in the owner being able to rug all baseToken in Collateral

Proof of Concept

https://github.com/prepo-io/prepo-monorepo//blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/Collateral.sol#L80-L83 https://github.com/prepo-io/prepo-monorepo//blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/ManagerWithdrawHook.sol#L17-L18

Tools Used

None

Consider using timelock

#0 - hansfriese

2022-12-14T18:18:53Z

duplicate of #254

#1 - c4-judge

2022-12-17T10:07:46Z

Picodes marked the issue as duplicate of #254

#2 - c4-judge

2023-01-01T17:44:11Z

Picodes marked the issue as satisfactory

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