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.