Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Royalty owner can steal Stakeholders fees #10

Closed
code423n4 opened this issue Feb 10, 2022 · 3 comments
Closed

Royalty owner can steal Stakeholders fees #10

code423n4 opened this issue Feb 10, 2022 · 3 comments
Assignees
Labels
bug Something isn't working duplicate This issue or pull request already exists QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2022-02-nested/blob/main/contracts/FeeSplitter.sol#L134-L140

Vulnerability details

Impact

Under certain circumstances, Royalty owner can steal all fees funds which were meant for Stakeholders

Proof of Concept

  1. Navigate to updateShareholder function in FeeSplitter.sol#L134-L140 and observe that there is no check to validate if newly updated weight!=0

  2. This can have disaster impact as shown below

  3. Assume owner has set one stakeholder X with the weight 1 and Royalty weight as 1

  4. Owner uses the updateShareholder function and sets the stakeholder X weight to 0, which is success

  5. This makes the final new totalWeights as 1 (Only Royalty weight)

  6. Factory calls the sendFees function with amount 1000.

function sendFees(IERC20 _token, uint256 _amount) external nonReentrant {
        uint256 weights;
        unchecked {
            weights = totalWeights - royaltiesWeight;
        }

        uint256 balanceBeforeTransfer = _token.balanceOf(address(this));
        _token.safeTransferFrom(_msgSender(), address(this), _amount);

        _sendFees(_token, _token.balanceOf(address(this)) - balanceBeforeTransfer, weights);
    }
  1. Since both totalWeights and royaltiesWeight is 1 so weight becomes 1-1 =0

  2. Amount 1000 is transffered to this contract

  3. _sendFees does nothing as stakeholder weight is 0 and also weights passed is 0

  4. Finally contract has balance fees with no stakeholder able to claim

  5. Now Factory owner calls sendFeesWithRoyalties with amount 100

function sendFeesWithRoyalties(
        address _royaltiesTarget,
        IERC20 _token,
        uint256 _amount
    ) external nonReentrant {
        require(_royaltiesTarget != address(0), "FS: INVALID_ROYALTIES_TARGET");

        uint256 balanceBeforeTransfer = _token.balanceOf(address(this));
        _token.safeTransferFrom(_msgSender(), address(this), _amount);
        uint256 amountReceived = _token.balanceOf(address(this)) - balanceBeforeTransfer;

        uint256 royaltiesAmount = _computeShareCount(amountReceived, royaltiesWeight, totalWeights);

        _sendFees(_token, amountReceived, totalWeights);
        _addShares(_royaltiesTarget, royaltiesAmount, address(_token));

        emit RoyaltiesReceived(_royaltiesTarget, address(_token), royaltiesAmount);
    }
  1. Since totalWeights is equal to Royalty weight so _computeShareCount gives full shares of 100 to Royalty owner which also sets _tokenRecords.totalShares to 100 at _addShares function

  2. Now Royalty owner has to simply call releaseToken which computes the amount using getAmountDue. Now getAmountDue considers full contract balance which currently is 1000+100=1100

function getAmountDue(address _account, IERC20 _token) public view returns (uint256) {
        TokenRecords storage _tokenRecords = tokenRecords[address(_token)];
        if (_tokenRecords.totalShares == 0) return 0;

        uint256 totalReceived = _tokenRecords.totalReleased + _token.balanceOf(address(this));
        return
            (totalReceived * _tokenRecords.shares[_account]) /
            _tokenRecords.totalShares -
            _tokenRecords.released[_account];
    }
  1. This means the amount received will be 1100 as shown below
totalReceived=1000+100=1100
returns 1100*100/100-0=1100
  1. As we can see here, Royalty owner was able to obtain 1100 amount when only 100 amount was meant for him and rest 1000 was only meant for stakeholder who might get added later by owner

Recommended Mitigation Steps

Add a new check in updateShareholder which does not allows the updated weight to be 0

function updateShareholder(uint256 _accountIndex, uint96 _weight) external onlyOwner {
        require(_weight!=0, "Incorrect weight assigned");
.....
    }
@code423n4 code423n4 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Feb 10, 2022
code423n4 added a commit that referenced this issue Feb 10, 2022
@maximebrugel maximebrugel added the disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) label Feb 10, 2022
@maximebrugel
Copy link
Collaborator

Bad owner manipulation. But we must check that.

@harleythedogC4
Copy link
Collaborator

As stated in the contest, the contracts will be owned by a multisig behind a 7 day timelock, so there is no actual threat here. However, this finding does have some value since the 0 check does appear in setRoyaltiesWeight and _addShareholder so it makes sense to add it to updateShareholder for consistency. So I am changing this to low severity.

@harleythedogC4 harleythedogC4 added QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 3 (High Risk) Assets can be stolen/lost/compromised directly disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) labels Feb 27, 2022
@harleythedogC4
Copy link
Collaborator

Addressed this in the QA report #9. Closing here.

@CloudEllie CloudEllie added the duplicate This issue or pull request already exists label Mar 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working duplicate This issue or pull request already exists QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
Projects
None yet
Development

No branches or pull requests

4 participants