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

Reentrancy Vulnerability in withdrawInheritedFunds Allows Malicious Beneficiaries to Drain Contract Funds

Summary

The withdrawInheritedFunds function in the InheritanceManager contract is vulnerable to a reentrancy attack when distributing Ether. A malicious beneficiary could exploit this vulnerability to repeatedly withdraw more funds than they are entitled to, potentially draining the contract's Ether balance.

Vulnerability Details

  1. withdrawInheritedFunds Function:

    • Purpose: Distributes the contract's Ether or ERC20 token balance equally among the beneficiaries.

    • Logic:

      • Checks if the contract has been inherited (isInherited).

      • Calculates the amount each beneficiary should receive (amountPerBeneficiary).

      • Iterates through the beneficiaries array.

      • If distributing Ether, it uses beneficiary.call{value: amountPerBeneficiary}("") to send Ether.

      • If distributing an ERC20 token, it uses safeTransfer to send tokens.

    • Vulnerability: The use of .call{value: ...}("") to send Ether opens up a reentrancy attack vector.

    • No nonReentrant: The function does not use nonReentrant to protect itself from reentrant calls.

    • Code:

      function withdrawInheritedFunds(address _asset) external {
      // ...
      if (_asset == address(0)) {
      uint256 ethAmountAvailable = address(this).balance;
      uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
      for (uint256 i = 0; i < divisor; i++) {
      address payable beneficiary = payable(beneficiaries[i]);
      (bool success,) = beneficiary.call{value: amountPerBeneficiary}(""); // Reentrancy vulnerability here!
      require(success, "something went wrong");
      }
      } else {
      // ...
      }
      }
  2. Reentrancy Exploit:

    • Malicious Beneficiary: A malicious beneficiary can create a contract that implements a fallback or receive function.

    • Fallback/Receive Trigger: When the withdrawInheritedFunds function calls the malicious beneficiary's contract with .call{value: ...}(""), the fallback or receive function in the malicious contract is triggered.

    • Reentrant Call: Within the fallback or receive function, the malicious contract can call back into withdrawInheritedFunds again before the original execution of withdrawInheritedFunds has completed.

    • Repeated Withdrawals: Because the first call hasn't finished, the malicious contract can receive eth again, draining more funds than it is allowed.

Impact

  1. Loss of Funds: The most severe impact is the potential for the malicious beneficiary to drain a significant portion or even all of the contract's Ether.

  2. Unfair Distribution: Even if the contract isn't fully drained, the intended equal distribution among beneficiaries will be disrupted, with the malicious beneficiary receiving more than their fair share.

Tools Used

  • Manual Code Review

Recommendations

Use nonReentrant Modifier: The simplest and most effective solution is to apply the nonReentrant modifier from Openzeppelin's ReentrancyGuard library to the withdrawInheritedFunds function. This will prevent reentrant calls.

```diff
- function withdrawInheritedFunds(address _asset) external {
+ function withdrawInheritedFunds(address _asset) external nonReentrant {
// ...
}
```
Updates

Lead Judging Commences

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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