H-17 Unmitigated #95
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
edited-by-warden
MR-H-17
satisfactory
satisfies C4 submission criteria; eligible for awards
unmitigated
Lines of code
https://github.com/pixeldaogg/florida-contracts/blob/7212bfbe9f78ca6eabb5eec86e24d754feb47f15/src/lib/loans/MultiSourceLoan.sol#L1195
Vulnerability details
Impacts:
In an edge case, the same struct renegotiationOffer and renegotiaionOffer signature can still be used interchangeably between refinanceFull() and addNewTranche().
Original Issue/ Current fix:
C4 Issue:
H-17: refinanceFull/addNewTranche reusing a lender's signature leads to unintended behavior
Original vulnerabilities:
refinanceFull and addNewTranche both require RenegotiationOffer’s signature check, but allows the same renegotiationOffer struct and same signature to pass.
Original impacts:
This encourages the attack vector of front-running to use the signature for malicious operations. In this case, an attacker can use the renegotiationOffer input and signature that was meant for refinanceFull() in addNewTranche().
Mitigation:
Fix: https://github.com/pixeldaogg/florida-contracts/pull/390/files
The current fix is to add additional checks on struct renegotiationOffer. In r
efinanceFull()
, checks are added to ensure that_renegotiationOffer.trancheIndex.length != _totalTranches
. InaddNewTranche()
, a different check is in place to make suretranchIndex.length ==1 && trancheIndex[0]== _totalTranches
(the only trancheIndex would be the next tranche index).Proof of concept
The problem is an edge case will still allow struct renegotiationOffer and renegotiaionOffer signature to be used interchangeably between refinanceFull() and addNewTranche().
Edge Case:
A loan only has 1 tranche, and
_renegotiationOffer.trancheIndex[0] == 1
.When a loan has 1 tranche, _renegotiationOffer.trancheIndex.length == _totalTranches will be true. And as long as the _renegotiationOffer's trancheIndex[0] is _totalTranches/1, the _renegotiationOffer will pass both
_checkAddNewTrancheOffer()
and_checkRefinanceFullRenegotiationOffer()
.Note: in refinanceFull() flow,
_renegotiationOffer.trancheIndex[0]
is not used and not checked. So the value of_renegotiationOffer.trancheIndex[0]
will not make a difference inrefinanceFull()
.Impact: The lender’s renegotiationOffer for refinanceFull can still be used for addNewTranche(); And vice versa.
(1) When a borrower wants to add a new tranche and receive the additional principal amount, instead, the lender might use the offer to
refinanceFull()
to avoid transferring the principal.(2) Or when a lender refinanceFull() for a loan with only 1 tranche and with input (_renegotiationOffer.trancheIndex[0] as 1). A malicious borrower can receive 100% more principal with existing NFT collateral - borrower's position will be significantly under-collateralized. Borrower might earn a profit even by giving up their NFT.
Recommendations:
In
_checkRefinanceFullRenegotiationOffer()
, add check to ensurerenegotiationOffer.trancheIndex[0] != _totalTranches
.Assessed type
Other
The text was updated successfully, but these errors were encountered: