FactoryDAO contest - hickuphh3'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: 13/71

Findings: 5

Award: $636.60

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

Awards

63.9296 DAI - $63.93

Labels

bug
duplicate
3 (High Risk)

External Links

Lines of code

https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/FixedPricePassThruGate.sol#L48 https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/FixedPricePassThruGate.sol#L53

Vulnerability details

Details & Impact

If msg.value exceeds gate.ethCost, the difference remains permanently stuck in the price gate because the call to the beneficiary is only gate.ethCost.

Just like the speedBumpGate, the call to the beneficiary should be msg.value instead of gate.ethCost.

(bool sent, bytes memory data) = gate.beneficiary.call{value: msg.value}("");

#1 - gititGoro

2022-06-14T03:20:21Z

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#L224-L234

Vulnerability details

Details & Impact

A reward pool that is created with a malicious reward token (Eg. one that intentionally always returns false on only transfer calls) will brick user withdrawals.

Proof of Concept

  • Attacker creates a malicious reward token (eg. MAL) that works normally for .transferFrom() calls such that fundPool() will not revert, but always returns false for .transfer()

  • Create a reward pool with USDC as the deposit token and MAL as the sole reward token

  • A user deposits into the pool, and after some time, attempts to withdraw his USDC

  • Withdrawal reverts because

    IERC20(pool.rewardTokens[i]).transfer(receipt.owner, transferAmount);

    will be false

Create an emergencyWithdraw() function that allows redemption of receipts for only deposited funds (forego rewards).

#1 - gititGoro

2022-06-14T01:47:49Z

Changing severity because pool isolation and rewards tokens represent leakage not lost funds.

#2 - HickupHH3

2022-06-19T08:36:50Z

Isn't this a closer duplicate of #20 than #34?

#3 - gititGoro

2022-06-23T03:05:16Z

Isn't this a closer duplicate of #20 than #34?

Yes, you're correct. Updating to duplicate of 20. Thank you.

#4 - gititGoro

2022-06-23T03:05:28Z

duplicate of #20

Awards

3.1753 DAI - $3.18

Labels

bug
duplicate
2 (Med Risk)

External Links

Lines of code

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

Vulnerability details

Details & Impact

Certain ERC20 tokens make modifications to their ERC20's transfer or balanceOf functions. One type of these tokens is deflationary tokens that charge a certain fee for every transfer() or transferFrom().

These deflationary tokens are incompatible as a reward token, and more importantly, a deposit token for basic staking because the amount recorded (receipt.amountDepositedWei = amount;) is less than the actual amount received. Attempts to redeem the receipt may fail because the contract could hold insufficient tokens.

One possible mitigation is to measure the asset change right before and after the asset-transferring calls.

Findings Information

🌟 Selected for report: hickuphh3

Also found by: GimelSec, gzeon, scaraven

Labels

bug
2 (Med Risk)
disagree with severity
sponsor confirmed

Awards

320.671 DAI - $320.67

External Links

Lines of code

https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/MerkleResistor.sol#L259 https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/MerkleResistor.sol#L264

Vulnerability details

Details & Impact

It is possible for coinsPerSecond to be zero. In these cases, the startTime calculation

uint startTime = block.timestamp + vestingTime - (totalCoins / coinsPerSecond);

will revert from division by zero, preventing initialization, and by extension, withdrawals of vested tokens.

Proof of Concept

We assume vesting time chosen is the maximum (tree.maxEndTime) so that totalCoins = maxTotalPayments. These examples showcase some possibilities for which the calculated coinsPerSecond can be zero.

Example 1: High upfront percentage

  • pctUpFront = 99 (99% up front)
  • totalCoins = 10_000e6 (10k USDC)
  • vestingTime = 1 year
uint coinsPerSecond = (totalCoins * (uint(100) - tree.pctUpFront)) / (vestingTime * 100);
// 10_000e6 * (100 - 99) / (365 * 86400 * 100)
// = 0

Example 2: Small reward amount / token decimals

  • pctUpFront = 0
  • totalCoins = 100_000e2 (100k EURS)
  • vestingTime = 180 days
uint coinsPerSecond = (totalCoins * (uint(100) - tree.pctUpFront)) / (vestingTime * 100);
// 100_000e2 * 100 / (180 * 86400 * 100)
// = 0

Scale up coinsPerSecond by PRECISION, then scale down when executing withdrawals. While it isn’t foolproof, the possibility of coinsPerSecond being zero is reduced significantly.

// L264
uint coinsPerSecond = (totalCoins * (uint(100) - tree.pctUpFront)) * PRECISION / (vestingTime * 100);

// L184
currentWithdrawal = (block.timestamp - tranche.lastWithdrawalTime) * tranche.coinsPerSecond / PRECISION;

#0 - illuzen

2022-05-10T09:47:00Z

Example 1 = 3, not 0

Duplicate, but this mitigation is better

#1 - gititGoro

2022-06-15T00:23:50Z

Reducing severity because rewards are not staked user funds.

Low Severity Findings

L01: PermissionlessBasicPoolFactory: globalTaxPerCapita bound is unchecked in constructor

Line References

https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L75-L78

https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L316

Description

The setGlobalTax() has an exclusive upper bound of 1000 on the globalTaxPerCapita value to be set, but this upper bound is not enforced in the constructor.

constructor(address _globalBeneficiary, uint _globalTaxPerCapita) {
  globalBeneficiary = _globalBeneficiary;
  require(_globalTaxPerCapita < 1000, 'Tax too high');
  globalTaxPerCapita = _globalTaxPerCapita;
}

L02: PermissionlessBasicPoolFactory: Return early in getRewards() if receipt has been redeemed

Line References

https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/PermissionlessBasicPoolFactory.sol#L156-L173

Description

Consider returning zero amounts if the receipt has already been redeemed.

// after L167
if (receipt.timeWithdrawn != 0) return rewardsLocal;

L03: VoterID: Incomplete contract description

Line References

https://github.com/code-423n4/2022-05-factorydao/blob/main/contracts/VoterID.sol#L10

Complete the missing description of the contract.

Non-Critical Findings

NC01: Inconsistent syntax in MerkleEligibility.addGate()

Line References

https://github.com/code-423n4/2022-05-factorydao/blob/db415804c06143d8af6880bc4cda7222e5463c0e/contracts/MerkleEligibility.sol#L47

Description

The other gates use the prefix operator ++numGates, should do the same here for consistency.

#0 - illuzen

2022-05-10T15:31:09Z

L1: duplicate L2: valid L3: valid NC01: valid

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