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

Owner cannot receive full amount of inherited funds

Summary

When the owner adds themselves as the sole beneficiary and triggers inheritance, they cannot withdraw funds through withdrawInheritedFunds() because the isInherited flag is never set to true for a single beneficiary. Instead, when there's only one beneficiary, the contract transfers ownership but doesn't set the inheritance flag, blocking access to the withdrawal functionality.

Vulnerability Details

The vulnerability stems from the logic in the inherit() function, which handles single beneficiaries differently than multiple beneficiaries:

unction inherit() external {
if (block.timestamp < getDeadline()) {
revert InactivityPeriodNotLongEnough();
}
if (beneficiaries.length == 1) {
owner = msg.sender;
_setDeadline();
} else if (beneficiaries.length > 1) {
isInherited = true;
} else {
revert InvalidBeneficiaries();
}
}

When there's only one beneficiary, the contract transfers ownership to the caller but doesn't set isInherited = true. This prevents the withdrawInheritedFunds() function from being used, as it has the following check:

function withdrawInheritedFunds(address _asset) external {
if (!isInherited) {
revert NotYetInherited();
}
// Rest of function...
}

This creates a situation where an owner who sets themselves as the sole beneficiary cannot access the withdrawal mechanism after inheritance. While they regain ownership and can use owner-level functions like sendETH and sendERC20, they must manually extract each asset type rather than using the streamlined withdrawal function.

Impact

This vulnerability has significant impact:

  1. Locked Functionality: The single-beneficiary inheritance path prevents access to the dedicated withdrawal function, creating inconsistent behavior

  2. Self-Recovery Limitation: An owner trying to recover their own wallet through inheritance will gain ownership but lack the intended withdrawal mechanism

  3. UX Inconsistency: Different number of beneficiaries leads to drastically different inheritance behavior with no clear documentation

  4. Redundant Gas Costs: An owner seeking to recover funds must extract each asset type individually rather than using the batch distribution function

In case owner decides to add another beneficiare and then removes it to bypass the if (beneficiaries.length == 1 then due the suboptiomal design - deleting array value the beneficiaries array will get an empty value for the removed beneficiaroes but the amounts will be split /beneficiaries.length, inlcuding the 0 address and the owner might not even realized he will get just a portion of the funds, rest going to the 0-th address.

Tools Used

  • Manual code review

  • Foundry testing framework

PoC

this reverts but is supposed to not.

function testBeneficiaryCantInheritProperly() public {
vm.deal(address(im), 3e18);
vm.startPrank(owner);
im.addBeneficiery(owner);
vm.warp(block.timestamp + 90 days);
im.inherit();
vm.expectRevert();
im.withdrawInheritedFunds(address(0));
vm.stopPrank();
console.log("owner address: ", address(owner).balance);
}
function testBeneficiaryCantInheritProperly() public {
vm.deal(address(im), 3e18);
vm.startPrank(owner);
im.addBeneficiery(user1);
im.removeBeneficiary(user1);
im.addBeneficiery(owner);
vm.warp(block.timestamp + 90 days);
im.inherit();
vm.expectRevert();
im.withdrawInheritedFunds(address(0));
vm.stopPrank();
console.log("owner address: ", address(owner).balance);
}

In the above case the owner seeking workaround might artificially add and remove beneficiary to bypass a check but then ends up splitting the assets with address 0

Recommendations

add a flag update:

function inherit() external {
if (block.timestamp < getDeadline()) {
revert InactivityPeriodNotLongEnough();
}
if (beneficiaries.length == 1) {
owner = msg.sender;
_setDeadline();
isInherited = true; // Add this line
} else if (beneficiaries.length > 1) {
isInherited = true;
} else {
revert InvalidBeneficiaries();
}
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!