Governance Attack Exploit Generator
Sigvex exploit generator that validates DAO governance vulnerabilities by simulating flash loan vote acquisition, insufficient quorum scenarios, and timelock bypass.
Governance Attack Exploit Generator
Overview
The governance attack exploit generator validates findings from the governance-attacks detector by simulating scenarios where an attacker acquires governance tokens via flash loan within a single transaction, casts votes on a malicious proposal, and executes it before the snapshot lock mechanism (if any) can prevent participation.
The most prominent example is the Beanstalk DAO flash loan governance attack (April 2022, $182M). The attacker borrowed $1B in DAI/USDC/USDT via Aave, converted them to Beanstalk governance tokens, voted in favor of a malicious proposal that transferred all protocol funds to the attacker’s address, executed immediately (no timelock), and repaid the flash loan — all within a single transaction.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
- Setup: The attacker identifies a governance contract without a voting snapshot mechanism (votes are counted at proposal execution time, not at proposal creation).
- Flash loan acquisition: Within a single transaction, the attacker borrows a massive amount of the governance token. With
1_000_000_000voting tokens, the attacker’s vote weight exceeds all other participants combined. - Vote: The attacker calls
castVote(proposalId, FOR). The contract records the flash-loan-inflated balance as the vote weight. - Execute: If the proposal has no timelock (or a timelock that already expired), the attacker calls
execute(proposalId). The malicious proposal runs — typically draining treasury funds or updating privileged parameters. - Repay: The attacker repays the flash loan with a portion of extracted funds. Net profit is the total protocol treasury minus flash loan fees.
Additional governance vulnerability patterns detected:
- Insufficient quorum: A proposal passes with only 1% participation if quorum is set too low.
- Whale control: A single address holds >50% of voting power (no diversification requirement).
- Proposal spam: Governance contracts without proposal fees or caps can be DoS’d by flooding with spam proposals.
Exploit Mechanics
The generator runs four execution scenarios against the governance contract bytecode:
| Scenario | Token balance (slot 0) | Quorum (slot 2) | Timelock (slot 3) | Snapshot (slot 4) |
|---|---|---|---|---|
| 1 — Normal voter | 1,000 | 10% | 48h | Required |
| 2 — Flash loan attack | 1,000,000,000 | 10% | 0 (no timelock) | Not required |
| 3 — Low quorum | 1,000 | 1% | 0 | Not required |
| 4 — Protected | 1,000 | 10% | 48h | Required |
Calldata targets castVote(uint256,uint8) (selector 0x56781388) followed by execute(uint256) (selector 0xa217fddf).
Critical findings are raised when:
- Flash loan scenario succeeds but normal scenario would not meet quorum → flash loan governance attack confirmed (confidence 0.90)
- Low quorum scenario succeeds → insufficient quorum protection (confidence 0.85)
- Both normal and flash loan succeed immediately → no timelock detected (confidence 0.85)
The generator produces a full PoC demonstrating the Beanstalk-style attack pattern.
Remediation
- Detector: Governance Attacks Detector
- Remediation Guide: Governance Attack Remediation
Multiple defenses are required in combination:
// 1. Vote snapshot at proposal creation time (prevents flash loan voting)
mapping(uint256 => mapping(address => uint256)) public voteSnapshotBalances;
function propose(...) external {
uint256 proposalId = ...;
// Capture token balances at this block, not at execution time
snapshotBlock[proposalId] = block.number;
voteSnapshotBalances[proposalId][msg.sender] =
token.getPastVotes(msg.sender, block.number);
}
// 2. Mandatory timelock (48+ hours between proposal and execution)
uint256 public constant MIN_TIMELOCK = 2 days;
// 3. Quorum requirement (10%+ of circulating supply)
uint256 public constant MIN_QUORUM_PERCENT = 10;
// 4. Proposal fee to prevent spam
uint256 public constant PROPOSAL_FEE = 100e18; // 100 tokens
OpenZeppelin Governor with GovernorVotes, GovernorTimelockControl, and GovernorSettings provides these protections out of the box.