The function buyLoan
lacks a crucial check, where it fails to validate msg.sender
against the actual pool.lender
. Consequently, any user can call the function with any poolId
, even if they do not own that pool. This loophole allows unauthorised users to deduct poolBalances from the specified pool, while making msg.sender
the lender
for the loan. As a result, the caller can exploit this situation for profit when using a poolId
that they do not hold.
Here is the working unit test of the above described scenario
To further exploit this, the attacker leverages the parameter outstandingLoans
, which poses a risk of underflow when the borrower repays the loan. To exploit this vulnerability, the attacker can follow the steps below:
Create a similar pool: The attacker creates a new pool similar to the one which is used in the function buyLoan
.
Take debt from own pool: The attacker takes a debt from the new pool that was just created, making it appear as if the loan was borrowed.
Match the debt amount: The attacker ensures that the borrowed amount in the new pool matches the debt of the targeted loan they want to exploit.
Pay the least possible collateral: To minimize the risk, the attacker provides the least possible collateral in the new pool by setting maxLoanRatio
value to the maximum.
By executing these steps, the attacker can manipulate the system and exploit the vulnerability, potentially resulting in a loss of funds for the target pool.
Note: Comments provided for the above POC.
Loss of funds to the specified pool, which was used by attacker in the function buyLoan
Foundry
Check if the msg.sender is the pool owner.
if (msg.sender != pools[poolId].lender) revert Unauthorized();
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.