MultiSig Timelock

First Flight #55
Beginner FriendlyWallet
100 EXP
Submission Details
Impact: high
Likelihood: low

M02. Multisig Fails to Protect Funds Against Owner Key Compromise Due to Unilateral Signer Management

Author Revealed upon completion

Root + Impact

Description

  • The intended behavior of the contract is to protect funds using a multisignature scheme, where multiple independent signers must approve transactions, thereby mitigating the risk of a single compromised key (including the owner) leading to fund loss.

  • The specific issue is that the owner retains unilateral and immediate control over signer management. If the owner private key is compromised, the attacker can revoke honest signers and add malicious signers, preserving quorum and fully bypassing the multisig’s protective guarantees, ultimately allowing fund theft.

// @> Owner has unilateral authority to modify the signer set
function grantSigningRole(address _account) external nonReentrant onlyOwner noneZeroAddress(_account) {
...
_grantRole(SIGNING_ROLE, _account);
}
// @> Owner can revoke any signer without multisig approval
function revokeSigningRole(address _account) external nonReentrant onlyOwner noneZeroAddress(_account) {
...
_revokeRole(SIGNING_ROLE, _account);
}

Risk

Likelihood:

  • Owner private key compromise occurs through phishing, key reuse, malware, or insecure key storage.

  • Governance actions (signer rotation or maintenance) are routinely performed using the owner key, increasing its exposure.

Impact:

  • Complete loss of all funds held by the multisig despite the presence of multiple signers.

  • False sense of security for users who assume multisig protection extends to owner compromise scenarios.

Proof of Concept

Explanation

This proof demonstrates that once the owner key is compromised, the attacker can fully control the signer set. By removing honest signers and adding malicious ones, the attacker restores quorum and executes a malicious transaction that drains funds. All protocol checks pass, and the multisig provides no resistance.

function testOwnerCompromiseLeadsToFundTheft() public {
// Setup initial signers
multiSigTimelock.grantSigningRole(SIGNER_TWO);
multiSigTimelock.grantSigningRole(SIGNER_THREE);
multiSigTimelock.grantSigningRole(SIGNER_FOUR);
// Fund the multisig
vm.deal(address(multiSigTimelock), 10 ether);
// Attacker controls owner key
address ATTACKER_SIGNER_1 = makeAddr("attacker1");
address ATTACKER_SIGNER_2 = makeAddr("attacker2");
// Owner removes honest signers
multiSigTimelock.revokeSigningRole(SIGNER_TWO);
multiSigTimelock.revokeSigningRole(SIGNER_THREE);
// Owner adds malicious signers
multiSigTimelock.grantSigningRole(ATTACKER_SIGNER_1);
multiSigTimelock.grantSigningRole(ATTACKER_SIGNER_2);
// Propose malicious transaction
uint256 txnId = multiSigTimelock.proposeTransaction(
address(ATTACKER_SIGNER_1),
10 ether,
""
);
// Malicious signers confirm
vm.prank(address(this)); // owner
multiSigTimelock.confirmTransaction(txnId);
vm.prank(ATTACKER_SIGNER_1);
multiSigTimelock.confirmTransaction(txnId);
vm.prank(ATTACKER_SIGNER_2);
multiSigTimelock.confirmTransaction(txnId);
// Execute transaction and drain funds
vm.prank(address(this));
multiSigTimelock.executeTransaction(txnId);
assertEq(address(multiSigTimelock).balance, 0);
}

Recommended Mitigation

Signer management must itself be protected by the multisig, ensuring that owner compromise alone cannot alter the signer set.

Function modified: grantSigningRole and revokeSigningRole

  • Remove unilateral owner authority

  • Require multisig-approved transactions to add or remove signers, using the same principle that to valide a transaction (not shown here)

- function grantSigningRole(address _account) external onlyOwner {
- function revokeSigningRole(address _account) external onlyOwner {

Alternatively, signer changes should be executed exclusively via executeTransaction with a timelock and quorum, or the owner role itself should be transferred to a multisig contract.

Support

FAQs

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

Give us feedback!