FactoryDAO contest - hubble's results

The DAO that builds DAOs.

General Information

Platform: Code4rena

Start Date: 04/05/2022

Pot Size: $50,000 DAI

Total HM: 24

Participants: 71

Period: 5 days

Judge: Justin Goro

Total Solo HM: 14

Id: 119

League: ETH

FactoryDAO

Findings Distribution

Researcher Performance

Rank: 30/71

Findings: 1

Award: $173.16

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: shenwilly

Also found by: 0xYamiDancho, hickuphh3, hubble, kenzo, leastwood

Labels

bug
duplicate
2 (Med Risk)

Awards

173.1623 DAI - $173.16

External Links

Lines of code

https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/PermissionlessBasicPoolFactory.sol#L215-L234

Vulnerability details

In PermissionlessBasicPoolFactory , and multiple reward tokens configured, if there is any issues with any one rewards token, then withdraw will fail for even the principal deposit. The transferFrom of rewardTokens can fail for any known/unknown reasons, like the rewardToken contract is paused or frozen at that time, or one of the rewardToken is a malicious token, etc.,

Since this is a permissionless setup, there is a possibility of deploying pools with wrong rewardTokens, and difficult for users to discover until withdraw is called.

Impact

Loss of user funds. The deposit amount of users will get stuck in the contract and not possible to withdraw.

Proof of Concept

Contract : PermissionlessBasicPoolFactory.sol Function : withdraw()

Need a backup function to withdraw only deposit amount. Suggested changes as given below.

In struct Receipt , instead of timeWithdrawn use two different members

uint timeWithdrawnRewards; // the time the rewards was withdrawn, or 0 if not withdrawn yet uint timeWithdrawnDeposit; // the time the deposit was withdrawn, or 0 if not withdrawn yet

Add another function for withdrawing deposit amount.

function withdrawDeposit(uint poolId, uint receiptId) external { Pool storage pool = pools[poolId]; require(pool.id == poolId, 'Uninitialized pool'); Receipt storage receipt = pool.receipts[receiptId]; require(receipt.id == receiptId, 'Can only withdraw real receipts'); require(receipt.owner == msg.sender || block.timestamp > pool.endTime, 'Can only withdraw your own deposit'); require(receipt.timeWithdrawnDeposit == 0, 'Can only withdraw once per receipt'); // close re-entry gate receipt.timeWithdrawnDeposit = block.timestamp; bool success = true; success = IERC20(pool.depositToken).transfer(receipt.owner, receipt.amountDepositedWei); require(success, 'Token transfer failed'); emit WithdrawalOccurred(poolId, receiptId, receipt.owner); // change the event log as appropriate }

Also modify the current withdraw function to take into account the modified Receipt members timeWithdrawnRewards & timeWithdrawnDeposit

Alternately , have different functions to withdrawRewards and withdrawDeposit for simplicity

#0 - illuzen

2022-05-12T04:45:10Z

Duplicate #20

#1 - gititGoro

2022-06-14T01:13:25Z

reducing severity as rewards are not base assets.

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