LiquidationPool::distributeAssets() function is meant to be called by LiquidationPoolManager::runLiquidation() with the list of assets which are based on the TokenManager's tokens array, their addresses and balance, as well as the HUNDERED_PC constant and the collateral rate, as the two other arguments but due to missing access control, the distributeAssets() function can be called by an arbitrary user with arbitrary arguments which can break the accounting system and lead to loss of funds.
LiquidationPool::distributeAssets() function should be called with the ILiquidationPoolManager.Asset[] array of assets calculated and called by the runLiquidation()function, together with the HUNDRED_PC and collateralRate() constants.
distributeAssets() should be called only by runLiquidation()
But due to missing access control which would be the onlyManager modifier or a require statement which would require the msg.sender to be the LiquidationManager address, distributeAssets can be called by anyone with arbitrary arguments which can significantly impact the distribution/burning of assets and lead to loss of funds.
Proof of Concept
Alice is a malicious user which decides to call the distributeAssets function with arbitrary arguments:
For the sake of the example, let's assume the function is called with ETH as _assets (1 ETH), a _collateralRate of "1" and a _hundredPc of 10_000.
stakeTotal is 20_000e18;
Assume that there are 2 holders in the pool.
If we take an arbitrary holder from the holders, with the assumption that he has TST 6_000e18 and EUROs 4_000e18 as stakes, 4_000e18 will be his stake.
_portion = 1e18 * 4_000e18 / 20_000e18 = 2e17 (or 0.2e18);
costInEuros = 2000e18(EUR Price) * 10_000 / 1 = 20_000_000e18. (or 2e25)
if-clause, since the cost is higher than the position:
_portion = 0.2e18 * 4_000e18 / 2e25 = 4e13 or 0.00004 in decimal representation.
costInEuros = 4_000e18.
_position.EUROs = 4_000e18 - 4_000e18 = 0
rewards = 4e13 of the ETH = 0.08 EUROs
burnEuros = 4000 EUROs.
....
This is just one example of how we can manipulate the protocol using native ETH to burn more tokens than the value we've sent and modify the user's balances/rewards causing catastrophic impact.
This won't work with ERC-20 tokens as it uses safeTransferFrom which will require approval from the manager (in runLiquidation()).
Calling the distributeAssets() function with arbitrary values using ETH as the assets (+ sending some amount of ETH), but changing the HUNDRED_PC and/or collateralRate() can significantly impact the calculations of the costInEuros variable, which would impact the EUROs position, the amount of tokens that need to be burned, as well as the amount of tokens which are going to be sent to the manager contract. This can negatively impact calculations and balances and cause a significant loss for the LiquidationManager and the holders in the LiquidationPool.
Manual Review
Add the OnlyManager modifier to the distributeAssets() function.
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.