Lucene search

K
code423n4Code4renaCODE423N4:2022-04-BACKD-FINDINGS-ISSUES-55
HistoryApr 26, 2022 - 12:00 a.m.

Unlimited reward minting with Function Transfer in StakerVault (updates balances before calling userCheckpoint)

2022-04-2600:00:00
Code4rena
github.com
5
stakervault
transfer function
usercheckpoint
flash loan
minting vulnerability
lp stake
exploit
mitigation

Lines of code

Vulnerability details

Impact

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).

Proof of Concept

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)

Tools Used

VIM

Recommended Mitigation Steps

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