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

Inheritance Takeover Vulnerability

Summary

The InheritanceManager.sol contract has a critical flaw in its inherit function. After a 90-day inactivity period, if there is exactly one beneficiary, any address can call inherit() and become the new owner of the contract. There’s no check to verify that the caller (msg.sender) is the listed beneficiary, allowing an attacker to hijack the contract and gain full control over its assets and functions.

Vulnerability Details

File: InheritanceManager.sol

  • Function: inherit()

Impact

An attacker can take ownership of the contract, accessing all funds (e.g., ETH, tokens), NFTs, and administrative privileges.

How the Exploit Works

The inherit function is designed to transfer ownership after 90 days of owner inactivity. When there’s one beneficiary, it simply sets owner = msg.sender without verifying the caller’s identity. An attacker can exploit this by waiting out the 90-day period and calling the function from any address.

Step-by-Step Exploit Execution

  1. Setup:

    • The contract is deployed with an owner and one beneficiary (beneficiary1).

    • The owner adds beneficiary1 using addBeneficiery() (note: likely a typo for addBeneficiary).

    • The owner stops interacting with the contract, triggering the 90-day countdown.

  2. Waiting Period:

    • Time advances past 90 days (e.g., using vm.warp in testing or real-world time).

  3. Exploit:

    • An attacker, using an unrelated address (e.g., attacker), calls inherit().

    • Since beneficiaries.length == 1, the function executes owner = msg.sender, making attacker the new owner.

  4. Result:

    • The attacker now owns the contract and can:

      • Withdraw all ETH and tokens.

      • Manage NFTs (e.g., burn or transfer via NFTFactory).

      • Add or remove beneficiaries at will.

Tools Used

my own vuln scanner, AI and foundry

Proof of Concept (PoC)

function test_InheritanceTakeover() public {
vm.startPrank(owner); // Owner deploys and configures the contract
manager = new InheritanceManager();
manager.addBeneficiery(beneficiary1); // Add one beneficiary
vm.stopPrank();
vm.warp(block.timestamp + 90 days + 1); // Fast-forward past 90 days
vm.prank(attacker); // Attacker calls inherit()
manager.inherit();
assertEq(manager.getOwner(), attacker); // Verify attacker is now owner
}

Recommendations

Modify the inherit function to restrict ownership transfer to the designated beneficiary when there is only one beneficiary:

Explanation

  • Change: Added if (msg.sender != beneficiaries[0]) revert NotBeneficiary(msg.sender); to ensure only the beneficiary can claim ownership.

  • Why It Works: This check prevents unauthorized callers from taking over the contract, aligning the function with its intended purpose.

  • Best Practice: Always validate caller permissions in functions that modify critical state, such as ownership.

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

Lead Judging Commences

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