Project

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

A malicious DAO user can steal profits from other users

Summary

A malicious user can abuse the 'sendProfit' function to inflate his profit and later claim more tokens than he should be allowed.

Vulnerability Details

First of all, we should understand how the claiming profit system works.

At the beginning you have to send a certain amount of tokens to the contract:

function sendProfit(uint256 amount) external {
uint256 _totalSupply = totalSupply;
if (_totalSupply > 0) {
totalProfit += (amount * ACCURACY) / _totalSupply;
IERC20(currency).safeTransferFrom(msg.sender, address(this), amount);
emit Profit(amount);
} else {
IERC20(currency).safeTransferFrom(msg.sender, creator, amount);
}
}

Note that there is no time limit or user roles to call this function.

Once some tokens are in the contract, users can call claimProfit to get them:

function claimProfit() external returns (uint256 profit) {
profit = saveProfit(msg.sender);
require(profit > 0, "No profit available");
savedProfit[msg.sender] = 0;
IERC20(currency).safeTransfer(msg.sender, profit);
emit Claim(msg.sender, profit);
}

Here it calculates the current profit for user and save it:

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) {
return (balanceOf(account, 0) * 64) +
(balanceOf(account, 1) * 32) +
(balanceOf(account, 2) * 16) +
(balanceOf(account, 3) * 8) +
(balanceOf(account, 4) * 4) +
(balanceOf(account, 5) * 2) +
balanceOf(account, 6);
}

saveProfit is an internal function that can be call only when claiming the profit or transfering NFT as it forward a call to _update:

function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual override(ERC1155Upgradeable) {
if (from != address(0)) saveProfit(from);
if (to != address(0)) saveProfit(to);
super._update(from, to, ids, amounts);
}

So here is what can be done to steal other user profits.

Before caliming a profit, a malicious user:

  1. Transfer a NFT to your own address or any other under your control to trigger the saveProfit in _Update. Now his profit is equal to totalProfit, or the difference between the last claim and the current one.

  2. Then he calls sendProfit with a small amount just to update the totalProfit value.

  3. And he makes a NFT transfer again to trigger saveProfit. But instead of a 0 value return he will get more tokens:

return ((totalProfit - lastProfit[account]) * shareOf(account)) / ACCURACY;

this small amount will be added to his savedProfit[account]

If he does this several times, he can inflate his profit to the max amount allowed, and claim all tokens stealing from others.

Impact

A malicious DAO user can steal profits from other users

Tools Used

Manual review

Recommendations

Consider adding a check for min amount to send to the contract as a profit for users.

Updates

Lead Judging Commences

0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!