Due of lack of access control user can call distributeAsset
again and again to burn other user euro and claim other users eth leading them unclaimed high eth reward.
When a user is under collateralized, liquidator can run liquidation. Wehn runLiquidation
is called, it send the vault assets to Liquidation Pool
and call distributeAssets
. This function burn the required euro required for the liquidation and credit rewards to holders/Stakers.
For erc20 tokens it's works as expected and cannot be called by others. But in case of eth, it doesn't check the value. Here is simple example, how it can be exploited.
There are 4 users. User A, B, C and D.
user A has 10 eth, 0.1 wbtc and 1000 arbitrum in his vault which is undercollateral.
Meanwhile user B, C and D are stakers in the liquidationPool, in which they are staking euro's and Tst's.
For simplicity, User B has staked 100 euro and 100 TST.
user C staked 400 euro and 400 TST.
user D staked 500 euro and 500 tst.
so there share is 10, 40 and 50 percent respectively.
When runLiquidation
is called, they rewards are credited and euro burned as follows:
User | ETH share | WBTC share | ARBITRUM share | Euro burned |
---|---|---|---|---|
User B | 1 | 0.01 | 100 | 10 |
User C | 4 | 0.04 | 400 | 40 |
User D | 5 | 0.05 | 500 | 50 |
User B claimed his rewards, But user C and B Did not.
Now user B call distributeAssets
again only putting Native in assetsTokens (amount same or more) and other values as is, as function don't have any check for this. So it will execute it based on the user input and burn the euro's from all other users account and credit the reward to claim. Now user B will be able to claim eth again. He will only loose euros in this process. and always stays in profit. He can call this recursively untill most of the eth get's emptied.
Meanwhile other users won't be able to claim there rewards due to no or less eth in the contract.
After Attack users stats will be like this-
User | ETH share | WBTC share | ARBITRUM share | Euro burned |
---|---|---|---|---|
User B | 10 (claimed all) | 0.01 (claimed) | 100 (claimed) | 65.14 |
User C | 40 (can't claimed due to insolvency) | 0.04 | 400 | 260.56 |
User D | 50 (can't claimed due to insolvency ) | 0.05 | 500 | 325.7 |
Deploy following contract along with all others contracts.
Attacker will deploy the contract.
Send the TST and EUROs to the contract
Call the stake function to increase postion in the liquidation pool, wait for liquidation.
Once liquidation is done, he call the attack function with ethAmount (that was set during liquidation)
This will enable him to claim his initial earning + other eth as well
during this process, he will be burning euro's from other account as well and eth reward get credited to there pending earnings as well.
due to insolvency (as most eth is drained by the user already, other users will be at loss, will be unable to claim there rewards.
attack is possible only, if attacker has 50% or less stake of the total pool. If he has say 30, he will call it 3 times and drain 90% of eth.
Innocent users loosing there euro's.
Manual Review
Add a modifier onlyPoolManager on this function avoid the issue.
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.