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

Lack of check that any user can't take flash loan from same token again and again.

Summary

There is a missing check in flashLoan() that check for require(!s_currentlyFlashLoaning[token]).According to protocol any user can take flash loan one time from same token.

Vulnerability Details

Any attacker can take flash loan many time by approving one token.

Impact

Affected Code

function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
    AssetToken assetToken = s_tokenToAssetToken[token];
    uint256 startingBalance = IERC20(token).balanceOf(address(assetToken));

    if (amount > startingBalance) {
        revert ThunderLoan__NotEnoughTokenBalance(startingBalance, amount);
    }

    if (!receiverAddress.isContract()) {
        revert ThunderLoan__CallerIsNotContract();
    }

    uint256 fee = getCalculatedFee(token, amount);
    // slither-disable-next-line reentrancy-vulnerabilities-2 reentrancy-vulnerabilities-3
    assetToken.updateExchangeRate(fee);

    emit FlashLoan(receiverAddress, token, amount, fee, params);

    s_currentlyFlashLoaning[token] = true;
    assetToken.transferUnderlyingTo(receiverAddress, amount);
    // slither-disable-next-line unused-return reentrancy-vulnerabilities-2
    receiverAddress.functionCall(
        abi.encodeWithSignature(
            "executeOperation(address,uint256,uint256,address,bytes)",
            address(token),
            amount,
            fee,
            msg.sender,
            params
        )
    );

    uint256 endingBalance = token.balanceOf(address(assetToken));
    if (endingBalance < startingBalance + fee) {
        revert ThunderLoan__NotPaidBack(startingBalance + fee, endingBalance);
    }
    s_currentlyFlashLoaning[token] = false;
}

POC

  function Sametoken_Can_Take_FlashLoan_Again() public setAllowedToken hasDeposits {
  for(uint256 i=0;i<2;i++)
    {
    uint256 amountToBorrow = AMOUNT * 10;
    uint256 calculatedFee = thunderLoan.getCalculatedFee(tokenA, amountToBorrow);
    vm.startPrank(user);
    tokenA.mint(address(mockFlashLoanReceiver), AMOUNT);
    thunderLoan.flashloan(address(mockFlashLoanReceiver), tokenA, amountToBorrow, "");
    vm.stopPrank();

    assertEq(mockFlashLoanReceiver.getbalanceDuring(), amountToBorrow + AMOUNT);
    assertEq(mockFlashLoanReceiver.getBalanceAfter(), AMOUNT - calculatedFee);
} }

Tools Used

Forge

Recommendations

Add a check in flashLoan() for that same token can't take flash loan again and again.

Write the flashLoan() like this

   function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
   require( s_currentlyFlashLoaning[token]);
    AssetToken assetToken = s_tokenToAssetToken[token];
    uint256 startingBalance = IERC20(token).balanceOf(address(assetToken));

    if (amount > startingBalance) {
        revert ThunderLoan__NotEnoughTokenBalance(startingBalance, amount);
    }

    if (!receiverAddress.isContract()) {
        revert ThunderLoan__CallerIsNotContract();
    }

    uint256 fee = getCalculatedFee(token, amount);
    // slither-disable-next-line reentrancy-vulnerabilities-2 reentrancy-vulnerabilities-3
    assetToken.updateExchangeRate(fee);

    emit FlashLoan(receiverAddress, token, amount, fee, params);

    s_currentlyFlashLoaning[token] = true;
    assetToken.transferUnderlyingTo(receiverAddress, amount);
    // slither-disable-next-line unused-return reentrancy-vulnerabilities-2
    receiverAddress.functionCall(
        abi.encodeWithSignature(
            "executeOperation(address,uint256,uint256,address,bytes)",
            address(token),
            amount,
            fee,
            // @audit-issue : Use address(this) instead of msg.sender
            msg.sender,
            params
        )
    );

    uint256 endingBalance = token.balanceOf(address(assetToken));
    if (endingBalance < startingBalance + fee) {
        revert ThunderLoan__NotPaidBack(startingBalance + fee, endingBalance);
    }
    s_currentlyFlashLoaning[token] = false;
}

function repay(IERC20 token, uint256 amount) public {
    if (!s_currentlyFlashLoaning[token]) {
        revert ThunderLoan__NotCurrentlyFlashLoaning();
    }
    AssetToken assetToken = s_tokenToAssetToken[IERC20(token)];
    token.safeTransferFrom(msg.sender, address(assetToken), amount);
}
Updates

Lead Judging Commences

0xnevi Lead Judge
over 1 year ago
0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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