The LiquidationPool::distributeAssets function is externally accessible and can be exploited to extract any tokens (EUROs included) from the LiquidationPoolManager contract, potentially leading to the loss of funds for users and the protocol itself.
The LiquidationPool::distributeAssets function takes an array of ILiquidationPoolManager.Asset objects and distributes assets based on the stake of each holder in the pool. The main source of rewards are sent to the contract LiquidationPoolManager after liquidation. Usually after running the function LiquidationPoolManager::runLiquidation. However, tokens can come from other sources (e.g., mint and burn from vaults, fees from swaps) including both ITokenManager(manager.tokenManager()).getAcceptedTokens() and EUROs.
A malicious actor can deposit both TST and EUROs in LiquidationPool contract. Using a bot that checks if the balance of LiquidationPoolManager then calls LiquidationPool::distributeAssets everytime the Manager has tokens. He can also use a large deposit amount of tokens (using mechanisms like flash loans) and then trigger LiquidationPool::distributeAssets. The issue is double :
Tokens (other than EUROs) that are sent to the LiquidationPoolManager contract via means other than liquidations mostly via (swaps fees + direct wallet transfers) will be direclty claimed rather than following the process of the LiquidationPoolManager::runLiquidation function.
Euros tokens are sent to the LiquidationPoolManager contract via (mint + burn + direct transfers) in the Vaults and are a signficant source of revenue to the protocol and LiquidationPool stakers. Also according to sponsor Euros are not among the acceptedTokens ITokenManager(manager.tokenManager()).getAcceptedTokens(). Even so, the attacker can still add this token to the ILiquidationPoolManager.Asset array, run the function LiquidationPool::distributeAssets then force send all LiquidationPoolManager Euros contract balance to the LiquidationPool contract. This balance will be added to the holder rewards depending on their stake. But since Euros token will not be added to the acceptedTokens. The holders cannot effectively claim their Euros rewards. Which means that tokens sent will be effectively locked in the LiquidationPool contract.
The attacker can do this everytime the balance of LiquidationPoolManager in any token increases. Since all Euros token would be sent to the LiquidationPool, this not only prevent holders from getting any additional fees from the LiquidationPoolManager::distributeFees function but prevents the LiquidationPoolManager.protocol() EOA address from getting paid as it should be from this line :
The following test demonstrates the attack. You can execute it using forge test --mt testCallDistributeAssetsWithoutLiquidate -vv
Rewards intended for legitimate liquidation events can be claimed.
Loss of funds, all Euros will be locked or burned in the LiquidationPool contract and preventing both the protocol EOA and stakers from earning fees
Manual review + foundry testing
Add the onlyManager modifier to the LiquidationPool::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.