HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Missing Reentrancy Protection

Summary

The supply and withdraw functions in IAave.sol contract do not contain any reentrancy protection. This could allow an attacker to exploit the functions and recursively call them, draining funds from the contract.

Vulnerability Details

  • The supply and withdraw functions involve token transfers (ERC20 transfer or transferFrom). These functions are vulnerable to reentrancy attacks, especially when external calls are made (e.g., interacting with a malicious contract).

  • An attacker could initiate a reentrancy attack to repeatedly withdraw or supply assets, draining the protocol.

Impact

The protocol could be drained of assets through reentrancy attacks, potentially leading to a complete loss of user funds.

Proof of Concept for Missing Reentrancy Protection

Overview:

The lack of reentrancy protection in the supply and withdraw functions makes the contract vulnerable to reentrancy attacks. These functions involve transferring tokens, which could be exploited by a malicious contract to recursively call the function before the state is updated, leading to potential funds being drained.

Actors:

  • Attacker: A malicious contract designed to exploit the reentrancy vulnerability by calling the withdraw function recursively.

  • Victim: The Aave contract system that holds users' funds.

  • Protocol: The Aave contract system responsible for handling user deposits and withdrawals.

Working Test Case:

// Solidity code demonstrating reentrancy vulnerability
// Step 1: Attacker deploys a malicious contract that exploits reentrancy
contract MaliciousAttacker {
address public aaveContract;
address public attacker;
constructor(address _aaveContract) {
aaveContract = _aaveContract;
attacker = msg.sender;
}
// The attacker's malicious fallback function
receive() external payable {
// Step 3: Attacker recursively calls the withdraw function of the Aave contract
if (aaveContract.balance > 0) {
aaveContract.call(abi.encodeWithSignature("withdraw(address,uint256,address)", address(this), 1000, attacker));
}
}
// Step 2: Attacker triggers the reentrancy attack by calling the withdraw function
function initiateAttack() external {
// Attacker calls the withdraw function with a large withdrawal amount
(bool success,) = aaveContract.call(abi.encodeWithSignature("withdraw(address,uint256,address)", address(this), 1000, attacker));
require(success, "Attack failed");
}
}
// Explanation:
// 1. The attacker deploys a malicious contract that interacts with the Aave contract's `withdraw` function.
// 2. The malicious contract's fallback function is triggered when it receives funds from the `withdraw` call, and it recursively calls `withdraw` before the first call is finished, draining the contract's funds.

Outcome & Implications:

  • Outcome: The attacker will be able to call the withdraw function repeatedly, draining funds from the protocol by exploiting the reentrancy vulnerability. The attacker can continue making recursive calls until the protocol is drained of assets.

  • Implications: This is a critical vulnerability, as it allows an attacker to withdraw more funds than originally intended by exploiting the reentrancy vulnerability, potentially draining the entire liquidity pool. This poses a significant risk to the protocol’s solvency and user assets.

Tools Used

Manual code review

Slitter

Recommendations

  • Use the ReentrancyGuard from OpenZeppelin or implement custom reentrancy guards to ensure that the functions are not callable recursively during execution.

  • State Updates Before External Calls: Another mitigation approach is to update the contract’s state (e.g., balances or reserves) before making external calls (like token transfers). This ensures that if a reentrancy attack happens, the state has already been updated, reducing the impact.

    function withdraw(address asset, uint256 amount, address to) external returns (uint256) {
    // Update internal state before calling external transfer
    balances[msg.sender] -= amount;
    // Now perform the transfer (this is where reentrancy could be exploited)
    IERC20(asset).transfer(to, amount);
    return amount;
    }
  • Checks-Effects-Interactions Pattern: Always use the "Checks-Effects-Interactions" pattern where internal state is updated (e.g., balances or state variables) before any external call (such as a token transfer). This minimizes the chance of reentrancy attacks.

Updates

Lead Judging Commences

bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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