Back to Blog

Governance Attack Vectors: How DAOs Get Exploited

|Odin Scan Team
Governance Attack Vectors: How DAOs Get Exploited

Governance proposals have the same authority as the deployer. They can change any parameter, upgrade any contract, move any fund. In most protocols, a passed proposal can do literally anything the protocol's admin can do.

They are also, consistently, the least-reviewed code in any protocol. Core contracts get audited. Test suites cover the main code paths. Governance proposals get a forum post and a snapshot vote. The actual execution script, the Solidity that runs on-chain, often gets less review than a junior developer's first PR.

This is where attackers have started focusing.


The Attack Surface

A governance system has multiple attack vectors, each targeting a different component:

Voting power manipulation: Acquiring enough votes to pass a malicious proposal, either by buying tokens, borrowing them via flash loans, or exploiting delegation mechanics.

Proposal content manipulation: Submitting a proposal that looks benign but contains malicious execution logic.

Execution timing attacks: Exploiting the gap between proposal approval and execution to set up profitable conditions.

Configuration attacks: Proposals that change parameters to values that create exploitable conditions, without any obviously malicious code.


Attack 1: Flash Loan Governance

How it works: The attacker flash-borrows governance tokens, acquires voting power, votes on a malicious proposal, and repays the loan, all in one transaction.

// Vulnerable: voting power based on current balance
function castVote(uint256 proposalId, bool support) external {
    uint256 votes = token.balanceOf(msg.sender); // flashloanable
    _recordVote(msg.sender, proposalId, support, votes);
}

Defense: Use historical balance snapshots. OpenZeppelin's Governor uses token.getPastVotes(account, proposalSnapshot), which reads the balance at the block when the proposal was created. Flash loans within the voting period have no effect.

// Safe: uses balance snapshot from proposal creation block
function castVote(uint256 proposalId, bool support) external {
    uint256 snapshotBlock = proposalSnapshot(proposalId);
    uint256 votes = token.getPastVotes(msg.sender, snapshotBlock);
    _recordVote(msg.sender, proposalId, support, votes);
}

Attack 2: Low-Quorum Takeover

How it works: The protocol requires only a small percentage of total tokens to vote for a proposal to pass. The attacker accumulates just enough tokens (often a fraction of total supply) and passes a proposal during a period of low voter participation.

Real-world examples are common. Many DeFi governance systems have quorum thresholds of 1-4% of total supply. If token holder participation drops below that threshold, an attacker with a relatively small position can pass any proposal.

Defense:

  • Set quorum as a percentage of circulating supply, not total supply (excludes locked/vesting tokens)
  • Implement a minimum voting period (7 days minimum for significant changes)
  • Use a timelock between approval and execution (48-72 hours minimum)
  • Require a minimum number of unique voters, not just a token threshold

Attack 3: Malicious Proposal Payloads

How it works: A proposal is submitted with a description that says one thing and calldata that does something else. Voters read the forum post and the description. They do not decode and verify the calldata.

// Proposal description: "Update fee from 0.3% to 0.25%"
// Actual calldata: transfers ownership to attacker address
bytes memory payload = abi.encodeWithSignature(
    "transferOwnership(address)",
    attackerAddress
);

This is surprisingly effective. Most governance UIs display the description prominently and the raw calldata in a collapsed section. Voters trust the description.

Defense:

  • Governance UIs should decode and display calldata in human-readable form
  • Require proposal simulations before execution (Tenderly, Foundry fork tests)
  • Implement a proposal review period where technical reviewers verify calldata matches description
  • Automated scanning of proposal scripts before on-chain submission

Attack 4: Oracle Configuration via Governance

The Moonwell hack was this pattern exactly. A governance proposal changed an oracle configuration. The oracle address was wrong. The proposal passed, executed, and $1.78 million was drained within minutes.

This is not technically a governance attack. It is a configuration error introduced through governance. The distinction does not matter to the users who lost money.

The pattern:

  1. Governance proposal adds a new collateral type
  2. Proposal specifies an oracle address
  3. Oracle address returns values in the wrong denomination (ETH instead of USD)
  4. Proposal executes, mispriced collateral is immediately exploitable

Defense:

  • Post-execution validation: the proposal script itself should assert that all oracle prices are within expected ranges after configuration
  • Fork testing: simulate the proposal against forked mainnet and verify all prices
// In the governance proposal execution script
function _afterExecution() internal {
    // Verify every oracle returns a sane price
    uint256 cbEthPrice = oracle.getUnderlyingPrice(cCBETH);
    require(cbEthPrice > 1000e18, "cbETH price sanity check failed");
    // cbETH should be > $1000, never $1.12
}

Attack 5: Timelock Bypass

How it works: The protocol has a timelock for governance execution, but there is a path that bypasses it. Emergency functions, guardian roles, or upgrade mechanisms that skip the timelock.

Common bypass patterns:

  • emergencyPause() that also allows parameter changes
  • Guardian role that can execute proposals without waiting
  • upgradeToAndCall() that changes implementation and calls an initializer atomically

Defense:

  • Emergency functions should only pause, never change parameters
  • Guardian roles should have explicit, narrow permissions (pause only, not execute)
  • Audit every path that leads to state changes and verify each goes through the timelock

Attack 6: Delegation Manipulation

How it works: The attacker acquires voting power not by buying tokens but by manipulating delegation. They convince or trick token holders into delegating voting power, or exploit delegation mechanics that allow re-delegation without the original delegator's consent.

The pattern:

  1. Create a legitimate-seeming delegate platform ("Vote with us for higher yields")
  2. Accumulate delegated voting power from many small holders
  3. Use that power to pass a malicious proposal
  4. Token holders cannot un-delegate fast enough to prevent execution

Defense:

  • Delegation should require active consent and be easily revocable
  • Proposals should have a minimum voting period that exceeds the time needed for delegators to react
  • Timelocks give delegators time to un-delegate between approval and execution

The Governance Security Checklist

Before deploying a governance system:

  • Voting power uses historical snapshots, not spot balances (prevents flash loan attacks)
  • Quorum threshold is meaningful relative to actual voter participation
  • Minimum voting period is at least 3 days for parameter changes, 7 days for upgrades
  • Timelock of at least 48 hours between approval and execution
  • Proposal calldata is decoded and displayed in the governance UI
  • Every proposal is simulated against forked mainnet before execution
  • Emergency functions can only pause, not change parameters
  • No bypass paths exist around the timelock
  • Post-execution validation checks are included in proposal scripts

Odin Scan scans governance proposal scripts and deployment configs, not just core contracts. It catches oracle misconfigurations, parameter changes that create exploitable conditions, and calldata that does not match the stated intent. Start your free trial and add a security gate before your next governance proposal.