Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Valid

Users might lose funds as a result of chain reorganizations.

Summary

Users may inadvertently join the wrong DAO due to a chain reorganization, resulting in the loss of entry fees.

Vulnerability Details

A DAO membership can be created by calling the createNewDAOMembership function:

function createNewDAOMembership(DAOInputConfig calldata daoConfig, TierConfig[] calldata tierConfigs)
external returns (address) {
require(currencyManager.isCurrencyWhitelisted(daoConfig.currency), "Currency not accepted.");
require(daoConfig.noOfTiers == tierConfigs.length, "Invalid tier input.");
require(daoConfig.noOfTiers > 0 && daoConfig.noOfTiers <= TIER_MAX, "Invalid tier count.");
require(getENSAddress[daoConfig.ensname] == address(0), "DAO already exist.");
if (daoConfig.daoType == DAOType.SPONSORED) {
require(daoConfig.noOfTiers == TIER_MAX, "Invalid tier count for sponsored.");
}
// Enforce maxMembers
uint256 totalMembers = 0;
for (uint256 i = 0; i < tierConfigs.length; i++) {
totalMembers += tierConfigs[i].amount;
}
require(totalMembers <= daoConfig.maxMembers, "Sum of tier amounts exceeds maxMembers.");
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
membershipImplementation,
address(proxyAdmin),
abi.encodeWithSignature("initialize(string,string,string,address,address)", daoConfig.ensname, "OWP", baseURI, _msgSender(), daoConfig.currency)
);
DAOConfig storage dao = daos[address(proxy)];
dao.ensname = daoConfig.ensname;
dao.daoType = daoConfig.daoType;
dao.currency = daoConfig.currency;
dao.maxMembers = daoConfig.maxMembers;
dao.noOfTiers = daoConfig.noOfTiers;
for (uint256 i = 0; i < tierConfigs.length; i++) {
require(tierConfigs[i].minted == 0, "Invalid tier config");
dao.tiers.push(tierConfigs[i]);
}
getENSAddress[daoConfig.ensname] = address(proxy);
userCreatedDAOs[_msgSender()][daoConfig.ensname] = address(proxy);
emit MembershipDAONFTCreated(daoConfig.ensname, address(proxy), dao);
return address(proxy);
}

Observations:

From the above code, it is evident that the DAO membership is implemented as a TransparentUpgradeableProxy deployed by the MembershipFactory contract. Since the deployment uses the new keyword, the address derivation relies solely on the factory’s nonce.

Attack Scenario:

  1. Alice Creates a DAO Membership:
    Alice creates a DAO membership, resulting in the deployment of a DAO at address 0xabc.

  2. Bob Attempts to Join Alice’s DAO:
    Bob intends to join Alice’s DAO by calling the joinDAO function with daoMembershipAddress set to 0xabc and paying the required entry fee.

  3. Reorganization and Exploitation:
    During a blockchain reorganization:

    • Charlie observes Bob’s pending transaction.

    • Charlie quickly creates a new DAO membership using the MembershipFactory identical to Alice’s DAO.

    • Since the DAO address derivation depends solely on the factory's nonce, Charlie’s DAO is deployed at the same address 0xabc.

  4. Redirection of Bob’s Payment:
    After the reorg, when Bob’s transaction is confirmed, it interacts with Charlie’s DAO instead of Alice’s. Consequently:

    • Bob's payment and membership are directed to Charlie’s DAO.

Impact

Loss of funds for users: Users will lose the platform fee and the tier price. The impact becomes significantly higher if the user approves the protocol with an allowance of type(uint256).max.

Tools Used

Manual Review

Recommendations

use CREATE2 for deploying the proxy

Updates

Lead Judging Commences

0xbrivan2 Lead Judge
9 months ago
0xbrivan2 Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

reorg issues

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.