MultiSig Timelock

First Flight #55
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

19Dec2025_AuditReport1_MultiSigTimelock

Root + Impact

Description

  • Owner Can Bypass Multisig Requirements Through Signer Manipulation

  • The owner has unilateral control to add and remove signers without any multisig approval. This creates a critical centralization risk where the owner can: 1) Remove all existing signers except themselves, 2) Add new signers under their control, 3) Confirm and execute any transaction bypassing the intended multisig security. Since the owner is automatically a signer and only 3 confirmations are required, they can effectively control the entire wallet by manipulating the signer set.

// Root cause in the codebase with @> marks to highlight the relevant section
// Step 1: Owner removes legitimate signers
multiSigTimelock.revokeSigningRole(legitimateSigner2);
multiSigTimelock.revokeSigningRole(legitimateSigner3);
multiSigTimelock.revokeSigningRole(legitimateSigner4);
// Step 2: Owner adds controlled addresses
multiSigTimelock.grantSigningRole(ownerControlledAddress1);
multiSigTimelock.grantSigningRole(ownerControlledAddress2);
// Step 3: Owner proposes malicious transaction
uint256 txId = multiSigTimelock.proposeTransaction(attackerAddress, address(this).balance, "");
// Step 4: Owner and controlled addresses confirm (3 confirmations)
multiSigTimelock.confirmTransaction(txId); // owner confirms
vm.prank(ownerControlledAddress1);
multiSigTimelock.confirmTransaction(txId);
vm.prank(ownerControlledAddress2);
multiSigTimelock.confirmTransaction(txId);
// Step 5: Execute and drain funds
multiSigTimelock.executeTransaction(txId);

Risk

Likelihood:

  • The owner has unilateral control to add and remove signers without any multisig approval. This creates a critical centralization risk where the owner can: 1) Remove all existing signers except themselves, 2) Add new signers under their control, 3) Confirm and execute any transaction bypassing the intended multisig security. Since the owner is automatically a signer and only 3 confirmations are required, they can effectively control the entire wallet by manipulating the signer set.

Impact:

  • Complete compromise of multisig security. A malicious or compromised owner can drain all funds from the wallet by replacing legitimate signers with addresses they control, then executing arbitrary transactions.

Proof of Concept

function grantSigningRole(address _account) external nonReentrant onlyOwner noneZeroAddress(_account) {
if (s_isSigner[_account]) {
revert MultiSigTimelock__AccountIsAlreadyASigner();
}
if (s_signerCount >= MAX_SIGNER_COUNT) {
revert MultiSigTimelock__MaximumSignersReached();
}
s_signers[s_signerCount] = _account;
s_isSigner[_account] = true;
s_signerCount += 1;
_grantRole(SIGNING_ROLE, _account);
}
function revokeSigningRole(address _account) external nonReentrant onlyOwner noneZeroAddress(_account) {
// ... owner can remove any signer except themselves
}

Recommended Mitigation

// Require multisig approval for adding/removing signers:
// Add these state variables
mapping(uint256 => SignerChangeRequest) private s_signerChangeRequests;
uint256 private s_signerChangeRequestCount;
struct SignerChangeRequest {
address account;
bool isAddition; // true for add, false for remove
uint256 confirmations;
bool executed;
}
// Replace grantSigningRole with:
function proposeSignerChange(address _account, bool _isAddition) external onlyRole(SIGNING_ROLE) {
uint256 requestId = s_signerChangeRequestCount++;
s_signerChangeRequests[requestId] = SignerChangeRequest({
account: _account,
isAddition: _isAddition,
confirmations: 0,
executed: false
});
}
// Add confirmation and execution functions similar to transaction flow
Updates

Lead Judging Commences

kelechikizito Lead Judge 4 days ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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

Give us feedback!