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

Removing beneficiary in `InheritanceManager` ends in funds distribution failure

Summary

There is a issue in the InheritanceManager::removeBeneficiaryfunction when using deleteto remove beneficiaries from the beneficiariesarray where the array gets populated with address(0). When calling the InheritanceManager::withdrawInheritedFundsfunction the transaction will revert locking funds in contract. This funds will be locked indefinatly if ownercannot access their wallet.

Vulnerability Details

Add this test to test suite:

function test_withdrawInheritedFundsERC20AfterRemovingBeneficiary() public {
address user2 = makeAddr("user2");
vm.startPrank(owner);
im.addBeneficiery(user1);
im.addBeneficiery(user2);
im.removeBeneficiary(user2);
vm.stopPrank();
usdc.mint(address(im), 9e18);
vm.warp(1 + 90 days);
vm.startPrank(user1);
im.inherit();
vm.expectRevert();
im.withdrawInheritedFunds(address(usdc));
vm.stopPrank();
}

Because user2has been removed from beneficiariesarray it has been substituted with an address(0)so when the withdrawInheritedFundsfunction tries to distribute funds the transaction reverts because it is trying to send funds to the address(0).

├─ [31483] InheritanceManager::withdrawInheritedFunds(ERC20Mock: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f])
│ ├─ [851] ERC20Mock::balanceOf(InheritanceManager: [0x88F59F8826af5e695B13cA934d6c7999875A9EeA]) [staticcall]
│ │ └─ ← [Return] 9000000000000000000 [9e18]
│ ├─ [25772] ERC20Mock::transfer(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], 4500000000000000000 [4.5e18])
│ │ ├─ emit Transfer(from: InheritanceManager: [0x88F59F8826af5e695B13cA934d6c7999875A9EeA], to: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], value: 4500000000000000000 [4.5e18])
│ │ ├─ storage changes:
│ │ │ @ 0x266f7372a76c4363ea04e26dfd6a2a19d44ec1cadb0d91060c1fb98ef6adc19b: 00x0000000000000000000000000000000000000000000000003e73362871420000
│ │ │ @ 0x046b8fe1df5e368589a8e0acb67ff926456dfd1af6a8b6b77fa4a7c8dea1c3fa: 0x0000000000000000000000000000000000000000000000007ce66c50e28400000x0000000000000000000000000000000000000000000000003e73362871420000
│ │ └─ ← [Return] true
@> │ ├─ [1076] ERC20Mock::transfer(0x0000000000000000000000000000000000000000, 4500000000000000000 [4.5e18])
│ │ └─ ← [Revert] ERC20InvalidReceiver(0x0000000000000000000000000000000000000000)

Impact

Funds could be locked in contract indefinatly if owner has lost access control over contract.

Tools Used

Manual code review

Recommendations

Adjust the removeBeneficiary function:

function removeBeneficiary(address _beneficiary) external onlyOwner {
uint256 indexToRemove = _getBeneficiaryIndex(_beneficiary);
- delete beneficiaries[indexToRemove];
+ uint256 lastIndex = beneficiaries.length - 1;
// If not the last element, swap with the last element
+ if (indexToRemove != lastIndex) {
+ beneficiaries[indexToRemove] = beneficiaries[lastIndex];
+ }
// Remove the last element
+ beneficiaries.pop();
}

Then add this if statement to withdrawInheritedFunds:

function withdrawInheritedFunds(address _asset) external {
if (!isInherited) {
revert NotYetInherited();
}
uint256 divisor = beneficiaries.length;
if (_asset == address(0)) {
uint256 ethAmountAvailable = address(this).balance;
// q does funds get stuck in the contract because of rounding errors?
uint256 amountPerBeneficiary = ethAmountAvailable / divisor;
for (uint256 i = 0; i < divisor; i++) {
address payable beneficiary = payable(beneficiaries[i]);
(bool success,) = beneficiary.call{value: amountPerBeneficiary}("");
require(success, "something went wrong");
}
} else {
uint256 assetAmountAvailable = IERC20(_asset).balanceOf(address(this));
uint256 amountPerBeneficiary = assetAmountAvailable / divisor;
for (uint256 i = 0; i < divisor; i++) {
+ if (beneficiaries[i] != address(0)) {
IERC20(_asset).safeTransfer(beneficiaries[i], amountPerBeneficiary);
+ }
}
}
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 10 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.

Give us feedback!