The buyLoan() function is used to buy a loan off of a refinance auction to claim it to a provided it's id, but the effective check for making sure it is the sender's pool is missing.
Unlike other functions regarding pools, checking for ownership of the pool, this function does not implement the necessary check: if (pools[poolId].lender != msg.sender) revert Unauthorized();
A user can buy a loan to an existing pool which isn't owner by him, the new loan lender would be the msg.sender effectively freezing the loan, making it unpayable, since the outstanding loan of the non-existing pool that the new id would point to is 0, reverting on a repay attempt, locking the borrowers collateral.
We update the balances of the loan's old pool and the balances of the pool which's id we provided but we as the malicious msg.sender pay only the protocol fee on the loan and then set it's parameters to the pool we provided + msg.sender as the lender, who may not have a pool or even if he does, it might not be the one he provides as a function parameter. The loan would become unrepayable and thus the malicious user can put it up for auction (outstanding loan still defaults to 0, nobody can buy it off), wait until it ends, seize the loan and steal the collateral.
Loans up for auction can get stolen by malicious users leading to loss of collateral.
Manual Review
Add the necessary if (pools[poolId].lender != msg.sender) revert Unauthorized(); to make sure the lender is buying the loan to his existing pool.
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.