The bug in โStakerVault.transferโ function (which is externally callable) is that first it is updating the balance of sender and receiver then it calls ILpGauge(lpGauge).userCheckpoint for those addresses. Function userCheckpoint use balance of address to calculated user rewards and userCheckpoint need to be called before updating balances (like other functions in StakerVault which calls userCheckpoint before updating balances)
With this bug one can mint unlimited rewards for himself(just need to stake with multiple wallets, wait some time then get flash loan and stake it and transfer to other wallets and collect the rewards).
To exploit this bug attacker first stake some LP in the StakerVault for his Wallet1 and wait some times and donโt do any action so userCheckpoint() is not called for this Wallet1.
then attacker with other Wallet2 get a flash loan(as much as pool allow to deposit) and deposit it in the pool and stake it and then Transfer stakes it to Wallet1.
In this moment StakerVault.Transfer function is gonna call lpGauge.userCheckpoint(Wallet1) which is going to create a lot of reward for Wallet1 (because balance of Wallet1 is increased recently and function userCheckpoint logic think this balance is there from the last time userCheckpoint called).
The detailed steps:
1- Wallet1: deposit some coin to Pool and get LP.
2- Wallet1: stake LP in StakerVault so LpGauge.perUserStakedIntegral[user] get value for this wallet.
3- wait 1 week until LpGauge.poolStakedIntegral_is get high and donโt interact with StakerVault so value of LpGauge.perUserStakedIntegral[user] stays the same.
4- Wallet2(smart contract): get a flash loan as much as possible.
4.1- Wallet2: deposit coins into Pool and Stake LP.
4.2- Wallet2: use StakerVault.Transfer(Wallet1, HIGH_AMOUNT) to transfer stacks to Wallet1 address.
4.3- StakerVault.Transfer updates: balances[Wallet1] += HIGH_AMOUNT
4.4- StakerVault.Transfer calls lpGauge.userCheckpoint(Wallet1)
4.5- lpGauge.userCheckpoint(Wallet1) will run this: (user==Wallet1)
perUserShare[user] += (
(stakerVault.stakedAndActionLockedBalanceOf(user)).scaledMul(
(poolStakedIntegral_ - perUserStakedIntegral[user])
)
);
4.6- stakerVault.stakedAndActionLockedBalanceOf(Wallet1) is gonna be a large number and (poolStakedIntegral_ - perUserStakedIntegral[Wallet1]) is gonna be all the pools per stake rewards for the last week.
4.7- perUserShare[Wallet1] is gonna be a big number (if pool allow large deposit attacker can generate unlimited rewards)
4.8- call lpGauge.claimRewards(Wallet1) to collect rewards.
5- attack can do this process for multiple addresses to increase his rewards even more. (transfer flashloan between those addresses)
VIM
call ILpGauge(lpGauge).userCheckpoint before updating balances like any other function in StakerVault.
The text was updated successfully, but these errors were encountered:
All reactions