This repository has been archived by the owner on Apr 28, 2024. It is now read-only.
0x52 - Adversary can reenter takeOverDebt() during liquidation to steal vault funds #76
Labels
High
A valid High severity issue
Reward
A payout will be made for this issue
Sponsor Confirmed
The sponsor acknowledged this issue is valid
Will Fix
The sponsor confirmed this issue will be fixed
0x52
high
Adversary can reenter takeOverDebt() during liquidation to steal vault funds
Summary
Due to the lack of nonReentrant modifier on takeOverDebt() a liquidatable position can be both liquidated and transferred simultaneously. This results in LPs being repaid from the vault while the position and loans continue to be held open, effectively duplicating the liquidated position. LPs therefore get to 'double dip' from the vault, stealing funds and causing a deficit. This can be abused by an attacker who borrows against their own LP to exploit the 'double dip' for profit.
Vulnerability Detail
First we'll walk through a high level breakdown of the issue to have as context for the rest of the report:
LiquidityManager.sol#L279-L287
The control transfer happens during the swap to UniV3. Here when the custom token is transferred, it gives control back to the attacker which can be used to call takeOverDebt().
LiquidityBorrowingManager.sol#L667-L672
The reason the reentrancy works is because the actual borrowing storage state isn't modified until AFTER the control transfer. This means that the position state is fully intact for the takeOverDebt() call, allowing it to seamlessly transfer to another address behaving completely normally. After the repay() call resumes, _removeKeysAndClearStorage is called with the now deleted borrowKey.
Keys.sol#L31-L42
The unique characteristic of deleteKey is that it doesn't revert if the key doesn't exist. This allows "removing" keys from an empty array without reverting. This allows the repay call to finish successfully.
LiquidityBorrowingManager.sol#L450-L452
Now we can see how this creates a deficit in the vault. When taking over an existing debt, the user is only required to provide enough hold token to cover any fee debt and any additional collateral to pay fees for the newly transferred position. This means that the user isn't providing any hold token to back existing LP.
LiquidityBorrowingManager.sol#L632-L636
On the other hand repay transfers the LP backing funds from the vault. Since the same position is effectively liquidated twice, it will withdraw twice as much hold token as was originally deposited and no new LP funds are added when the position is taken over. This causes a deficit in the vault since other users funds are being withdrawn from the vault.
Impact
Vault can be drained
Code Snippet
LiquidityBorrowingManager.sol#L395-L453
Tool used
Manual Review
Recommendation
Add the
nonReentrant
modifier totakeOverDebt()
The text was updated successfully, but these errors were encountered: