30860 - [SC - Critical] Wrong timestamp for totalVoting
Submitted on May 7th 2024 at 04:17:29 UTC by @cryptoticky for Boost | Alchemix
Report ID: #30860
Report type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/Bribe.sol
Impacts:
Theft of unclaimed yield
Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results
Description
Brief/Intro
The attacker can change totalVoting at the first second of a new epoch and steal all reward for an epoch.
Vulnerability Details
/// @inheritdoc IBribe
function earned(address token, uint256 tokenId) public view returns (uint256) {
...
Checkpoint memory cp = checkpoints[tokenId][_endIndex];
uint256 _lastEpochStart = _bribeStart(cp.timestamp);
uint256 _lastEpochEnd = _lastEpochStart + DURATION;
uint256 _priorSupply = votingCheckpoints[getPriorVotingIndex(_lastEpochEnd)].votes;
// Prevent divide by zero
if (_priorSupply == 0) {
_priorSupply = 1;
}
if (block.timestamp > _lastEpochEnd) {
reward += (cp.balanceOf * tokenRewardsPerEpoch[token][_lastEpochStart]) / _priorSupply;
}
return reward;
}If an attacker call Voter.distribute at _lastEpochStart + DURATION, then Bribe.resetVoting function is triggered and totalVoting is updated to 0. And then if the attacker call Voter.vote function the same params (or tokenId with less votingPower), then Bribe._writeVotingCheckpoint is triggered and votingCheckpoints[getPriorVotingIndex(_lastEpochEnd)].votes is updated with the new votes. And the attacker claim the reward at the next block before other users claim. The smaller the new vote amount, the more rewards the attacker can steal.
Impact Details
The attacker can steal reward and other users can't claim the reward.
Recommandation
Bribe.sol: 268 line
to
Proof of Concept
Last updated
Was this helpful?