Governance Attacks
Detects vulnerabilities in DAO governance mechanisms including flash loan-based voting, timelock bypass, quorum manipulation, delegation exploits, treasury drain, and emergency action abuse.
Governance Attacks
Overview
Remediation Guide: How to Fix Governance Attacks
The governance attack detector identifies vulnerabilities in DAO governance mechanisms. On-chain governance systems — where proposals are created, voted on, and executed via smart contract — are high-value targets because a successful governance attack grants the attacker control over protocol parameters, treasury funds, and upgrade authority. Sigvex identifies six distinct vulnerability classes in governance contracts:
- Flash loan voting: Voting weight computed from token balance at the moment of the vote call rather than at proposal creation time. An attacker can borrow governance tokens via flash loan, vote with borrowed weight, then repay — all within one block.
- Timelock bypass: A proposal can be executed before the timelock delay has elapsed, or the timelock contract is controlled by an address that can be manipulated.
- Quorum manipulation: Quorum thresholds expressed as a fraction of
totalSupply()ortotalVotes()can be manipulated by flash-minting or burning governance tokens to reduce the effective quorum. - Delegation exploits: Token delegation allows double-counting voting power or lets an attacker redelegate tokens to themselves before a vote snapshot.
- Treasury drain: Governance proposals that transfer protocol treasury funds without multi-sig confirmation, mandatory time delays, or spending limits.
- Emergency action abuse: Emergency functions that bypass governance controls (timelocks, quorum) can be triggered by a small number of signers, enabling rapid fund extraction without normal safeguards.
Sigvex also correlates governance findings with oracle data: when a contract has both oracle manipulation risk and governance control over the oracle configuration, the combined confidence is elevated to reflect the combo attack surface.
Why This Is an Issue
Governance attacks have produced some of DeFi’s largest single-transaction losses:
- Beanstalk DAO (2022): $182M. An attacker used a flash loan to acquire a supermajority of governance tokens in a single transaction, passed an emergency proposal that transferred all protocol assets to their address, and repaid the flash loan — in one block. The protocol had no snapshot-based voting or timelock delay on emergency proposals.
- Tornado Cash (2023): Governance takeover via a malicious proposal that granted the attacker 1.2M votes and full contract control. Emergency proposals lacked independent verification.
- Audius (2022): $6M. A malicious proposal upgraded the staking contract and extracted treasury funds. The timelock was present but the upgrade proposal bypassed it through a delegatecall path.
- Build Finance (2022): $470K. An attacker with a small amount of governance tokens passed a proposal uncontested during a low-participation period when quorum was easily reached with borrowed tokens.
How to Resolve
// Before: Vulnerable — voting weight from current balance (flash-loanable)
contract VulnerableGovernor {
IGovernanceToken public token;
function castVote(uint256 proposalId, bool support) external {
// VULNERABLE: balance read at vote time, not snapshot time
uint256 votes = token.balanceOf(msg.sender);
_recordVote(proposalId, msg.sender, support, votes);
}
}
// After: Use ERC-20 vote snapshots (ERC-5805)
contract SecureGovernor is Governor, GovernorVotes, GovernorTimelockControl {
constructor(IVotes _token, TimelockController _timelock)
Governor("SecureDAO")
GovernorVotes(_token)
GovernorTimelockControl(_timelock)
{}
// Voting weight from the snapshot at proposal creation block, not current balance
function _getVotes(address account, uint256 blockNumber, bytes memory)
internal
view
override
returns (uint256)
{
return token.getPastVotes(account, blockNumber); // Historical, not current
}
// Quorum: 4% of total supply at the proposal snapshot block
function quorum(uint256 blockNumber) public pure override returns (uint256) {
return token.getPastTotalSupply(blockNumber) * 4 / 100;
}
// Timelock delay applied to all proposals before execution
function votingDelay() public pure override returns (uint256) { return 7200; } // 1 day
function votingPeriod() public pure override returns (uint256) { return 50400; } // 1 week
}
Examples
Vulnerable Code
contract VulnerableDAO {
IERC20 public govToken;
uint256 public constant QUORUM = 1_000_000e18;
mapping(uint256 => Proposal) public proposals;
struct Proposal {
address target;
bytes callData;
uint256 yesVotes;
uint256 noVotes;
bool executed;
uint256 createdAt;
}
function vote(uint256 proposalId, bool support) external {
// VULNERABLE: current balance can be borrowed via flash loan
uint256 weight = govToken.balanceOf(msg.sender);
if (support) {
proposals[proposalId].yesVotes += weight;
} else {
proposals[proposalId].noVotes += weight;
}
}
function execute(uint256 proposalId) external {
Proposal storage p = proposals[proposalId];
require(!p.executed, "Already executed");
require(p.yesVotes >= QUORUM, "Quorum not met");
// VULNERABLE: no timelock delay — can be executed immediately after creation
p.executed = true;
(bool ok,) = p.target.call(p.callData);
require(ok, "Execution failed");
}
}
Fixed Code
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
contract SecureDAO is
Governor,
GovernorSettings,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
constructor(IVotes _token, TimelockController _timelock)
Governor("SecureDAO")
GovernorSettings(
7200, // voting delay: 1 day in blocks
50400, // voting period: 1 week
100e18 // minimum proposal threshold
)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4) // 4% quorum
GovernorTimelockControl(_timelock)
{}
// Voting weight is locked at the proposal snapshot block via EIP-5805 checkpoints
// Flash loans cannot manipulate past balance checkpoints
}
Sample Sigvex Output
{
"detector_id": "governance-attacks",
"severity": "high",
"confidence": 0.75,
"description": "Function castVote() reads governance token balance at vote time via token.balanceOf(msg.sender) rather than from a historical checkpoint. An attacker can use a flash loan to borrow governance tokens, cast a supermajority vote, and repay the loan within a single block. Additionally, execute() applies no timelock delay between proposal passage and execution.",
"location": { "function": "castVote(uint256,bool)", "offset": 12 }
}
Detection Methodology
Sigvex identifies governance vulnerabilities through six targeted analysis passes:
- Flash loan voting detection: Identifies calls to
balanceOf()(ERC-20) orgetVotes()(ERC-5805) within voting functions and checks whether the token value is derived from current state (vulnerable) vs. a historical checkpoint viagetPastVotes(account, blockNumber)(protected). - Timelock bypass detection: Verifies that execution paths from proposal creation to proposal execution include a storage read or comparison against a timestamp/block-number delay variable. Proposals that can be created and executed in the same transaction are flagged.
- Quorum manipulation detection: Identifies quorum thresholds derived from live
totalSupply()reads without a snapshot block parameter. Flash-mint attacks can manipulate livetotalSupply(). - Delegation exploit detection: Checks for ERC-5805
delegate()calls that occur after proposal creation but before voting, which can redirect voting power without transferring tokens. - Treasury drain detection: Flags governance proposals that call
transfer(),transferFrom(), orsend()on high-value assets without a separate multi-sig or spending limit check. - Emergency action abuse: Detects functions marked as emergency or with restricted access control (Pausable, Guardian role) that bypass the standard proposal → timelock → execution flow.
Context modifiers:
- OpenZeppelin
Governor+GovernorTimelockControldetected: all confidence values multiplied by 0.3 - Audited governance library present (OpenZeppelin, Solmate, Solady): multiplied by 0.3
Pausablewith access control present: emergency abuse confidence reduced
Limitations
False positives:
- Off-chain governance systems (Snapshot + multisig execution) that use a custom on-chain executor may trigger flash-loan voting findings because the executor reads current state rather than past snapshots.
- Protocols where the timelock is enforced off-chain (e.g., a guardian must sign execution) may be flagged for missing on-chain timelock even when the off-chain process is secure.
False negatives:
- Cross-protocol governance attacks — where governance control over protocol A is used to manipulate protocol B — require cross-contract analysis beyond single-contract scope.
- Social engineering attacks on multisig signers are outside static analysis scope.
Related Detectors
- Flash Loan — flash loans are the primary amplifier for governance attacks
- Access Control — inadequate role separation allows proposal manipulation
- Oracle Manipulation — oracle + governance combo attacks