By function definition, buyLoan can be called by anyone, but caller needs to own the pool and bugLoan's object is that pool.
But there's no correlation check in the function, and caller can optionally specify a pool it doesn't own, and set loans[loanId].lender caller.
If caller does not own the pool, the calculated poolId does not exist, and the lender cannot get their collateral back anymore.
As you can see from the code, there is no validation for msg.sender.
The caller only needs to specify a poolId to pass the above verification, and then he can point the loan.lender to himself, causing the lender to be unable to redeem the collateral, because caller does not own a pool.
Malicious users can call buyLoan to permanently lock the collateral and the corresponding pool get debt.
Because last line of code overflowed, the attacker could not sell the loan directly, but they could then manually create pool and borrow, then sell the loan.
Manual review
Should verify that the specified pool is owned by caller.
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.