29262 - [SC - Insight] Some users can get more rewards than others whi...
Submitted on Mar 12th 2024 at 12:49:20 UTC by @oxumarkhatab for Boost | ZeroLend
Report ID: #29262
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/zerolend/governance
Impacts:
Theft of unclaimed yield
Description
Brief/Intro
The notifyReward Allows the reward amount for a specific token for multiple times in 14 days Duration. This opens up a weakness of the system using which an attacker can earn more rewards for exact same time as others.
Why this impact?
Essentially the attacker can get more of the yield than other users. So Attacker is Thefting the yield ( Just in my opinion ) This is the most relevant impact I found so consider it.
Vulnerability Details
This Vulnerability details section contains the PoC itself too because the only way to show this weakness is to crunch the numbers before you.
In short , if a user has tokens for earning rewards at the time the notifyReward is called , they will get less tokens than a person who stakes tokens at the time after the second notifyReward function call is made to the GuageIncentiveController contract within 14 days and not after 14 days of initial call because second call in the interval [0,14 days) allows setting greater rewardRate for a token ( same amount passed in function call ) that will allow the attacker to gain more rewards after the second notifyReward function call
than other people who has staked before the second notifyReward function call
The main lines of concern are setting the rewardRate lines
and
Let's see this in action
PoC
The rewards calculation for GuageIncentiveController is unfair
LEt's say just after the notifyReward function call , Alice has get's her 100e18 of tokens from somewhere and willing to stake for one day.
LEt's say the notifyReward amount is 10e18
And the periodFinish is set to block.timestamp+Deadline
She calculates the reward and gets some tokens on A
amount of tokens on the rate of 10e18/14 days = 8.2671958e+12
=> rewardRate = 8e12
Now some time passes , as the Duration is 14 days long , a malicious actor Bob can monitor the mempool for notifyReward for the next notifyReward call within next 13.9 days.
When the notifyReward is called ( Let's say for the amount is same as 10e18 ) , block.timestamp < PeriodFinish clearly , so first condition is falsified
The second condition is executed
Let's say notifyReward is called after one day , _remaining = 13 days = 86400*13 = 1,123,200 _left = 1,123,200 * 8e12 = 9e18
clearly , amount > _left , 10e18 > 9e18 so following condition does not execute and new rate is set
Carefully see how the rate is being set:
rewardRate[token] = (10e18+9e18) / DURATION = 19e18/14 days = 2.1990741e+14 = 2e14
Now when Bob stakes tokens , he gets far more tokens than alice
See 8e12 < 2e14 , This amount is substantially large when accumulate with each passing moment.
so using a simpler formula of elapsedTime and rewardPerToken ( the protocol does slightly different but essense is same)
Alice keeps tokens for 1 day = 86400 seconds :
Alice's rewards = (86400*8e12) = 6.912e+17 = Solidity integer rounding down = 6e17
Bob's keeps tokens for 1 day = 86400 seconds :
Bob's rewards = (86400*2e14) = 1.728e+19 = Solidity integer rounding down = 1e19
Thus Bob is able to get more rewards for the same time as Alice's. This is a source of unfair distribution.
Impact Details
Unfair rewards distribution between users
Theft of rewards
Proof of concept
Disclaimer: This is essentially the same PoC in Vulnerability Details section. If you've read that, you can safely ignore this one
PoC
The rewards calculation for GuageIncentiveController is unfair
LEt's say just after the notifyReward function call , Alice has get's her 100e18 of tokens from somewhere and willing to stake for one day.
LEt's say the notifyReward amount is 10e18
And the periodFinish is set to block.timestamp+Deadline
She calculates the reward and gets some tokens on A
amount of tokens on the rate of 10e18/14 days = 8.2671958e+12
=> rewardRate = 8e12
Now some time passes , as the Duration is 14 days long , a malicious actor Bob can monitor the mempool for notifyReward for the next notifyReward call within next 13.9 days.
When the notifyReward is called ( Let's say for the amount is same as 10e18 ) , block.timestamp < PeriodFinish clearly , so first condition is falsified
The second condition is executed
Let's say notifyReward is called after one day , _remaining = 13 days = 86400*13 = 1,123,200 _left = 1,123,200 * 8e12 = 9e18
clearly , amount > _left , 10e18 > 9e18 so following condition does not execute and new rate is set
Carefully see how the rate is being set:
rewardRate[token] = (10e18+9e18) / DURATION = 19e18/14 days = 2.1990741e+14 = 2e14
Now when Bob stakes tokens , he gets far more tokens than alice
See 8e12 < 2e14 , This amount is substantially large when accumulate with each passing moment.
so using a simpler formula of elapsedTime and rewardPerToken ( the protocol does slightly different but essense is same)
Alice keeps tokens for 1 day = 86400 seconds :
Alice's rewards = (86400*8e12) = 6.912e+17 = Solidity integer rounding down = 6e17
Bob's keeps tokens for 1 day = 86400 seconds :
Bob's rewards = (86400*2e14) = 1.728e+19 = Solidity integer rounding down = 1e19
Thus Bob is able to get more rewards for the same time as Alice's. This is a source of unfair distribution.
Last updated