Closed howlbot-integration[bot] closed 4 months ago
hansfriese marked the issue as not a duplicate
Duplicate of #146.
hansfriese marked the issue as duplicate of #146
hansfriese changed the severity to 2 (Med Risk)
hansfriese marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/actions/SellCreditMarket.sol#L40
Vulnerability details
Impact
Malicious lenders can force borrowers into fragmenting their credit positions
Proof of Concept
Users can create a loan offer that other users might use to borrow tokens. The borrower might use a credit position he has in order for him to take the borrow. The borrower can also specify whether he wants the
amount
parameter he put as an input to equalcashAmountOut
orcreditAmountIn
by using theexactAmountIn
boolean. If he chooses that he wantsamount
to equalcashAmountOut
(by setting the boolean as false), a malicious lender can force him into fragmenting his credit position and paying extra fees.This is the code that is responsible for handling the case when the user has specified that he wants the
amount
parameter to equalcashAmountOut
:RESERVED_ID
is used when doing a simple borrow, this is not the case here as the borrower wants to use a credit position he already has. If thecashAmountOut
andmaxCashAmountOut
, then the borrower will have to pay a fragmentation fee. Even if he carefully inputted data that would not fragment him, the lender can still force him into fragmentation. That is becausemaxCashAmountOut
gets computed like this:The malicious lender has control over 1 of the values here and that is
ratePerTenor
. TheratePerTenor
value is the interest the borrower has to pay, in simplest terms. The lender can frontrun and change his yield curve and thus, change theratePerTenor
value which makesmaxCashAmountOut
a different value than the user expected. There is this check invalidateSellCreditMarket()
:However, this check does not prevent the issue. Even if the borrower specified the
maxAPR
parameter exactly equal to the expectedapr
, the lender can actually decrease the APR on his yield curve. This will make the check pass as it would makeapr
even lower than expected and definitely lower than themaxAPR
specified by the user. A user can decrease his APR slightly and that would makeratePerTenor
not much smaller than what it would usually be (100000000000000000
vs99884000000000000
according to my POC below, a difference of slightly higher than 1.001%).For the POC, I have used event emissions as I couldn't find a way to see the values we need to see to confirm the issue without significantly changing the code.
Create a
Log
event foruint256
inSellCreditMarket.sol
. Then, add the following event emissions inSellCreditMarket::executeSellCreditMarket()
(second and third line in the else block in the if statement, the same way as I did in the code below, first event is equal tomaxCashAmountOut
and the second one iscashAmountOut
):Then, add these 2 event emissions right at the end of the function (the amount the borrower receives and the amount he has to repay with interest):
Then, paste the following POC in
SellCreditMarket.t.sol
(use-vvvv
to see the events, toggle the line where Alice changes the APR by making it a comment and back into code to see the scenario of Alice frontrunning and not frontrunning play out):Tools Used
Manual Review
Recommended Mitigation Steps
You could add a
minAPR
value as well or changemaxAPR
toexpectedAPR
and use==
, that way the APR can't change and this scenario won't be possible.Assessed type
Other