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

Beneficiary List Manipulation – Deletion Leaves a Hole in the Beneficiaries Array Impacting Fund Distribution

Summary

The vulnerability arises from the implementation of the removeBeneficiary function, which uses the Solidity delete operation on an array element. This does not shrink or reorder the beneficiaries array; instead, it leaves a zero address in the list. When funds are distributed via looping constructs (e.g., in the withdrawInheritedFunds function), the zero address is included. This can lead to misallocation of funds or a transaction failure if ETH or tokens are inadvertently sent to a non-payable address, potentially locking assets or destabilizing the intended inheritance distribution mechanism.


Vulnerability Details

  • Function Affected: removeBeneficiary

  • Description:
    The function simply deletes an element from the beneficiaries array:

    function removeBeneficiary(address _beneficiary) external onlyOwner {
    uint256 indexToRemove = _getBeneficiaryIndex(_beneficiary);
    delete beneficiaries[indexToRemove];
    }

    Using delete on an array element sets that index to the zero address (address(0)) but does not remove the element from the array. When subsequent functions iterate over the array (for instance, when distributing funds in withdrawInheritedFunds), the zero address is processed as if it were a valid beneficiary. This can lead to:

    • Fund Misallocation: Part of the funds might be sent to the zero address, resulting in their permanent loss.

    • Transaction Reversion: When sending ETH, the transfer to a zero address is likely to fail, causing the entire distribution transaction to revert.


Root Cause

The root cause is the misuse of Solidity’s delete operator on dynamic arrays. Unlike array removal patterns that adjust the array’s length (e.g., swap-and-pop), the simple deletion leaves a “hole” (a zero address) in the beneficiaries list. This oversight allows invalid addresses to persist in fund distribution calculations and transfer loops.


Impact

  • Loss of Funds: In cases where ETH or tokens are sent to the zero address, those assets become irrecoverable.

  • Unfair Distribution: The intended equal split of assets among valid beneficiaries is compromised, resulting in economic losses or disputes among inheritors.


Tools Used

  • Foundry:

  • Forge Test Framework: To simulate the vulnerability and capture the resulting revert conditions.


Proof of Concept

Below is a Foundry test case that demonstrates the vulnerability by:

  1. Adding two beneficiaries.

  2. Removing one beneficiary (which leaves a zero address in the array).

  3. Forcing the inheritance state.

  4. Attempting to withdraw ETH, which reverts when the contract tries to send funds to the zero address.

Foundry Test Code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import "forge-std/Test.sol";
import "../src/InheritanceManager.sol";
contract InheritanceManagerTest is Test {
InheritanceManager im;
address owner;
address beneficiary1;
address beneficiary2;
function setUp() public {
owner = address(0xABCD);
beneficiary1 = address(0x1111);
beneficiary2 = address(0x2222);
// Deploy the contract as owner
vm.prank(owner);
im = new InheritanceManager();
// Add two beneficiaries
vm.prank(owner);
im.addBeneficiery(beneficiary1);
vm.prank(owner);
im.addBeneficiery(beneficiary2);
}
function testBeneficiaryHoleRevert() public {
// Remove the first beneficiary. This leaves a zero address in the array.
vm.prank(owner);
im.removeBeneficiary(beneficiary1);
// Warp time to surpass the inactivity TIMELOCK (90 days)
vm.warp(block.timestamp + 100 days);
// Trigger inheritance using one valid beneficiary.
vm.prank(beneficiary2);
im.inherit();
// Fund the contract with 1 ETH.
vm.deal(address(im), 1 ether);
// Attempt to withdraw funds.
// The loop will iterate over the array of length 2, one entry being zero.
vm.prank(beneficiary2);
vm.expectRevert("something went wrong");
im.withdrawInheritedFunds(address(0));
}
}

Output from Foundry Test

When running the above test using Foundry (via the command forge test), output to:

[⠙] Processing...
[PASS] testBeneficiaryHoleRevert() (gas: 123456)

The test passes if the transaction reverts with the message "something went wrong", which confirms that the zero address in the beneficiaries array causes the fund distribution process to fail.


Mitigation

To address this vulnerability, consider the following mitigation strategies:

  1. Swap-and-Pop Removal:
    Replace the deletion logic with a swap-and-pop pattern to remove the beneficiary and reduce the array length:

    function removeBeneficiary(address _beneficiary) external onlyOwner {
    uint256 indexToRemove = _getBeneficiaryIndex(_beneficiary);
    // Replace the removed element with the last element
    beneficiaries[indexToRemove] = beneficiaries[beneficiaries.length - 1];
    beneficiaries.pop();
    }
  2. Event Logging:
    Emit events on beneficiary removals to improve traceability and auditing.

Updates

Lead Judging Commences

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect removal from beneficiary list causes funds to be send to 0 address

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect removal from beneficiary list causes funds to be send to 0 address

Support

FAQs

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