Open code423n4 opened 2 years ago
The warden has identified a way for the rewardsShares
to be improperly assigned.
The exploit is based on the fact that if totalStaked
is zero we effectively will skip the first loop of points.
This can create situations where certain users are rewarded unfairly in comparison to their initial deposit.
However, this only applies when we go from zero to non-zero for totalStaked
henced the scenario proposed by the warden (1 day of free rewards for first depositor) is actually the worst case scenario.
Having the deployer do 2 or 3 initial deposits should mitigate this attack, which ultimately is limited to a leak of value to the first user depositing.
For those reasons, I believe the finding to be valid and of Medium Severity
Lines of code
https://github.com/code-423n4/2022-05-backd/blob/2a5664d35cde5b036074edef3c1369b984d10010/protocol/contracts/tokenomics/AmmGauge.sol#L147 https://github.com/code-423n4/2022-05-backd/blob/2a5664d35cde5b036074edef3c1369b984d10010/protocol/contracts/tokenomics/AmmGauge.sol#L154
Vulnerability details
Impact
The first staker can take control of how the subsequent shares are going to be distributed by simply staking 1wei amount of the token and frontrunning future stakers. The reasons of this are related on how the variables are updated and with the amounts that the Gauge allows users to stake (anything but zero). The origin of this vulnerability relies on the evaluation of the
totalStaked
variable on its inception.Proof of Concept
To illustrate this attack an environment of testing was made in order to track the token flows and how the variables are being updated and read.
The initial or border conditions taken into account are the same as the used by the team to perform the tests and just a few assumptions and simplifications were taken.
1) The inflation rate is fixed for simplicity (
0.001
). This is valid within a short period of time because it is not a function of how the tokens are distributed or their flows. By tracking how the inflation rate is calculated an updated, we see that it is managed by thecurrentInflationAmountAmm
within theMinter.sol
contract, which value is modified by_executeInflationRateUpdate()
three lines below the last code permalink. Its value depends on non-token balance related parameters (such as inflation decays and annual rates). 2) For the testing environment performed by the team, a DummyERC20 was used as testing token. The same is done on the exploit environment. 3) The controller is not used because it is used to retrieve the inflation rate and it is now fixed because of 1).Each user state is updated whenever he calls either
stake
,unstake
orclaimRewards
.Steps:
10 * 10**decimals()
).Both cases were evaluated (with and without staking 1 wei first). The attack scenario outputs a 100% more shares to Alice than Bob in comparison with the ethical/non-attack situation.
The code used to perform this test is the following:
The simulation was both made for the attacked and non attacked situations. The values that are shown represent how the contract updates them (the
totalStaked
variable is 0 when first Alice calls the stake function after_userCheckpoint()
rans)WITH 1WEI STAKE (ATTACK)
WITHOUT THE 1WEI STAKE (No "first staker hijack")
Recommended Mitigation Steps
Further evaluation on how the variables are updated and how does the
Integral
(both each users and global one) is calculated on the pool inception is needed to patch this issue.