Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: high
Likelihood: high

Cross-Operation State Desynchronization Allows Privileged Action Execution Without Required Quorum

Author Revealed upon completion

Root + Impact

Description

  • The multisig system is designed to ensure that each sensitive operation reaches the required confirmation threshold before it becomes executable.

However, confirmation tracking is not scoped to the full operation lifecycle, allowing confirmations obtained under one governance state to be reused after critical state changes, leading to execution of privileged actions without satisfying the intended quorum under the final state.

// @> Confirmations are tracked independently of operation-critical state
confirmTransaction(txId);
...
updateRequirement(newRequiredConfirmations);
...
executeTransaction(txId); // executes using stale confirmation set

Risk

Likelihood:

  • Occurs during normal governance flows where signer set or confirmation thresholds are modified

Common when protocol performs upgrades, signer rotations, or security responses

Impact:

  • Allows execution of high-privilege transactions with fewer confirmations than intended

Breaks the core multisig invariant: “execution reflects current governance requirements”

Proof of Concept

  • The transaction’s confirmation set is not invalidated or re-evaluated after the confirmation requirement changes. This causes a time-of-check vs time-of-use mismatch, where the transaction passes checks that no longer reflect governance reality.

// Initial state: required confirmations = 2
submitTransaction(target, value, data); // txId = X
confirmTransaction(X); // signer A
confirmTransaction(X); // signer B (X now executable)
// Governance change
submitTransaction(
address(this),
0,
abi.encodeWithSelector(updateRequirement.selector, 3)
);
executeTransaction(updateTxId);
// Now required confirmations = 3
// But tx X still has 2 confirmations recorded
executeTransaction(X); // executes despite insufficient quorum

Recommended Mitigation

  • Invalidate or rebind confirmations whenever governance-critical parameters change.

- remove this code
+ add this code
function updateRequirement(uint256 newRequirement) external onlyGovernance {
+ _invalidateAllPendingTransactions();
requiredConfirmations = newRequirement;
}
function executeTransaction(uint256 txId) external {
- require(isConfirmed(txId), "Not enough confirmations");
+ require(
+ isConfirmedUnderCurrentRequirement(txId),
+ "Confirmations invalid under current governance"
+ );
}

Support

FAQs

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

Give us feedback!