All incidents

BUILD Governance Takeover and Unlimited Mint ACT Exploit

Share
Feb 11, 2022 02:22 UTCAttackLoss: 4.6 ETHManually checked7 exploit txWindow: 1d 1h
Estimated Impact
4.6 ETH
Label
Attack
Exploit Tx
7
Addresses
2
Attack Window
1d 1h
Feb 11, 2022 02:22 UTC → Feb 12, 2022 03:32 UTC

Exploit Transactions

TX 1Ethereum
0x544e5849b71b98393f41d641683586d0b519c46a2eeac9bcb351917f40258a85
Feb 11, 2022 02:22 UTCExplorer
TX 2Ethereum
0xe6f47156c24c7628b93c5abc0d663dc337e976781778aa6f630aa327977b4e60
Feb 11, 2022 02:23 UTCExplorer
TX 3Ethereum
0xff2cf5ec92983daf075967b5652a8682419e882bf447d8da4d222118bfd94fba
Feb 11, 2022 02:24 UTCExplorer
TX 4Ethereum
0xc42f7af9ae94f6c5f480202c4bb268a24341d6cbe3aa0323eb4a99b0321c3fcd
Feb 11, 2022 02:26 UTCExplorer
TX 5Ethereum
0x648ea1548c20542d9545b329f9c1b5f5265a0ec71686caa944376213bdb0327b
Feb 11, 2022 02:24 UTCExplorer
TX 6Ethereum
0xc34c8a55776f9a02be163b5bcd52c21ac08df2c1da97f0f13e5618c764526689
Feb 12, 2022 03:28 UTCExplorer
TX 7Ethereum
0x3a9868b67f3e2ec90a5e9d5fce9a8176dfd8189b2cc37a2ed8cd8e5016c74955
Feb 12, 2022 03:32 UTCExplorer

Victim Addresses

0x5a6ebeb61a80b2a2a5e0b4d893d731358d888583Ethereum
0x6e36556B3ee5Aa28Def2a8EC3DAe30eC2B208739Ethereum

Loss Breakdown

4.6ETH

Similar Incidents

Root Cause Analysis

BUILD Governance Takeover and Unlimited Mint ACT Exploit

1. Incident Overview TL;DR

An unprivileged adversary cluster on Ethereum mainnet exploited BUILD Governance and the BUILD token to seize mint authority and extract ETH profit. Using only public governance functions on Governance contract 0x5a6e...8583, the cluster created and passed proposal 7 whose calldata called BUILD.setGovernance(attackerEOA). After proposal 7 reached the executable state, attacker EOA 0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28 executed it, causing BUILD token contract 0x6e36...8739 to set its governance variable to this EOA.

Once governance was captured, the attacker used the now-privileged EOA to call BUILD.mint multiple times, creating large amounts of BUILD from nothing, then routed portions of the minted BUILD through 1inch and related DeFi infrastructure into WETH and native ETH. Balance diffs across the mint, swap, and withdraw transactions show the attacker’s native ETH balance increased by exactly 4.599762619888583463 ETH after gas over the attack window. The core root cause is that Governance’s execute function can arbitrarily reassign BUILD.governance, and BUILD’s mint and setGovernance functions enforce only require(msg.sender == governance) with no restriction that governance remain a neutral contract or that minting be capped, enabling an ACT-style anyone-can-take governance-capture and unlimited-mint exploit.

2. Key Background

BUILD Governance (0x5a6e...8583) is a token-locking governance system. Users lock a voting token to gain voting power, must meet proposalThreshold to create proposals, and proposals must accumulate quorumVotes in favor to pass. Once a proposal reaches the appropriate state, Governance exposes an execute(uint256,address,uint256,bytes) function that can call an arbitrary target contract with arbitrary calldata. There is no on-chain allowlist or guardrail limiting which contracts or functions can be invoked via execute.

The BUILD token (0x6e36...8739) is an ERC20 with a single governance address that fully controls minting and further delegation of this power. The critical functions are:

contract BUILD is ERC20, ERC20Detailed {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint;
  
  address public governance;

  constructor () public ERC20Detailed("BUILD Finance", "BUILD", 18) {
      governance = msg.sender;
  }

  function mint(address account, uint amount) public {
      require(msg.sender == governance, "!governance");
      _mint(account, amount);
  }
  
  function setGovernance(address _governance) public {
      require(msg.sender == governance, "!governance");
      governance = _governance;
  }
}

