When rewardRate or rewardPerToken is increased, the attacker runs the stake token forward, The attacker gains a reward after the rewardPerToken is increased, and finally stakes the token, the attacker loses nothing and contributes nothing to the protocol, but gains a reward.
Root Cause
An attacker can immediately unstake(withdraw) after a stake token:
function earned(address account) public view returns (uint256) {
return (_balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];
}
The attacker has a stake before the rewardPerToken is increased, and userRewardPerTokenPaid is the old value, so the user can get the reward after the increase.
notifyRewardAmount assigns rewards based on time, but this does not prevent sandwich attack:
If periodFinish is updated with a long interval, the increment of rewardPerToken after rewardRate is added is:
deltaRewardRate = rewardRate * UpdateTimeInterval
After rewardRate is updated, but the interval is shorter, the value of 'rewardPerToken' is increased less, and the attacker can get less reward at this time,
But attackers can wait for the right moment to strike, If lastUpdateTime is not updated after a long time, the attacker finds that another user calls lastUpdateTime(or triggers itself), and the time difference is large and the increment of rewardPerToken is large, the attacker can implement the sandwich attack again.
zhoo
Medium
The StakingRewards will be sandwich attacked
Summary
When
rewardRate
orrewardPerToken
is increased, the attacker runs thestake token
forward, The attacker gains a reward after therewardPerToken
is increased, and finally stakes the token, the attacker loses nothing and contributes nothing to the protocol, but gains a reward.Root Cause
https://github.com/sherlock-audit/2024-06-makerdao-endgame/blob/dba30d7a676c20dfed3bda8c52fd6702e2e85bb1/endgame-toolkit/src/synthetix/StakingRewards.sol#L102-L121
The attacker has a stake before the
rewardPerToken
is increased, anduserRewardPerTokenPaid
is the old value, so the user can get the reward after the increase.notifyRewardAmount
assigns rewards based on time, but this does not prevent sandwich attack: IfperiodFinish
is updated with a long interval, the increment ofrewardPerToken
afterrewardRate
is added is: deltaRewardRate = rewardRate * UpdateTimeIntervalAfter rewardRate is updated, but the interval is shorter, the value of 'rewardPerToken' is increased less, and the attacker can get less reward at this time,
But attackers can wait for the right moment to strike, If
lastUpdateTime
is not updated after a long time, the attacker finds that another user callslastUpdateTime
(or triggers itself), and the time difference is large and the increment ofrewardPerToken
is large, the attacker can implement the sandwich attack again.Internal pre-conditions
The protocol calculates the reward based on the user's _balances.
External pre-conditions
The share of rewards allocated to users increases.
Attack Path
front-running
to stake tokens and increase their balances before the share increases.Impact
The attacker loses nothing and contributes nothing to the protocol, but gains a reward.
PoC
Mitigation
Add time lock when extracting revenue.