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

[H-5] Lacking Borrower Access Control

Summary

In ThunderLoan.sol:153, there is an absence of proper access control mechanisms to verify if the entity attempting to repay the loan is the actual borrower. This poses a security risk as it allows for unauthorized repayment, potentially leading to incorrect state changes or other unintended interactions.

Vulnerability Details

Vulnerable code:

function repay(IERC20 token, uint256 amount) public {
// @audit-issue [H-7] No proper access control to check if caller is the actual borrower
if (!s_currentlyFlashLoaning[token]) {
revert ThunderLoan__NotCurrentlyFlashLoaning();
}
AssetToken assetToken = s_tokenToAssetToken[IERC20(token)];
token.safeTransferFrom(msg.sender, address(assetToken), amount);
}

Impact

Without strict checks on who can call the repay function, there's a risk that anyone could interfere with the repayment process, potentially allowing for malicious entities to disrupt the normal flow of the smart contract. This could lead to loss of funds, denial of service, or unintended contract states which might be exploited.

Tools Used

  1. Manual Review

  2. Vs Code

Recommendations

Implement Access Control: The contract should check if msg.sender is the borrower recorded in the contract's state before allowing the repay function to proceed. This can be implemented using mapping to keep track of each borrower's state and require statements to assert proper authorization.

Possible fix:

  1. Add a mapping to track activeBorrowers:

// @audit-info We added this to ensure access control during critical function calls
// Maps the borrower address to the token for active loans
mapping(IERC20 => address) private activeBorrowers;
  1. Add borrower to the mapping when initiating a flashloan:

function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
uint256 startingBalance = IERC20(token).balanceOf(address(assetToken));
if (amount > startingBalance) {
revert ThunderLoan__NotEnoughTokenBalance(startingBalance, amount);
}
if (!receiverAddress.isContract()) {
revert ThunderLoan__CallerIsNotContract();
}
// Record the borrower's address
// @audit-info we added this to insure proper access control
activeBorrowers[token] = receiverAddress;
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;
}
  1. Verify caller is the actual borrower and reset state after repaying the flashloan:

function repay(IERC20 token, uint256 amount) public {
// @audit-issue [H-7] No proper access control to check if caller is the actual borrower
if (!s_currentlyFlashLoaning[token]) {
revert ThunderLoan__NotCurrentlyFlashLoaning();
}
// Verify that the caller is the borrower for this token
// @audit-info we added access control in here
address borrower = activeBorrowers[token];
require(msg.sender == borrower, "Caller is not the borrower");
AssetToken assetToken = s_tokenToAssetToken[IERC20(token)];
token.safeTransferFrom(msg.sender, address(assetToken), amount);
// Clear the active borrower record
// @audit-info added this due to access logic implemented above
delete activeBorrowers[token];
}
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.