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.