In the intended design, Governance is the contract that should hold the governance role on BUILD, so that BUILD minting decisions go through on-chain proposals and voting. However, nothing in BUILD enforces that governance remain a contract, or that it not be set to an EOA capable of minting directly for its own benefit. Nothing in Governance enforces that execute cannot be used to call BUILD.setGovernance to assign governance to an arbitrary address.

The adversary uses standard DeFi components to realize profit: a DSProxy instance to interact with Balancer-style pools, and 1inch AggregationRouterV4 to swap minted BUILD into WETH and then unwrap WETH to ETH. All of these components are permissionless and accessible to any EOA.

3. Vulnerability Analysis & Root Cause Summary

The vulnerability is an overpowered governance execute combined with unguarded governance role on the token, which collapses the intended separation between a neutral governance contract and profit-seeking EOAs. On the BUILD token, mint and setGovernance are both gated solely by require(msg.sender == governance) with no additional constraints. On the Governance contract, execute allows any passed proposal to call an arbitrary target with arbitrary calldata once its state indicates readiness for execution.

This combination means an adversary that accumulates enough voting power to pass a single proposal can schedule a call to BUILD.setGovernance(attackerEOA), then execute it to move BUILD.governance from Governance to their own EOA. Once that invariant is broken, the EOA can call BUILD.mint in a permissionless way, creating arbitrary BUILD supply and routing it through DEXes for ETH. The protocol never enforces that governance remain a contract, never restricts the set of callable functions via execute, and never caps minting. The root cause is thus a governance-takeover enabling unlimited minting, fully realizable under an ACT adversary model because all steps rely only on public governance rules, public contract code, and permissionless DeFi interactions.

4. Detailed Root Cause Analysis

Invariant and breakpoint

The critical invariant is:

For all times t, increases in BUILD.totalSupply must be authorized exclusively by a neutral governance contract that cannot directly mint BUILD for its own unilateral profit; equivalently, the BUILD.governance storage slot must never be set to an EOA that can arbitrage mint() for personal gain.

The concrete breakpoint operation is:

In transaction 0x544e5849b71b98393f41d641683586d0b519c46a2eeac9bcb351917f40258a85 at block 14182038, attacker EOA 0xdcc8... calls Governance.execute(7, BUILD, 0, data), where data encodes BUILD.setGovernance(0xdcc8...). The call path Governance.execute -> BUILD.setGovernance updates the BUILD.governance storage slot from the Governance contract address to 0xdcc8..., violating the invariant.

The seed transaction trace for this tx confirms that Governance is the entrypoint and that a call into BUILD is executed with calldata corresponding to setGovernance:

Seed transaction trace (cast run -vvvvv) for tx 0x544e...:
SM Address: 0x5a6ebeb61a80b2a2a5e0b4d893d731358d888583, caller:0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28
...
; Governance.execute(7, BUILD, 0, data) decodes and initiates an external call to BUILD
...
; downstream call targets BUILD 0x6e36...8739 with function selector matching setGovernance(address)

From this point onward, require(msg.sender == governance) on BUILD accepts 0xdcc8..., granting the attacker unrestricted access to mint and the ability to reassign governance again if desired.

Victim contracts and behavior

On the Governance contract (0x5a6e...8583):

  • votingPeriod, executionPeriod, quorumVotes, and proposalThreshold are static numerical parameters controlling the lifecycle of proposals.
  • Proposals specify a target, value, and data. Once a proposal passes and reaches a ready-to-execute state, anyone can call execute(id, target, value, data) to perform the call.
  • There is no filtering on target or data; any contract and function can be invoked, including those that assign or escalate privileges.

On the BUILD token (0x6e36...8739):

  • governance is initialized to the deployment caller (in practice, the Governance contract).
  • mint(address,uint256) mints arbitrary amounts to any account so long as msg.sender == governance.
  • setGovernance(address) reassigns governance to any _governance if called by the current governance.

The protocol assumes that Governance will always be the owner of governance on BUILD, but this is not enforced. There is no require that governance be a contract, nor any safeguard against proposals that reassign governance to an EOA.

Evidence of governance takeover

Governance txlists and traces around proposal 7 show the following sequence:

  • EOA 0xd6dbed... approves BUILD to Governance and submits proposal 7 targeting BUILD with calldata encoding setGovernance(0xdcc8...).
  • The same EOA and the attacker EOA vote on the proposal such that forVotes exceed againstVotes and meet quorumVotes, moving the proposal into the state where it can be executed.
  • In tx 0x544e... at block 14182038, EOA 0xdcc8... calls Governance.execute(7, BUILD, 0, data). The seed trace confirms the external call from Governance into BUILD with setGovernance(0xdcc8...), and storage diffs around the BUILD governance slot show a change from the Governance address to 0xdcc8....

