# Beanstalk: flash-loan governance takeover drains $181M

Attacker flash-loaned stablecoins to buy governance voting power in Beanstalk, passed a malicious emergency BIP, and drained ~$181M from the protocol.

- Payout: Real-world hack
- Severity: critical
- Program: BeanstalkFarms
- Analysis by: DeFiHackLabs
- Disclosed: 2022-04-16
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-04/Beanstalk_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20220416-beanstalk
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:chained vuln:business-logic vuln:flash-loan vuln:governance

**Target:** on-chain DAO governance / Diamond contract with emergencyCommit
**Root cause:** Governance voting power was purchasable with flash-loaned liquidity and the emergencyCommit path executed proposals with no execution delay, so a single flash-loan-funded voter could pass and instantly run an arbitrary malicious proposal.
**Steps:**
1. Attacker pre-staged proposal BIP18 (0xe5ecf73603d98a0128f05ed30506ac7a663dbb69) with an _init contract that sweeps protocol funds, via `beanstalkgov.propose(_diamondCut, address(this), data, 3)`.
2. Wait out the ~1 day governance delay.
3. Flash-loan 350M DAI, 500M USDC, 150M USDT from Aave plus 32M BEAN from Uniswap v2 and 11.6M LUSD from SushiSwap.
4. `threeCrvPool.add_liquidity` the stablecoins to get ~979M 3Crv, then `BEAN3CRV-f.add_liquidity` to mint BEAN3CRV-f LP tokens.
5. Deposit the LP tokens into the Diamond (`siloV2Facet.deposit`) to acquire voting power.
6. Call `beanstalkgov.emergencyCommit(bip=18)` so the malicious proposal executes immediately, transferring BEAN, UNI-V2_WETH_BEAN, BEAN3CRV-f and BEANLUSD-f to the attacker.
7. `BEAN3CRV-f.remove_liquidity_one_coin` to unwind the LP position back to 3Crv.
8. Burn 3Crv via `threeCrvPool.remove_liquidity_imbalance` to recover DAI/USDC/USDT, then `threeCrvPool.remove_liquidity_one_coin` and repay Aave flash loans with premiums.
9. Burn UNI-V2_WETH_BEAN to get ~10,883 WETH and swap remaining stablecoins to WETH; net ~24,830 WETH (~$76M) sent to Tornado Cash.
**How found:** Attacker recognized that Beanstalk governance weight derived from Curve LP deposits and that emergencyCommit executed proposals without delay, so voting power could be rented via flash loans within a single transaction.
**Exploitation:** Pre-staged malicious BIP18 with an _init contract that sweeps protocol assets, waited out the 1-day delay, then flash-loaned ~$1B in stablecoins plus BEAN/LUSD to mint Curve LP tokens, deposited them into the Diamond for voting power, and called emergencyCommit to instantly execute the proposal. LP positions were unwound, flash loans repaid, and proceeds converted to ~24,830 WETH routed to Tornado Cash.
**Escalation:** Chained flash loans across Aave, Uniswap v2 and SushiSwap with Curve LP minting to manufacture enough voting power to pass and instantly execute a governance proposal, converting governance control into full protocol fund theft.
**Insight:** When governance weight is measured by on-chain LP or token balances, check whether a proposal can be both proposed and executed inside one transaction (emergencyCommit-style paths); any flash-loanable voting asset plus a no-delay execution path is a full-drain primitive.

## Proof of concept (copied exactly from the original report)

**Malicious proposal with sweep init calldata**

```
IBeanStalk.FacetCut[] memory _diamondCut = new IBeanStalk.FacetCut[](0);
        bytes memory data = abi.encodeWithSelector(ContractTest.sweep.selector);
        //emit log_named_uint("BIP:", bip);
        // function propose(
        //     IDiamondCut.FacetCut[] calldata _diamondCut,
        //     address _init,
        //     bytes calldata _calldata,
        //     uint8 _pauseOrUnpause
        // )
        // https://dashboard.tenderly.co/tx/mainnet/0x68cdec0ac76454c3b0f7af0b8a3895db00adf6daaf3b50a99716858c4fa54c6f
        beanstalkgov.propose(_diamondCut, address(this), data, 3);
```

