Every time BaseGauge::stake is called, the updateReward modifier is triggered which calls a helper function to update the reward allocation for the called of the transaction. However, an incorrect order of state changes allow an attacker to immediately get a reward after staking. Exploiting this vulnerability, they can stake, get the reward tokens, withdraw their staked amount of stakingToken and transfer it to another account of theirs to repeat the whole process and steal as much reward tokens as possible.
In the helper function _updateReward the state.rewardPerTokenPaid representing reward pair to the user so far is updated after setting the state.rewards.
This allows the user to immediately get rewards because:
Here, in the BaseGauge::earned the rewards for the user are calculated by subtracting the current reward per token by userStates[account].rewardPerTokenPaid.
As I mentionned, userStates[account].rewardPerTokenPaid is updated after calling earned() which means in the case of staking, the variable will hold value of 0.
Output:
This vulnerability allows a malicious user to drain almost the whole reward balance from the gauge.
VSCode
Add additional check in the BaseGauge::_updateReward:
This ensures that rewards are only updated after rewardPerTokenPaid is set, preventing immediate reward accumulation upon staking.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.