Fractional v2 contest - codexploder's results

A collective ownership platform for NFTs on Ethereum.

General Information

Platform: Code4rena

Start Date: 07/07/2022

Pot Size: $75,000 USDC

Total HM: 32

Participants: 141

Period: 7 days

Judge: HardlyDifficult

Total Solo HM: 4

Id: 144

League: ETH

Fractional

Findings Distribution

Researcher Performance

Rank: 18/141

Findings: 8

Award: $1,216.93

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: kenzo

Also found by: 0x1f8b, bin2chen, codexploder, dipp, minhtrng, smiling_heretic

Labels

bug
duplicate
3 (High Risk)

Awards

339.95 USDC - $339.95

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L105

Vulnerability details

Impact

User can deposit funds by joining a non existent proposal id. Once the proposal with this id is created, user would have joined a proposal which they might not have wanted

Proof of Concept

  1. User accidentally provides proposal id 2 which is non existent, for Vault A while calling join function
  2. No error is thrown and User funds are deposited for this non existent proposal
  3. This becomes a problem once another user creates proposal id 2 for Vault A and commit it
  4. Now User from Step 1 has deposited to a proposal in which he never wanted (proposal 1 and 2 on same vault may differ on price)

Add below check to ensure that proposal actually exists

require(proposal.startTime>0, "No proposal found");

#0 - stevennevins

2022-07-20T18:34:41Z

Duplicate of #208

#1 - HardlyDifficult

2022-07-28T21:33:15Z

Findings Information

🌟 Selected for report: xiaoming90

Also found by: 0x52, Lambda, cccz, codexploder, hyh, kenzo, oyc_109, zzzitron

Labels

bug
duplicate
3 (High Risk)

Awards

214.1685 USDC - $214.17

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L334

Vulnerability details

Impact

migrateVaultERC20/migrateVaultERC721 can lead to loss of all assets held by Vault

Proof of Concept

  1. User starts a migration proposal
  2. After PROPOSAL_PERIOD, proposal.targetPrice is achieved. Hence buyout process is started
  3. Auction ends and buyout was success
  4. Before User could call settleVault to set up the new vault, Attacker calls migrateVaultERC20/migrateVaultERC721/.. which migrates the tokens/nft to migrationInfo[_vault][_proposalId].newVault which is currently address(0)
  5. Hence all tokens/assets are transferred to address 0 and are lost forever

Add below check which make sure that Vault has been settled before migration

require(migrationInfo[_vault][_proposalId].newVault!=address(0),"Vault not settled");

#0 - stevennevins

2022-07-20T15:26:09Z

#1 - HardlyDifficult

2022-08-02T23:53:30Z

Findings Information

Labels

bug
duplicate
3 (High Risk)

Awards

97.2803 USDC - $97.28

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L72

Vulnerability details

Impact

User funds can stuck in case where multiple proposal exist on same Vault

Proof of Concept

  1. User starts proposal id 1 vault A
  2. User B joins this proposal id 1 and invests in it
  3. User starts proposal id 2 vault A
  4. User C joins this proposal id 2 and invests in it
  5. Proposal id 2 is committed and buyout is success and migration gets success
  6. Since migration is success User B cannot use withdraw and his funds get stuck

In withdraw function, allow user to withdraw fund if proposal was not committed. In short, proposal was not committed or proposal was inactive - then allow withdraw

#0 - stevennevins

2022-07-20T16:46:54Z

#1 - HardlyDifficult

2022-08-11T16:31:53Z

Findings Information

🌟 Selected for report: hansfriese

Also found by: 0x52, codexploder

Labels

bug
duplicate
2 (Med Risk)

Awards

362.6962 USDC - $362.70

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L105

Vulnerability details

Impact

User should not be allowed to join/leave a proposal if block.timestamp > proposal.startTime + PROPOSAL_PERIOD

Proof of Concept

  1. Observe the join function
  2. Notice that user can join as long as commit is not being called
  3. This means if commit is not called early then user can even join post proposal.startTime + PROPOSAL_PERIOD which is incorrect

Add below check to revert if proposal end time has reached

require(block.timestamp <= proposal.startTime + PROPOSAL_PERIOD, "Time has ended");

#0 - stevennevins

2022-07-22T10:18:12Z

Duplicate of #250

Findings Information

🌟 Selected for report: bbrho

Also found by: 0xNazgul, Saintcode_, codexploder, infosec_us_team, s3cunda, zzzitron

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed

Awards

