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

Critical Unauthorized Ownership Transfer Vulnerability: Single-Beneficiary Caller Validation Flaw in InheritanceManager Contract

Summary

The InheritanceManager contract is designed to facilitate asset inheritance by enabling ownership transfers or asset claims after a specified period of owner inactivity. However, a critical vulnerability in the inherit function undermines this functionality. Specifically, when there is a single beneficiary, the function lacks proper access control and validation, allowing any external caller to assume ownership of the contract after the inactivity deadline, regardless of whether they are the designated beneficiary. This flaw exposes the contract to unauthorized ownership transfers, risking the loss of assets and control to malicious actors.

Vulnerability Details

Lack of Caller Validation for Single Beneficiary

  • When the number of beneficiaries is exactly one (beneficiaries.length == 1), the function assigns owner = msg.sender without verifying that msg.sender matches the address stored in the beneficiaries array.

  • This omission allows any external address to call inherit() after the deadline and claim ownership, circumventing the intended inheritance process.

    Unrestricted External Access

    • The function is marked external, making it callable by any address.

    • Once the inactivity deadline (getDeadline()) is reached, any caller can invoke inherit(), and in the single-beneficiary case, they will automatically become the new owner.

    No Ownership Transfer Safeguards

    • In the single-beneficiary scenario, the function directly reassigns ownership to msg.sender without additional checks or confirmation mechanisms.

    • This design contradicts the contract’s purpose of securely transferring ownership to a pre-designated beneficiary.

Vulnerable Code

The inherit function is implemented as follows in the InheritanceManager contract:

function 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();
}
}

Example Scenario

Consider the following sequence of events:

  • Deployment: The contract is deployed with an owner who designates Alice as the sole beneficiary.

  • Inactivity: The owner remains inactive, and the deadline is surpassed (i.e., block.timestamp >= getDeadline()).

  • Exploitation: A malicious actor, Bob, calls inherit().

  • Result: Since beneficiaries.length == 1, the contract assigns owner = Bob, granting him full control over the contract and its assets, despite Alice being the intended beneficiary.

This scenario demonstrates how the flaw enables unauthorized parties to hijack the contract, rendering the inheritance mechanism ineffective.

Proof of Concept

Below is a Foundry test demonstrating the vulnerability:

function test_inheritVulnerability() public {
address bob = makeAddr("bob");
vm.prank(owner);
im.addBeneficiery(user1);
vm.warp(1 + 90 days);
vm.prank(bob);
im.inherit();
assertEq(bob, im.getOwner());
}

Below is a Foundry test demonstrating the vulnerability:

Impact

The absence of proper validation and access control in the inherit function introduces significant risks:

  • Unauthorized Ownership Transfer
    Any external actor can call inherit() after the deadline and assume ownership if there is only one beneficiary, effectively taking control of the contract.

  • Asset Loss
    The new, unauthorized owner gains the ability to manage or withdraw the contract’s assets, potentially leading to theft or misappropriation.

  • Violation of Inheritance Intent
    The contract fails to enforce the owner’s intent to transfer ownership to a specific beneficiary, eroding trust in the inheritance system.

  • Potential for Disruption
    Even non-malicious callers could inadvertently trigger inherit(), disrupting the intended inheritance process and causing disputes among legitimate beneficiaries.

Tools Used

Manual Review

Recommendations

To mitigate this vulnerability and align the contract with its intended purpose, the following remediation steps are proposed:

  1. Implement Caller Validation for Single Beneficiary
    Update the inherit function to verify that msg.sender is the designated beneficiary before transferring ownership in the single-beneficiary case.

    • Updated Code:

      if (beneficiaries.length == 1) {
      require(msg.sender == beneficiaries[0], "Not the beneficiary");
      owner = msg.sender;
      _setDeadline();
      }
  2. Restrict Access for Multiple Beneficiaries
    When there are multiple beneficiaries (beneficiaries.length > 1), ensure that only a listed beneficiary can trigger the inheritance process.

    • Updated Code:

      } else if (beneficiaries.length > 1) {
      require(_isBeneficiary(msg.sender), "Not a beneficiary");
      isInherited = true;
      }
    • Helper Function:

      function _isBeneficiary(address _address) internal view returns (bool) {
      for (uint256 i = 0; i < beneficiaries.length; i++) {
      if (beneficiaries[i] == _address) {
      return true;
      }
      }
      return false;
      }
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.