Evidence of unlimited minting

Once BUILD.governance equals 0xdcc8..., the attacker submits three mint transactions:

  • 0xe6f47156c24c7628b93c5abc0d663dc337e976781778aa6f630aa327977b4e60 (block 14182042, mechanism mint)
  • 0xff2cf5ec92983daf075967b5652a8682419e882bf447d8da4d222118bfd94fba (block 14182048, mechanism mint)
  • 0xc42f7af9ae94f6c5f480202c4bb268a24341d6cbe3aa0323eb4a99b0321c3fcd (block 14182054, mechanism mint)

Balance diffs for these txs show the BUILD token being created directly to the attacker:

// Example: tx 0xe6f4... (first mint)
{
  "erc20_balance_deltas": [
    {
      "token": "0x6e36556b3ee5aa28def2a8ec3dae30ec2b208739",
      "holder": "0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28",
      "before": "0",
      "after": "100000000000000000000000",
      "delta": "100000000000000000000000",
      "contract_name": "BUILD"
    }
  ]
}

The other two mint transactions similarly show large positive BUILD deltas to 0xdcc8... (7,600 BUILD and 1,000,000 BUILD-equivalent amounts), with only small negative ETH deltas corresponding to gas costs. There is no incoming token spend from the attacker; BUILD appears from the zero address via mint.

Profit realization via swaps and withdraws

After minting, the attacker converts BUILD into WETH/ETH via DeFi infrastructure:

  • 0x648ea1548c20542d9545b329f9c1b5f5265a0ec71686caa944376213bdb0327b (block 14182050, mechanism swap via 1inch)
  • 0xc34c8a55776f9a02be163b5bcd52c21ac08df2c1da97f0f13e5618c764526689 (block 14188829, mechanism swap via 1inch)
  • 0x3a9868b67f3e2ec90a5e9d5fce9a8176dfd8189b2cc37a2ed8cd8e5016c74955 (block 14188842, mechanism withdraw via WETH)

The balance diffs for these txs show ETH inflows and BUILD outflows from the attacker:

// Example: tx 0x648e... (1inch swap)
{
  "native_balance_deltas": [
    {
      "address": "0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28",
      "before_wei": "353676727661241689",
      "after_wei": "4831548861904691557",
      "delta_wei": "4477872134243449868"
    }
  ],
  "erc20_balance_deltas": [
    {
      "token": "0x6e36556b3ee5aa28def2a8ec3dae30ec2b208739",
      "holder": "0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28",
      "before": "7600000000000000000000",
      "after": "0",
      "delta": "-7600000000000000000000",
      "contract_name": "BUILD"
    }
  ]
}
// Example: tx 0x3a98... (WETH.withdraw)
{
  "native_balance_deltas": [
    {
      "address": "0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28",
      "before_wei": "745679340714433351",
      "after_wei": "897716860263569111",
      "delta_wei": "152037519549135760"
    }
  ]
}

Summing positive ETH deltas to 0xdcc8... across the swap/withdraw leg and subtracting the gas costs measured in the relevant mint/swap/withdraw txs yields a net ETH profit of 4.599762619888583463 ETH over the attack window. This net delta is encoded directly in the success predicate of root_cause.json and does not rely on estimated prices or off-chain data.

The exploit therefore consists of:

  1. Using public governance to reassign BUILD’s governance to an EOA.
  2. Using the now-privileged EOA to mint BUILD at zero cost.
  3. Dumping minted BUILD into liquidity pools via 1inch and DSProxy to extract WETH/ETH.

All steps are permissionless and reproducible by any adversary that controls sufficient BUILD voting power to pass the governance proposal.

5. Adversary Flow Analysis

Adversary-related cluster

