MultiSig Timelock

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

Timelock Bypass via Operation-Type Blind Enforcement

Root + Impact

Description

  • A multisig timelock must enforce a mandatory delay for all security-critical operations, ensuring signers and observers have time to react before execution.

  • The timelock enforcement is applied uniformly without distinguishing operation type, allowing privilege-changing or governance actions (e.g., signer additions, role grants) to execute after the same delay as regular transfers. This collapses the security boundary between fund movement and authority escalation, enabling rapid governance takeover.

// @> Timelock does not differentiate sensitive operations
function executeTransaction(uint256 txId) external {
require(block.timestamp >= transactions[txId].eta, "Timelock not expired");
_execute(txId);
}

Risk

Likelihood:

  • Governance or role-management transactions are proposed regularly.

Signers assume timelock sufficiently protects all operations equally.

Impact:

  • Attackers gain signer/admin roles with minimal delay.

Full multisig takeover possible without external detection window.

Proof of Concept

  • Although the timelock expires correctly, it does not account for risk asymmetry between transfers and governance actions. A single signer-set mutation permanently compromises system trust.

// Propose role escalation
submitTransaction(
address(accessControl),
0,
abi.encodeWithSelector(
grantRole.selector,
SIGNER_ROLE,
attacker
)
);
// Wait minimal delay
confirmTransaction(txId);
executeTransaction(txId);
// Attacker is now a signer → governance compromised

Recommended Mitigation

- remove this code
+ add this code
+ function executeTransaction(uint256 txId) external {
+ if (_isGovernanceOperation(transactions[txId])) {
+ require(
+ block.timestamp >= transactions[txId].eta + GOVERNANCE_DELAY,
+ "Extended timelock for governance"
+ );
+ }
require(block.timestamp >= transactions[txId].eta, "Timelock not expired");
_execute(txId);
}
Updates

Lead Judging Commences

kelechikizito Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Too generic

Appeal created

pushprakash23 Submitter
3 days ago
kelechikizito Lead Judge
3 days ago
kelechikizito Lead Judge
3 days ago
kelechikizito Lead Judge 2 days ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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

Give us feedback!