Thunder Loan

AI First Flight #7
Beginner FriendlyFoundryDeFiOracle
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

[H-4] Reentrancy During Flash Loan Execution Enables Asset Drain

Reentrancy During Flash Loan Execution Enables Asset Drain

Description

  • The flashLoan function performs multiple external calls without comprehensive reentrancy protection. Specifically, it transfers underlying assets and invokes a callback on an untrusted external contract without enforcing a strict checks-effects-interactions pattern or using a reentrancy guard.

    Reentrancy can occur via:

    • A malicious flash loan receiver contract reentering the protocol during the callback.

    • A malicious ERC20 token executing callbacks during token transfers.

    Without explicit safeguards, reentrancy allows attackers to interact with protocol state in unexpected intermediate states, enabling balance manipulation or asset extraction.

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(receierAddress, 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;
}

Risk

Likelihood:

  • The vulnerability is exploitable whenever a malicious receiver contract or a malicious ERC20 token is involved. Flash loan functionality inherently interacts with external contracts, making this attack vector realistic.

Impact:

  • High — Loss of Protocol-Held Assets.
    A malicious actor may reenter sensitive functions during flash loan execution, manipulate internal accounting, and drain assets from the protocol.

Recommended Mitigation

Please do the check effect interaction to avoid this reentrancy, and please add reentrancyGuard modifier on the function
- function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
+ function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external reentrancyGuard {
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(receierAddress, 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;
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 9 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!