The adversary-related cluster accounts are:

  • EOA 0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28 (Ethereum mainnet)
    • Executes Governance.execute(7, ...) in tx 0x544e....
    • Receives all minted BUILD in mint txs 0xe6f4..., 0xff2c..., and 0xc42f....
    • Initiates 1inch and WETH interactions that convert BUILD/WETH into native ETH.
    • Accumulates net profit of 4.599762619888583463 ETH in native balance after gas.
  • EOA 0xd6dbed6297b539a11f1aab7907e7df7d9ffeda7e (Ethereum mainnet)
    • Approves BUILD to Governance and creates proposal 7 with calldata BUILD.setGovernance(0xdcc8...).
    • Votes in favor of proposal 7 and exchanges ETH with 0xdcc8..., supporting classification as a cooperating governance/trading address.
  • DSProxy 0x40753fee22b6a7bf197ac25c3b31dc1e13e3d694 (Ethereum mainnet)
    • A DSProxy instance built via DSProxyFactory from 0xdcc8....
    • Only its owner or authorized authority can call execute(address,bytes), so its interactions with Balancer and DEX pools are under attacker control.

Lifecycle stages

  1. Governance priming and proposal creation

    • Tx 0xf8990741e0195b3616b0b1f422f59b405c2b655109f6ed05fa90e9b4738c48d1 (block 14169189, mechanism approve): the adversary cluster approves BUILD to Governance, enabling token lock and voting.
    • Tx 0xfa9cca40f225745c2eac97521d979ddfa4edae589ff0f1b543b94345e52335e9 (block 14169198, mechanism governance_propose): proposal 7 is created with target BUILD and calldata encoding setGovernance(0xdcc8...).
    • Txs 0x9b116ef389e12c6ccb99fedd9f6dd881ea6a893d8d4a890928be45839932e024 and 0xb6edc1f0b5b2558aae3d886c1d52c15e0126021c91f052e0378ee707f6e85be8 (blocks 14169204 and 14182045, mechanisms governance_vote): adversary EOAs vote so that forVotes exceed againstVotes and meet quorumVotes, moving proposal 7 to a ready-to-execute state.
  2. Governance execution and mint authority capture

    • Tx 0x544e5849b71b98393f41d641683586d0b519c46a2eeac9bcb351917f40258a85 (block 14182038, mechanism governance_execute):
      • EOA 0xdcc8... calls Governance.execute(7, BUILD, 0, data).
      • Governance calls BUILD.setGovernance(0xdcc8...).
      • The BUILD.governance storage slot is updated from 0x5a6e...8583 (Governance) to 0xdcc8....
      • From this point, BUILD’s mint and setGovernance functions accept 0xdcc8... as msg.sender.
  3. Unlimited minting and ETH extraction

    • Tx 0xe6f4... (block 14182042, mechanism mint): BUILD.mint mints 100,000 BUILD-equivalent units directly to 0xdcc8..., as shown by a +100,000 BUILD delta in the balance diff, with only gas cost in ETH.
    • Tx 0xff2c... (block 14182048, mechanism mint): BUILD.mint mints 7,600 BUILD directly to 0xdcc8....
    • Tx 0xc42f... (block 14182054, mechanism mint): BUILD.mint mints approximately 1,000,000 BUILD-equivalent units to 0xdcc8....
    • Tx 0x648e... (block 14182050, mechanism swap): the attacker uses 1inch to swap 7,600 BUILD for ETH, with the balance diff showing a large positive ETH delta at 0xdcc8... and a full depletion of the 7,600 BUILD.
    • Tx 0xc34c... (block 14188829, mechanism swap): the attacker uses 1inch unoswap to route a ~1e24 BUILD chunk through liquidity pools; balance diffs show large negative BUILD deltas from 0xdcc8... into pools and relatively small ETH gas costs.
    • Tx 0x3a98... (block 14188842, mechanism withdraw): the attacker calls WETH.withdraw, converting WETH into 0.152037519549135760 ETH at 0xdcc8..., as recorded in the native balance diff.

Over these mint, swap, and withdraw transactions, the summed ETH inflows to 0xdcc8... minus gas outflows equal a net profit of 4.599762619888583463 ETH. No privileged roles, whitelists, or private channels are used; the only prerequisite is sufficient BUILD voting power to pass proposal 7 under the public Governance rules.

6. Impact & Losses

The directly evidenced financial impact on-chain is:

  • ETH: at least 4.599762619888583463 ETH of net profit realized by attacker EOA 0xdcc8a38a3a1f4ef4d0b4984dcbb31627d0952c28 over the core attack-leg transactions, after accounting for gas costs.

Beyond the ETH-denominated profit, the exploit causes unbounded inflation of BUILD supply under attacker control:

  • BUILD’s total supply increases by more than 1,107,600 BUILD units directly minted to 0xdcc8... across the documented mint transactions.
  • Large amounts of BUILD are routed into DeFi liquidity pools (e.g., via DSProxy and 1inch), redistributing the new supply across LPs and other counterparties.

