- name:
- governance-attacks
- description:
- Governance execution without multi-step state machine - proposals can be executed without passing through proper lifecycle states (Pending, Active, Succeeded, Queued, Executed), enabling bypass of voting periods and quorum requirements (cf. Build Finance DAO hostile takeover)
- category:
- vulnerability-pattern
- pattern_category:
- governance
- - regex:
- function\s+(execute|executeProposal)\s*\([^)]*\)\s+(external|public)[^}]*\{(?![\s\S]*(_execute|state\s*==|ProposalState|hasVoted))
- severity:
- High
- confidence:
- Medium
Governance Attack Patterns
Overview
On-chain governance is a privileged execution pipeline. Any flaw in how proposals are created, voted, queued, or executed can be equivalent to handing over protocol admin keys. Unlike many isolated bugs, governance flaws often impact treasury control, upgrade authority, risk parameters, and emergency controls in a single exploit path.
Modern attacks exploit the difference between governance latency and liquidity speed. Flash-loan capital allows an attacker to assemble temporary voting power, pass malicious actions, and unwind in one transaction unless voting weight is checkpointed at historical blocks. If timelocks and state transitions are weak, proposals can skip social review windows and execute immediately.
The highest-risk governance systems combine three anti-patterns: live balance voting, weak proposal gating, and permissive execution paths. Auditing governance must focus on lifecycle invariants, not only individual functions. The question is whether every privileged state change requires a full, observable sequence of controls before execution.
Key Attack Vectors
- Timelock bypass in
execute or queue flows allows immediate execution after proposal success.
- Flash-loan voting exploits live
balanceOf semantics when vote power is not snapshot based.
- Quorum manipulation occurs when total supply or vote weight is read at execution time instead of proposal snapshot time.
- Unprotected proposal creation enables spam, griefing, and malicious payload staging by any account.
- Single-step governance design collapses propose-vote-queue-execute into partial paths that skip lifecycle checks.
- Weak role boundaries allow governance executors to call arbitrary targets with arbitrary calldata and value.
- Incomplete cancellation rules let compromised proposers preserve malicious proposals through changing conditions.
- Vote delegation edge cases can mint effective influence if delegation checkpoints are inconsistent.
- Cross-governance integrations can create circular privilege where one module can reconfigure another's quorum.
- Emergency guardian paths can become permanent backdoors if sunset logic is absent.
Typical Exploit Chain
- Acquire temporary voting power through flash loan or borrow market.
- Submit or support a proposal with privileged target calls.
- Satisfy quorum using live-balance vote accounting.
- Bypass or minimize delay due to weak timelock enforcement.
- Execute payload to drain treasury, transfer ownership, or upgrade logic.
- Repay borrowed capital and exit before governance can react.
High-Value Governance Targets
- Treasury transfer executors.
- Upgrade proxy admin contracts.
- Oracle and risk parameter setters.
- Pause and unpause emergency modules.
- Bridge allowlists and relayer configuration.
- Fee recipient and distribution routes.
Detection Heuristics
Lifecycle Integrity Checks
- Verify a proposal state machine exists with explicit transitions: Pending -> Active -> Succeeded -> Queued -> Executed.
- Confirm
execute requires Succeeded or Queued state and cannot run directly from Pending or Active.
- Ensure
queue enforces a minimum delay through timelock metadata that cannot be zeroed by governance itself in the same proposal.
- Check replay protections so a proposal cannot execute twice.
Voting Power Semantics
- Flag direct use of
balanceOf() in vote calculation paths.
- Require
getPastVotes() and getPastTotalSupply() at a specific snapshot block.
- Validate that the snapshot block is set at proposal creation and cannot be user-provided at vote time.
- Review delegation checkpoint code for overflows, stale checkpoints, and self-delegation assumptions.
Proposal Gating and Anti-Spam
- Confirm
propose enforces proposalThreshold or role checks.
- Check whether threshold compares against historical votes, not current balances.
- Detect absence of proposal deposits, cooldowns, or proposer rate limits in high-noise systems.
- Verify guardian cancellation rights are bounded and transparent.
Timelock and Execution Constraints
- Check for
onlyTimelock or equivalent guard on privileged execution methods.
- Confirm target/callData hashing includes all fields used at execute time.
- Validate operation IDs are unique and consumed on execution.
- Ensure timelock admin rotation itself is timelocked.
Concrete Code Smells
function castVote(uint256 proposalId, uint8 support) external {
uint256 weight = token.balanceOf(msg.sender); // live-balance voting
_countVote(proposalId, msg.sender, support, weight);
}
function execute(uint256 proposalId) external {
// no state or timelock checks
_execute(proposalId);
}
function propose(bytes[] calldata calls) external {
// no threshold, no role, no deposit
_storeProposal(msg.sender, calls);
}
Prevention
Governance Architecture Controls
- Use OpenZeppelin
Governor with ERC20Votes checkpoints and TimelockController.
- Set non-trivial
votingDelay, votingPeriod, and timelock delay based on protocol TVL and response capacity.
- Keep proposer thresholds dynamic or governance-adjustable, but changes should be timelocked.
- Separate emergency powers from treasury powers, with explicit expiry of emergency authority.
Secure Vote Accounting
- Compute voter weight from historical checkpoints only.
- Freeze quorum math to snapshot-era total supply.
- Include anti-whale and anti-borrow constraints where applicable (lock periods, staking requirements, escrowed governance).
- Monitor concentrated delegation changes near snapshot boundaries.
Execution Hardening
- Gate execution through timelock-only entry points.
- Require proposal state assertions at each stage.
- Bind targets, values, calldata, and salt in operation hashes.
- Prevent same-block queue and execute operations.
Operational Safeguards
- Add off-chain alerting for proposal creation, queueing, and execution scheduling.
- Publish human-readable calldata decoders for governance payloads.
- Maintain an incident playbook for malicious proposal response.
- Use simulation tooling to preview proposal side effects before queueing.
Baseline Hardened Pattern
function castVote(uint256 proposalId, uint8 support) external {
ProposalCore storage p = _proposals[proposalId];
uint256 weight = token.getPastVotes(msg.sender, p.snapshotBlock);
require(weight > 0, "No voting power at snapshot");
_countVote(proposalId, msg.sender, support, weight);
}
function queue(uint256 proposalId) external {
require(state(proposalId) == ProposalState.Succeeded, "Not succeeded");
timelock.schedule(_operationHash(proposalId), MIN_DELAY);
}
function execute(uint256 proposalId) external {
require(state(proposalId) == ProposalState.Queued, "Not queued");
timelock.execute(_operationHash(proposalId));
}
Real-World Examples
Beanstalk (2022)
- Reference: https://rekt.news/beanstalk-rekt/
- Flash-loaned governance power was used to pass and execute a malicious proposal in a compressed timeline.
- Core lesson: snapshot voting and meaningful timelock delays are non-optional for treasury governance.
Build Finance DAO Takeover (2022)
- Reference: https://rekt.news/build-finance-rekt/
- Governance control was captured and treasury assets were redirected by hostile governance actions.
- Core lesson: weak lifecycle controls and insufficient proposer/voter safeguards enable hostile takeovers.
Audius Governance Exploit (2022)
- Reference: https://rekt.news/audius-rekt/
- Governance configuration weakness allowed attacker influence over protocol control paths.
- Core lesson: proposal and execution authorization must be explicit, layered, and invariant-tested.
Pattern-to-Exploit Mapping
timelock-bypass -> Beanstalk-like rapid execution risk.
flash-loan-governance -> temporary capital vote capture.
quorum-manipulation -> live-balance distortion of quorum and support.
unprotected-proposal -> governance spam and payload staging.
single-step-governance -> lifecycle bypass into privileged execution.
References
- OpenZeppelin Governor docs: https://docs.openzeppelin.com/contracts/4.x/governance
- OpenZeppelin TimelockController: https://docs.openzeppelin.com/contracts/4.x/api/governance#TimelockController
- Rekt News Beanstalk: https://rekt.news/beanstalk-rekt/
- Rekt News Build Finance: https://rekt.news/build-finance-rekt/
- Rekt News Audius: https://rekt.news/audius-rekt/
- Trail of Bits governance security discussions: https://blog.trailofbits.com/
- Sigma Prime solidity security notes: https://github.com/sigp/solidity-security-blog