The protocol allows attackers to steal loans and lock collateral, due to missing checks if the loan requester is the owner of the associated pool. Additionally, the lender's funds can be trapped with the borrower, who is unable to repay or refinance the loan. Attackers can exploit this vulnerability to set up foreign pools. This poses a significant risk to borrowers and lenders.
The problem with the buyLoan is that it lacks a check to ensure whether the msg.sender is the owner of the pool associated with the passed poolId. Additionally, at the end of the function, the new loan lender is set to msg.sender instead of the pool.lender.
Let's consider the following scenario:
Bob is Lender1; Alice is Lender2; Mallory is the Borrower; Eve is an Attacker with no pool and without any loanTokens
Bob calls setPool to create a pool with poolBalance: 1000e18, loanToken: WETH, and collateralToken: DAI, assigned poolId: 0x0, and transfers the WETH amount into the protocol.
Alice calls setPool to create a pool with poolBalance: 2000e18, loanToken: WETH, and collateralToken: DAI, assigned poolId: 0x1, and transfers the WETH amount into the protocol.
Mallory wants to take a loan of 1000e18 from Bob's pool, so she calls borrow, transferring the DAI tokens into the protocol and receiving her amount of WETH.
After some time, Bob decides to sell Mallory's loan and retrieve his WETH tokens, so he calls startAuction.
Eve sees an opportunity to steal Mallory's loan, so she calls buyLoan with loanId = 0 and poolId = 0x1 (Mallory's loan and Alice's pool). She can do this because there is no check if msg.sender == pool.lender.
As a result:
The WETH amount will be subtracted from Alice's pool poolBalance without her consent.
Mallory is unable to repay or refinance the loan. Alice is also unable to startAuction because his address != pool.lender, giveLoan because his address != pool.lender, or seizeLoan because the attacker is not started auction.
Bob will not be harmed as he will be able to withdraw his loan amount along with the interest from his pool
Eve can exploit this vulnerability to set up foreign pools that meet certain requirements: poolBalance ≥ totalDebt and interestRate ≤ currentAuctionRate.
Add this test in the Lender.t.sol
The borrower is unable to retrieve their collateral because it becomes locked in a non-existent pool. The lender's funds will be stuck with the borrower, who cannot return them because they are unable to repay or refinance.
The attacker can choose to lock funds in the protocol or, if profitable, set up their own pool with a very small auctionLength and enough balance to seizeLoan and get the user's collateral.
Manual review, Foundry
Set the new loan lender to pool.lender if you want everyone to be able to match arbitrary loans with arbitrary pools.
https://github.com/Cyfrin/2023-07-beedle/blob/main/src/Lender.sol#L518
The better option is to add a check if the msg.sender == newPool.lender.
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.