These effects degrade the integrity of BUILD Governance and the BUILD token:

  • Governance’s assumption that mint authority is constrained by community decisions is broken once an EOA becomes governance.
  • Liquidity providers and traders in BUILD pools are exposed to sudden inflation and dump pressure from attacker-minted supply.
  • The exploit demonstrates that, under the current design, any adversary with sufficient BUILD voting power can reproduce the same governance-takeover and unlimited-mint cycle.

7. References

  • Contracts

    • Governance contract (0x5a6ebeb61a80b2a2a5e0b4d893d731358d888583) – source code as collected in artifacts/root_cause/data_collector/iter_1/contract/1/0x5a6e...8583/source/src/Contract.sol. Used to confirm proposal lifecycle, voting thresholds, and the behavior of execute(uint256,address,uint256,bytes).
    • BUILD token contract (0x6e36556B3ee5Aa28Def2a8EC3DAe30eC2B208739) – source code as collected in artifacts/root_cause/data_collector/iter_1/contract/1/0x6e36...8739/source/src/Contract.sol. Used to confirm governance storage, and the implementations of mint and setGovernance.
  • Key transactions

    • 0xf8990741e0195b3616b0b1f422f59b405c2b655109f6ed05fa90e9b4738c48d1 – BUILD approval to Governance (governance priming).
    • 0xfa9cca40f225745c2eac97521d979ddfa4edae589ff0f1b543b94345e52335e9 – proposal 7 creation (BUILD.setGovernance(0xdcc8...)).
    • 0x9b116ef389e12c6ccb99fedd9f6dd881ea6a893d8d4a890928be45839932e024 and 0xb6edc1f0b5b2558aae3d886c1d52c15e0126021c91f052e0378ee707f6e85be8 – governance votes for proposal 7.
    • 0x544e5849b71b98393f41d641683586d0b519c46a2eeac9bcb351917f40258a85 – Governance execution of proposal 7, calling BUILD.setGovernance(0xdcc8...).
    • 0xe6f47156c24c7628b93c5abc0d663dc337e976781778aa6f630aa327977b4e60 – first BUILD.mint to attacker (100,000 BUILD-equivalent).
    • 0xff2cf5ec92983daf075967b5652a8682419e882bf447d8da4d222118bfd94fba – second BUILD.mint to attacker (7,600 BUILD).
    • 0xc42f7af9ae94f6c5f480202c4bb268a24341d6cbe3aa0323eb4a99b0321c3fcd – third BUILD.mint to attacker (~1,000,000 BUILD-equivalent).
    • 0x648ea1548c20542d9545b329f9c1b5f5265a0ec71686caa944376213bdb0327b – 1inch swap converting 7,600 BUILD into ETH.
    • 0xc34c8a55776f9a02be163b5bcd52c21ac08df2c1da97f0f13e5618c764526689 – 1inch unoswap routing a ~1e24 BUILD chunk through liquidity pools.
    • 0x3a9868b67f3e2ec90a5e9d5fce9a8176dfd8189b2cc37a2ed8cd8e5016c74955 – WETH.withdraw converting WETH into 0.152037519549135760 ETH at the attacker EOA.
  • Traces and balance diffs

    • Seed transaction trace for tx 0x544e...artifacts/root_cause/seed/1/0x544e5849b71b98393f41d641683586d0b519c46a2eeac9bcb351917f40258a85/trace.cast.log. Confirms the call path Governance.execute -> BUILD.setGovernance and the update of the governance slot.
    • Balance diffs for BUILD.mint txs – artifacts/root_cause/data_collector/iter_2/tx/1/0xe6f4.../balance_diff.json, 0xff2c.../balance_diff.json, 0xc42f.../balance_diff.json. Used to verify BUILD supply creation and ETH gas costs.
    • Balance diffs for swap and withdraw txs – artifacts/root_cause/data_collector/iter_3/tx/1/0x648e.../balance_diff.json, 0xc34c.../balance_diff.json, 0x3a98.../balance_diff.json. Used to compute the net +4.599762619888583463 ETH change in the attacker’s native balance.
  • Txlists and address-level context

    • Governance-related txlists for 0xd6dbed... and 0xdcc8... – collected under the artifacts/root_cause/data_collector/iter_3/address/... directory, providing context for governance participation, DSProxy deployment, and value transfers between the adversary addresses.