Project

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

Incorrect Token Weight Calculation in Burn Functions Leads to Supply Manipulation

Summary

MembershipERC1155 contract implements a tiered membership system where each tier has a different weight for profit distribution. The vulnerability lies in the burn functions where token weights are calculated using unchecked arithmetic operations, allowing manipulation of the total supply calculation.

Th affected function in MembershipERC1155.sol: https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/tokens/MembershipERC1155.sol#L73-L76

function burn_(address from, uint256 tokenId, uint256 amount) internal {
totalSupply -= amount * 2 ** (6 - tokenId); // @FOUND - Unchecked arithmetic in weight calculation
_burn(from, tokenId, amount);
}

Vulnerability Details

In the burn functions where token weights are calculated incorrectly, allowing manipulation of the total supply and profit distribution system.

  1. The burn_ function performs unchecked arithmetic that can underflow when tokenId > 6

  2. The weight calculation 2 ** (6 - tokenId) assumes tokenId ≤ 6 but doesn't enforce it

  3. burnBatch iterates through tokens 0-6 without validating if those token tiers exist for the DAO

In MembershipERC1155.sol: https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/tokens/MembershipERC1155.sol#L73-L87

function burn_(address from, uint256 tokenId, uint256 amount) internal {
totalSupply -= amount * 2 ** (6 - tokenId); // @FOUND - Unchecked arithmetic allows supply manipulation through underflow
// @FOUND - Arithmetic underflow when tokenId > 6 leads to incorrect total supply
_burn(from, tokenId, amount);
}
function burnBatch(address from) public onlyRole(OWP_FACTORY_ROLE) {
for (uint256 i = 0; i < 7; ++i) { // @FOUND - No validation of token existence or tier bounds
uint256 amount = balanceOf(from, i);
if (amount > 0) {
burn_(from, i, amount);
}
}
}

Attack Scenario

  1. Alice creates a DAO with 3 tiers

  2. Bob joins the DAO and receives tokens

  3. Bob calls burn with tokenId = 7

  4. The calculation 6 - tokenId underflows, resulting in incorrect weight

  5. Total supply becomes corrupted, affecting profit distribution

Proof of Concept

  1. Call burn with tokenId = 7, amount = 1

  2. This causes 6 - 7 to underflow

  3. Results in incorrect totalSupply calculation

  1. Initial Setup:

// Attacker creates or joins a DAO
membershipContract.mint(attacker, 0, 1);
// Step 1: Exploit weight calculation
membershipContract.burn(attacker, 7, 1); // Uses invalid tokenId
// Step 2: Profit distribution manipulation
membershipContract.sendProfit(1000); // Profits are now incorrectly distributed

Here's how an attacker would create or join a DAO:

To Create a DAO:

  1. Call createNewDAOMembership in MembershipFactory with: https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/MembershipFactory.sol#L65-L93

DAOInputConfig memory config = DAOInputConfig({
ensname: "attackerDAO",
daoType: DAOType.SPONSORED, // or NORMAL
currency: whitelistedCurrency, // Must be whitelisted in CurrencyManager
maxMembers: 100,
noOfTiers: 7 // For SPONSORED type
});
TierConfig[] memory tiers = new TierConfig[]();
// Set up tier configurations

To Join an Existing DAO:

  1. Approve USDC/token spending to MembershipFactory

  2. Call joinDAO function: https://github.com/Cyfrin/2024-11-one-world/blob/1e872c7ab393c380010a507398d4b4caca1ae32b/contracts/dao/MembershipFactory.sol#L140-L150

membershipFactory.joinDAO(
daoMembershipAddress,
tierIndex // 0-6 depending on desired tier
);

DAO members are not trusted parties of the protocol. They are regular users who have purchased membership NFTs through the joinDAO function in MembershipFactory.

Looking at the codebase:

  1. In MembershipFactory.sol:

function joinDAO(address daoMembershipAddress, uint256 tierIndex) external {
// Anyone can join by paying the required amount
// No special trust assumptions
}

2. In MembershipERC1155.sol, DAO members only have standard ERC1155 token holder capabilities:

  • Can hold tokens

  • Can transfer tokens

  • Can claim profits

  • Cannot mint or burn tokens (restricted to OWP_FACTORY_ROLE)

The only privileged roles in the system are:

  • DEFAULT_ADMIN_ROLE

  • OWP_FACTORY_ROLE

  • DAO_CREATOR

Regular DAO members do not receive any special permissions or trusted status within the protocol's security model.

Impact

  1. Economic Impact:

  • Incorrect total supply tracking affects profit distribution calculations

  • Token weights become corrupted, leading to unfair profit sharing

  • Users with higher tier tokens could lose their proper profit share allocation

  1. System Integrity:

  • Violates core invariant that burning should decrease total supply

  • Compromises the tier-based membership system

  • Could allow exploitation of profit distribution mechanism

The vulnerability directly impacts the economic model of the DAO membership system and its profit-sharing mechanism, requiring immediate attention to prevent potential exploitation.

Recommendations

Add validation for tokenId bounds and ensures burning only occurs for valid tiers within initialized DAOs

function burn_(address from, uint256 tokenId, uint256 amount) internal {
+ require(tokenId <= 6, "Invalid token ID");
+ require(amount > 0, "Invalid amount");
totalSupply -= amount * 2 ** (6 - tokenId);
_burn(from, tokenId, amount);
}
function burnBatch(address from) public onlyRole(OWP_FACTORY_ROLE) {
+ require(daos[msg.sender].noOfTiers > 0, "DAO not initialized");
for (uint256 i = 0; i < daos[msg.sender].noOfTiers; ++i) {
uint256 amount = balanceOf(from, i);
if (amount > 0) {
burn_(from, i, amount);
}
}
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
0xbrivan2 Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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