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

Missing Access Control in inherit() Function

Summary

The inherit() function lacks the onlyBeneficiaryWithIsInherited modifier, allowing any address to call it. When there is only one beneficiary, this can result in unauthorized ownership transfer to any caller, completely bypassing the intended inheritance mechanism.

Vulnerability Details

The current implementation:

function inherit() external {
if (block.timestamp < getDeadline()) {
revert InactivityPeriodNotLongEnough();
}
if (beneficiaries.length == 1) {
owner = msg.sender; // Critical: No access control!
_setDeadline();
} else if (beneficiaries.length > 1) {
isInherited = true;
} else {
revert InvalidBeneficiaries();
}
}

The issue is that when beneficiaries.length == 1, the function:

  1. Transfers ownership to msg.sender without verifying if they are the legitimate beneficiary

  2. Resets the deadline, giving the attacker full control

  3. Allows complete bypass of the inheritance mechanism

Other functions that handle inheritance properly use the onlyBeneficiaryWithIsInherited modifier:

Impact

HIGH

  • Direct risk to all funds in the contract

  • Complete compromise of contract ownership

  • Bypass of core inheritance mechanism

  • Attacker gains full control of:

Likelihood: High

Exploit Scenario:

  1. Alice sets up an inheritance wallet with Bob as the only beneficiary

  2. Alice deposits 100 ETH and various tokens into the contract

  3. The 90-day deadline passes without activity

  4. Malicious actor Eve, who is not the beneficiary, calls inherit()

  5. Since beneficiaries.length == 1, Eve becomes the new owner instead of Bob

  6. Eve now has full control of the contract and can:

    // Steal all ETH
    function steal() external {
    InheritanceManager manager = InheritanceManager(WALLET_ADDRESS);
    // First become owner through inherit()
    manager.inherit();
    // Now we can drain all ETH
    manager.sendETH(address(this).balance, attacker);
    // Drain all tokens
    manager.sendERC20(TOKEN_ADDRESS, manager.balanceOf(TOKEN_ADDRESS), attacker);
    // Even interact with external protocols using the wallet's funds
    manager.contractInteractions(DEFI_PROTOCOL, withdrawData, 0, false);
    }
  7. Bob, the legitimate beneficiary, permanently loses access to their inheritance

The attack:

  • Requires no special permissions

  • Only needs to wait for the deadline

  • Results in complete loss of funds

  • Cannot be reversed once executed

  • No special conditions required beyond single beneficiary setup

  • Can be executed by any external address

  • Only requires waiting for deadline

  • No complex exploitation steps needed

Tools Used

  • Manual review

  • Code inspection

  • Foundry tests

Recommendations

Add the onlyBeneficiaryWithIsInherited modifier to the inherit() function:

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

This change would:

  • Ensure only legitimate beneficiaries can trigger inheritance

  • Protect against unauthorized ownership transfer

  • Maintain consistency with other inheritance-related functions

  • Preserve the intended security model of the contract

Updates

Lead Judging Commences

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

Inherit depends on msg.sender so anyone can claim the contract

Support

FAQs

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