30671 - [SC - Critical] Reward token permanent freeze due to bulk call ...

Submitted on May 4th 2024 at 00:49:59 UTC by @cryptoticky for Boost | Alchemix

Report ID: #30671

Report type: Smart Contract

Report severity: Critical

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/Voter.sol

Impacts:

  • Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results

  • Permanent freezing of unclaimed yield

Description

Brief/Intro

The poke function facilitates users to vote with the same weight for each pool in each epoch easily. The problem is that this function does not use a onlyNewEpoch modifier. As a result, an attacker could potentially call this function hundreds of times within a single epoch, and the totalVoting of bribe contract does not accurately track such actions.

Vulnerability Details

Voting.poke function doesn't use onlyNewEpoch modifier

    /// @inheritdoc IVoter
    function poke(uint256 _tokenId) public {
        // Previous boost will be taken into account with weights being pulled from the votes mapping
        uint256 _boost = 0;

        if (msg.sender != admin) {
            require(IVotingEscrow(veALCX).isApprovedOrOwner(msg.sender, _tokenId), "not approved or owner");
        }

        address[] memory _poolVote = poolVote[_tokenId];
        uint256 _poolCnt = _poolVote.length;
        uint256[] memory _weights = new uint256[](_poolCnt);

        for (uint256 i = 0; i < _poolCnt; i++) {
            _weights[i] = votes[_tokenId][_poolVote[i]];
        }

        _vote(_tokenId, _poolVote, _weights, _boost);
    }

_vote function call _reset function and the _reset function call withdraw of Bribe contract.

As you can see, in Bribe.withdraw function, totalVoting is not calcutated. In the end, totalVoting only keeps increasing.

The totalVoting is used to calculate reward amount of a tokenId.

Impact Details

  • Users end up receiving less rewards than what the actual voting results would entitle them to.

  • The remaining reward amount is locked forever.

Unfortunately, the bribe contract does not have a function to withdraw this remaining amount.

Proof of Concept

Last updated

Was this helpful?