101.985 USDC - $101.98

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/Vault.sol#L38

Vulnerability details

Impact

The Merkle proof check can be bypassed by using fallback function which allows anyone to call _execute function (missing ACL checks). This allows attacker to execute plugins without caller being a module with permission to call or the owner.

Proof of Concept

  1. User A makes an interaction (non existing method) such that fallback function on Vault.sol#L38 is invoked
  2. User A has passed msg.sig as a valid plugin
  3. _execute function is called which misses any ACL checks over the user passed plugin

Instead of calling_execute, call to execute should be made including proof sent by user

#0 - stevennevins

2022-07-19T15:45:08Z

We're confirming these issues around malicious owners exploiting delegation via the fallback and execute functions. The ownership of vaults isn't intended to be EOAs (in most situations) and we limited ownership access to installing plugins/delegation(as owner) at the protoform level, which we think could be more clear or additional limitations created in hindsight.

#1 - HardlyDifficult

2022-08-11T18:29:39Z

Awards

1.3977 USDC - $1.40

Labels

bug
duplicate
2 (Med Risk)

External Links

Lines of code

https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L172

Vulnerability details

Impact

The transfer function is bounded by 2300 gas limit. But some contract might need more than this gas which can lead to transfer failing

Proof of Concept

  1. Observe that leave function makes use of transfer to send eth back to caller instead of using call

Use call instead of transfer

(bool success, ) = msg.sender.call{ethAmount}(""); require(success, "Transfer failed.");

#0 - 0x0aa0

2022-07-21T17:09:11Z

Duplicate of #325

#1 - HardlyDifficult

2022-07-28T15:49:10Z

Duping to #504

Plugin and Selector length are not matched

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/VaultRegistry.sol#L165

Issue: In _deployVault function, it is not verified that length for _plugins and _selectors is same which means the loop will run incorrect number of times

Recommendation: Add below check

require(_plugins.length==_selectors.length, "Incorrect length");

Missing zero address check

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/Vault.sol#L93

Issue: In transferOwnership function, add a zero address check otherwise this vault becomes useless as no plugins can now be installed

Recommendation: Add below check:

require(_newOwner!=address(0), "zero address");

Amount not given back to user post burn

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Buyout.sol#L278

Issue: redeem function is burning the totalSupply from vault but is not transferring the amount back to caller

Reverting early can save gas

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/FERC1155.sol#L241

Recommendation: royaltyInfo function: Revert if royaltyAddress[_id] is address(0) as in this case Royalty is not defined

Use pre increment operator

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/FERC1155.sol#L339 https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/FERC1155.sol#L363

Recommendation: _computePermitStructHash Function : Change to ++nonces[_owner] instead of nonces[_owner]++

Use Immutable to save gas

Contract: https://github.com/code-423n4/2022-07-fractional/blob/8f2697ae727c60c93ea47276f8fa128369abfe51/src/constants/Permit.sol#L10

Recommendation: PERMIT_TYPEHASH : Assigned operations to constant variables are re-evaluated every time.Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

Same need to be done for other variables like DOMAIN_TYPEHASH etc

No need to compute length

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/protoforms/BaseVault.sol#L131

Recommendation: generateMerkleTree Function: Max leaves length allowed is 6. Instead of running the loop till leaves length, its better to check that length is within range

require(leaves<=6,"Incorrect length");

Committed proposal - no need to go forward

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Migration.sol#L179

Recommendation: commit Function : If proposal is already committed then no need for going forward

require(!proposal.isCommited, "Proposal already committed");

No need to compute length

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Buyout.sol#L454

Recommendation: getLeafNodes Function: We already know permissions.length is always 5 so array can be revised accordingly

for (uint256 i; i < 5; )

Use local scope

Contract: https://github.com/code-423n4/2022-07-fractional/blob/e2c5a962a94106f9495eb96769d7f60f7d5b14c9/src/modules/Buyout.sol#L351-L358

Recommendation: withdrawERC721 Function: Line 351-358 for Buyout.sol can be placed in local scope to avoid stack too deep. Same can be done for withdrawERC20

{ (, uint256 id) = IVaultRegistry(registry).vaultToToken(_vault); if (id == 0) revert NotVault(_vault); // Reverts if auction state is not successful (, address proposer, State current, , , ) = this.buyoutInfo(_vault); State required = State.SUCCESS; if (current != required) revert InvalidState(required, current); // Reverts if caller is not the auction winner if (msg.sender != proposer) revert NotWinner(); }
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