Lucene search

K
code423n4Code4renaCODE423N4:2023-05-MAIA-FINDINGS-ISSUES-856
HistoryJul 05, 2023 - 12:00 a.m.

setBooster() function may be used to steal unclaimed rewards in FlywheelCore contract

2023-07-0500:00:00
Code4rena
github.com
9
flywheelcore
malicious owner
unclaimed rewards
rug vector
trustworthiness
claimrewards()
setbooster()
theft
vulnerability

Lines of code

Vulnerability details

Lines of code

Vulnerability details

Impact

A malicious owner can steal all unclaimed rewards and break the reward accounting mechanism

Proof of Concept

Even if the owner is a good guy but the fact that there exists a rug vector available may negatively impact the protocolโ€™s reputation. Or maybe the hotkeys of a multi-sig wallet may be stolen. Furthermore, since this contract is meant to be used by other projects, the trustworthiness of every project cannot be vouched for.

The problem lies in the fact that the flywheelRewards is not immutable. Letโ€™s check a hypothetical process a malicious owner can take.
The boostedBalanceOf() function is used to calculate the boosted balance of a user in a given strategy. By creating and setting a booster contract that returns zero when users call boostedBalanceOf() in a situation where the user address is not under the attackerโ€™s control, and returning arbitrary values for those under his/her control, an attacker can choose specific amounts of rewardToken to assign to himself/herself. The attacker can then call claimRewards() to withdraw the funds. Any amounts that the attacker assigns to himself/herself over the amount that normally would have been assigned, upon claiming, is taken from other usersโ€™ unclaimed balances since tokens are custodied by the flywheelRewards address rather than per-user accounts.

    function setBooster(IFlywheelBooster newBooster) external onlyOwner {
        flywheelBooster = newBooster;

        emit FlywheelBoosterUpdate(address(newBooster));
    }

In the function accrueUser() we have the supplierToken variable defined in a way that calls boostedBalanceOf():

    function accrueUser(ERC20 strategy, address user, uint256 index) private returns (uint256) {
        // load indices
        uint256 supplierIndex = userIndex[strategy][user];

        // sync user index to global
        userIndex[strategy][user] = index;

        // if user hasn't yet accrued rewards, grant them interest from the strategy beginning if they have a balance
        // zero balances will have no effect other than syncing to global index
        if (supplierIndex == 0) {
            supplierIndex = ONE;
        }

        uint256 deltaIndex = index - supplierIndex;
        // use the booster or token balance to calculate reward balance multiplier
        uint256 supplierTokens = address(flywheelBooster) != address(0)
            ? flywheelBooster.boostedBalanceOf(strategy, user)
            : strategy.balanceOf(user);

        // accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimed
        uint256 supplierDelta = (supplierTokens * deltaIndex) / ONE;
        uint256 supplierAccrued = rewardsAccrued[user] + supplierDelta;

        rewardsAccrued[user] = supplierAccrued;

        emit AccrueRewards(strategy, user, supplierDelta, index);

        return supplierAccrued;
    }

Finally, the function claimRewards() is defined in the preceding way:

    function claimRewards(address user) external {
        uint256 accrued = rewardsAccrued[user];

        if (accrued != 0) {
            rewardsAccrued[user] = 0;

            rewardToken.safeTransferFrom(address(flywheelRewards), user, accrued);

            emit ClaimRewards(user, accrued);
        }
    }

Tools Used

Manual Review
<https://twitter.com/RugDocIO/status/1411732108029181960&gt;
<https://twitter.com/Mudit__Gupta/status/1675584195798913024&gt;

Recommended Mitigation Steps

Make flywheelRewards address immutable, or only allow it to change if there are no current users

Assessed type

Decimal


The text was updated successfully, but these errors were encountered:

All reactions