Open c4-bot-3 opened 4 months ago
GalloDaSballo marked the issue as duplicate of #238
GalloDaSballo marked the issue as sufficient quality report
Selected as best because of good POC + well balanced severity rationalization.
trust1995 marked the issue as selected for report
trust1995 marked the issue as satisfactory
For transparency and per conversation with the sponsors, see here for the Wise Lending team's mitigation.
Lines of code
https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseCore.sol#L493-L499
Vulnerability details
Description
When liquidating a user,
_withdrawOrAllocateSharesLiquidation()
can be called which checks if a pool is large enough to pay out the liquidator, and if not, then the liquidator is allocated shares that can be used to withdraw at a later time when the pool is large enough. There are two scenarios in which the pool will not be large enough to pay out the liquidator:However, if these scenarios were ever to arise, the
_withdrawOrAllocateSharesLiquidation()
function would not work as expected. This is due to how it calculates the lending shares (totalPoolInShares
) of the total pool: https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseCore.sol#L493When liquidating, it will calculate the
totalPoolInShares
like this (with amount being the total pool):However, calculating the lending shares this way will cause the
_compareSharePrices()
to revert on thesyncPool
modifier. Specifically the check onThis is because
_maxSharePrice
is passed in as false tocalculateLendingShares()
, which subtracts one.https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseCore.sol#L497
However, because we are withdrawing, it should be set to true so that it adds one. In all other areas where
calculateLendingShares()
is being used, the functions that focus on withdrawing have_maxSharePrice: true
and the depositing functions have_maxSharePrice: false
:withdrawOnBehalfExactAmount
onWiseLending.sol
: https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLending.sol#L874_preparationsWithdraw
onMainHelper.sol
: https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/MainHelper.sol#L154_handleDeposit
onWiseCore.sol
: https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseCore.sol#L119When
_maxSharePrice
is set to true, the_validateParameter()
check will pass.Even if the
_validateParameter()
check did not exist, if_maxSharePrice
is left as-is (set to false), any subsequent liquidations through_withdrawOrAllocateSharesLiquidation()
would revert with panic due to an underflow. This is because (as stated above in bold), liquidating when the pool is not large enough to pay out the liquidator will drop the total pool to 0 and issue the remainder as shares. Therefore, any subsequent calls to liquidate when the total pool is 0 will underflow:Impact
When a particular receiving token is desired and
_withdrawOrAllocateSharesLiquidation()
is called, the liquidation will always revert if the total pool is not large enough to cover the withdraw amount. This defeats the purpose of_withdrawOrAllocateSharesLiquidation()
, as stated in the NatSpec:This will lead to frustrated users who desire a particular receiving token. The level of frustration will be even higher if the reason this function reverts is because the total pool is borrowed out. This would lead to a very high APY, thus a much higher desire to receive the particular token. Additionally, the borrowers in these particular situations will not be liquidated even though they should be. Ultimately, this will lead to loss of faith in the protocol.
However, this can be easily avoided by just passing in a different receiving token that is large enough to payout the withdraw amount, which is why this is a medium level issue.
Proof of Concept
Copy and paste this foundry test into the
/contracts
folder and runforge test --fork-url mainnet --match-path ./contracts/WoolCentaurLiquidationTest.t.sol --match-test testLiquidateMockPool -vvvv
. As the code is, the test will fail. To have it pass, change the bool here: https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseCore.sol#L497 totrue
.Tools Used
Foundry/Manual Analysis
Recommended Mitigation Steps
Change the
_maxSharePrice
undercalculateLendingShares
on_withdrawOrAllocateSharesLiquidation()
to true;Assessed type
Error