Governance attacks exploit flaws in voting and proposal mechanisms, allowing attackers to seize control of DAOs and protocols. Beanstalk ($182M, 2022) and Lido's near-attack demonstrated that governance vulnerabilities can drain entire treasuries. Attacks range from flash loan voting to voting weight issues to unguarded proposal execution. Firepan's HOUND AI detects governance vulnerabilities automatically during development and post-deployment monitoring, covering continuous analysis across the full contract lifecycle.
Governance attacks exploit protocols' decision-making mechanisms to execute unauthorized actions, steal treasury funds, or change critical parameters. Common attack vectors:
The vulnerability is critical because:
Governance attacks follow a pattern:
// VULNERABLE — example only
// Demonstrates: Governance Attack
// Do NOT use in production
pragma solidity ^0.8.0;
contract VulnerableGovernance {
address public gov;
mapping(address => uint256) public balance;
mapping(uint256 => Proposal) public proposals;
uint256 public proposalCount;
struct Proposal {
address target;
bytes data;
uint256 votesFor;
uint256 votesAgainst;
bool executed;
uint256 deadline;
}
// VULNERABLE: Voting power from current balance (flashable)
function vote(uint256 proposalId, bool support) public {
// VULNERABLE: Uses current balance, which can be flash loaned!
uint256 votingPower = balance[msg.sender];
if (support) {
proposals[proposalId].votesFor += votingPower;
} else {
proposals[proposalId].votesAgainst += votingPower;
}
}
// VULNERABLE: No access control on execution
function executeProposal(uint256 proposalId) public {
Proposal storage prop = proposals[proposalId];
require(!prop.executed);
require(block.timestamp > prop.deadline);
require(prop.votesFor > prop.votesAgainst);
// VULNERABLE: Anyone can execute!
prop.executed = true;
(bool success, ) = prop.target.call(prop.data);
require(success);
}
// VULNERABLE: No quorum check
function propose(address target, bytes memory data) public {
require(balance[msg.sender] > 0);
proposals[proposalCount] = Proposal({
target: target,
data: data,
votesFor: 0,
votesAgainst: 0,
executed: false,
deadline: block.timestamp + 7 days
});
proposalCount++;
}
}
contract GovernanceAttacker {
VulnerableGovernance public gov;
address public flashLoanProvider;
address public govToken;
function attackViaFlashLoan() public {
// STEP 1: Borrow governance token via flash loan
IFlashLoanProvider(flashLoanProvider).flashLoan(
address(this),
govToken,
1000000 ether, // Borrow huge amount
abi.encodeWithSignature("_executeAttack()")
);
}
function _executeAttack() internal {
// STEP 2: With borrowed tokens, vote on proposal
// Create proposal: transfer entire treasury to attacker
gov.propose(
address(gov),
abi.encodeWithSignature("stealTreasury(address)", address(this))
);
// STEP 3: Vote with borrowed tokens
gov.vote(gov.proposalCount() - 1, true);
// STEP 4: Wait for deadline, execute proposal
// (In flash loan attack, attacker prepares second transaction to execute after 7 days)
// STEP 5: Repay flash loan
IERC20(govToken).approve(flashLoanProvider, borrowed + fee);
}
}
contract QuorumBypassAttack {
VulnerableGovernance public gov;
function attackQuorum() public {
// Proposal might require quorum, but vote() doesn't check it
// Attacker creates proposal, votes with minimal balance
// Votes > quorum without actually meeting quorum requirement
gov.propose(address(0), ""); // Create proposal
gov.vote(0, true); // Vote with attacker's balance
gov.executeProposal(0); // Execute without proper quorum
}
}
| Protocol | Date | Loss | Root Cause | |----------|------|------|-----------| | Beanstalk | 2022-04 | $182M | Flash loan voting to pass malicious proposal | | Lido (near-miss) | 2021 | $10B+ at risk | Governance attack prevented last-minute | | Curve DAO | 2020-2023 | $5M+ | Governance parameter manipulation | | Compound (near-miss) | 2020 | $150M+ at risk | cToken voting weight bugs |
Manual detection requires voting mechanism review:
Red flags:
Firepan's HOUND AI performs voting mechanism analysis:
Firepan's HOUND AI engine identifies governance vulnerabilities across all monitored contracts.
1. Use Block Number Snapshot for Voting Power
Never use current balance; always snapshot at specific block:
mapping(address => mapping(uint256 => uint256)) public balanceAtBlock; // block → balance
function vote(uint256 proposalId, bool support) public {
Proposal memory prop = proposals[proposalId];
// Use balance at proposal block, not current balance
uint256 votingPower = balanceAtBlock[msg.sender][prop.snapshotBlock];
if (support) {
prop.votesFor += votingPower;
} else {
prop.votesAgainst += votingPower;
}
}
2. Implement Quorum Requirement
Require minimum participation for proposal passage:
uint256 public quorum = 40; // 40% of tokens must participate
function executeProposal(uint256 proposalId) public {
Proposal memory prop = proposals[proposalId];
uint256 totalVotes = prop.votesFor + prop.votesAgainst;
uint256 requiredVotes = (totalSupply * quorum) / 100;
require(totalVotes >= requiredVotes, "Quorum not met");
require(prop.votesFor > prop.votesAgainst);
// Execute proposal...
}
3. Use Timelock for Execution
Delay execution after proposal passes to allow emergency pause:
import "@openzeppelin/contracts/governance/TimelockController.sol";
contract GovernanceWithTimelock {
TimelockController timelock;
function execute(uint256 proposalId) public {
require(block.timestamp >= proposals[proposalId].delayedUntil);
// Execute with delay protection
}
}
4. Restrict Proposal Execution
Only allow governance or authorized addresses to execute:
function executeProposal(uint256 proposalId) public onlyGovernance {
Proposal storage prop = proposals[proposalId];
require(prop.votesFor > prop.votesAgainst);
prop.executed = true;
(bool success, ) = prop.target.call(prop.data);
require(success);
}
5. Use OpenZeppelin Governor
Implement battle-tested governance contract:
import "@openzeppelin/contracts/governance/Governor.sol";
contract MyGovernor is Governor {
constructor(IVotes _token)
Governor("MyDAO")
GovernorSettings(
1, // voting delay
50400, // voting period (1 week)
100000e18 // proposal threshold
)
{}
}
Q: What is governance attack in smart contracts?
A: Governance attack exploits voting flaws to seize control of DAOs, passing malicious proposals to steal treasury or change parameters. Flash loan voting is most common, where attackers borrow tokens, vote, and return tokens in same transaction.
Q: Which protocols have been exploited via governance?
A: Beanstalk ($182M, 2022) suffered flash loan governance attack. Lido and Compound faced critical vulnerabilities. Curve DAO governance parameters have been manipulated. Aggregate governance attack risk exceeds $20B+ across major DAOs.
Q: How does Firepan detect governance attack?
A: Firepan analyzes voting mechanisms, confirms snapshot blocks are used (not current balance), verifies quorum enforcement, checks execute() access control, identifies flash loan attack paths, and validates timelock delays.
Q: Can governance attacks be exploited after deployment?
A: Yes, governance vulnerabilities are immediately exploitable post-deployment. If flash loan voting is possible, attacker can drain treasury within one transaction. Beanstalk was exploited within hours of vulnerability.
Q: How do I prevent governance attacks?
A: Use block number snapshots for voting power (not current balance). Implement quorum requirement. Use timelock delay before execution. Restrict proposal execution to governance contract. Use OpenZeppelin Governor contract.
Governance attacks have cost DAOs $200M+, with potential risk exceeding $20B. Beanstalk's $182M loss demonstrated the severity. Modern governance uses block snapshots, quorums, and timelocks to eliminate flash loan voting. OpenZeppelin's Governor contract implements best practices. Firepan's HOUND AI detects governance vulnerabilities across all monitored contracts.
Start securing your smart contracts at https://app.firepan.com/
Firepan
12,453 contracts secured. 2,851 vulnerabilities blocked. 236 exploits prevented. Run a free surface scan — results in minutes, no credit card required.
Run Free Scan →