Closed code423n4 closed 1 year ago
closely related to #156 and the fix might be the same. Will leave as seperate but may choose to duplicate these together after sponsor review / comment.
chechu marked the issue as sponsor confirmed
0xean marked the issue as duplicate of #365
0xean marked the issue as satisfactory
Lines of code
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol#L641-L694 https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol#L449-L454 https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol#L946 https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol#L1026-L1032
Vulnerability details
Impact
In the
Comptroller.liquidateAccount()
function, the liquidation orders are executed by calling theforceLiquidateBorrow()
function and setting theskipLiquidityCheck
totrue
. Hence during the check for thepreLiquidateHook()
, in theVToken._liquidateBorrowFresh()
function, the following check is performed inside theif
block.preLiquidateHook() function has the following
if
block code snippet :Here the transaction enters the
if
block since theskipLiquidityCheck == true
. But the issue is the transaction will revert if therepayAmount > borrowBalance
.Hence a malicious borrower can front run the
liquidateAccount
transaction by repaying any of hisborrowed asset
such thatrepayAmount > borrowBalance
will be made true, for that asset. SinceforceLiquidateBorrow()
is called for each borrowed asset of theborrower
by iterating through afor
loop, theborrower
can revert theliquidateAccount
transaction by only repaying a single borrowed asset of his choice.If the malicious borrower has borrowed many assets, then he can delay the
liquidateAccount
transaction by repaying any of his borrows at a time, such thatrepayAmount > borrowBalance
will be made true. So he can buy his own time to find more funds and provide more collateral to the protocol to avoid force liquidation permanently.Proof of Concept
The PoC flow starts in the
Comptroller.liquidateAccount()
function by calling theforceLiquidateBorrow()
function.https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol#L677-L684
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol#L1026-L1032
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/Comptroller.sol#L449-L454
https://github.com/code-423n4/2023-05-venus/blob/main/contracts/VToken.sol#L946
Tools Used
Manual Review and VS Code
Recommended Mitigation Steps
In the
VToken._liquidateBorrowFresh()
function there is an internal call to the_repayBorrowFresh()
function. This function correctly handles therepayAmount
if it is greater than theborrowBalance
as shown below.If
repayAmount > borrowBalance
thenrepayAmount = borrowBalance
is assigned without reverting the transaction.But this operation is conducted after the
preLiquidateHook()
is called. Hence ifrepayAmount > borrowBalance
happens then transaction will already be reverted before transaction proceeds to the_repayBorrowFresh()
function call.Hence it is recommended to use the same logic used in the
_repayBorrowFresh()
mentioned above, in theComptroller.preLiquidateHook()
function as well.So in the
preLiquidateHook()
function, if therepayAmount > borrowBalance
occurs therepayAmount
should be set toborrowBalance
and transaction should proceed with out reverting. This will not affect the accounting of the protocol since the actual token transfer happens in theVToken._doTransferIn()
function.Assessed type
Other