Regulatory capture: the $181mm $BEAN hack brings hacking to DAO governance.

The heist

On 16 April 2022 hackers used a flash loan to purchase governance tokens for Beanstalk Farms. They used these tokens to vote with a supermajority (> 66%) on a proposal that granted them access to all the protocol’s funds. Shortly afterwards they completely drained Beanstalk’s smart contracts and managed to escape with $75mm in wrapped ETH.

The heist was a first of a kind. It didn’t exploit a technical vulnerability in a smart contract but focused on a weakness in the governance construct. I have seen similar attacks with less scope a short while before and expect to see others following shortly. “Whatever can be thought once can be thought again”, as F. Duerenmatt said in his seminal novel “The Physicists”.

In this piece, I want to break down what happened step-by-step and identify key learnings for DAO governance. I will follow the trail of transactions, so this is a bit more technical. This piece was created as part of buildspace’s IRL hackathon in Amsterdam.

I’m working off the amazing tweets by Igor Igamberdiev, Xohn and PeckShield, so a huge shout out to them. Let’s dive in.

Getting technical

It continues to amaze me that even a completely public, transparent, and immutable ledger doesn’t keep criminal activity at bay. But it makes forensics easier and telling the story a lot more exciting because we have all the details available.

I’ll keep referring to the hackers in the plural, even though it’s completely possible that this was the work of a single person. First, I think this might just as well have been teamwork and second, it makes gender a non-issue.

The attackers used a flash loan to borrow $1bn in stablecoins (DAI, USDC and USDT) which were used to buy some $BEAN and $LUSD. If you want to see the transaction on the blockchain you can find it here on Etherscan.

Flahsloan transaction log
Flahsloan transaction log

Flash loans are unique to blockchains or to smart contracts to be exact. A flash loan is when a borrower takes out a loan, with no collateral and pays it back before closing the transaction. If the loan is not successfully repaid the transaction is reverted and no funds are lost. This minimizes the risk of defaults or illiquidity, making flash loans extremely cheap. Usually, no interest is charged, and a small fee (~0.09%) is paid instead.

Transactions do not have to finish within a single block (~13 secs on Ethereum), so more complex operations are possible, which is exactly what the hackers took advantage of.

Let’s get into the details of what happened.

Thanks to PeckShield for laying it all out <3
Thanks to PeckShield for laying it all out <3

The attackers created the Beanstalk Flashloan Exploiter smart contract. They took out flashloans of $350mm in DAI, $500mm in USDC and $150mm in USDT from Aave, as well as 32mm $BEAN.

They deposited the stablecoins into Curve’s 3Pool to get 980mm 3CRV, 15mm of which they exchanged for LUSD on SushiSwap.

965mm of 3CRV where converted to 795mm BEAN3CRV-f. The 32mm $BEAN where pooled with $15mm in LUSD to get BEANLUSD-f. Whenever a deposit is made to one of these silos (pools) on Beanstalk, the depositor is accredited with stalk and seed rewards and is allowed to participate in the governance system of the protocol. The system itself implements a lock mechanism to disallow the same amount of units from being voted with more than one time. The attackers deposited their LP tokens into Beanstalk and now had a supermajority of 70% of the voting rights, because of the sheer size of their holdings. Supermajority is needed to vote pass core protocol changes and trigger the emergencyCommit() function.

Now the good part

After this feat of number crunching came the good part, the artistic flourish: Beanstalk supports protocol upgrades via its Beanstalk-Improvement-Proposal (BIP) governance mechanism, making it possible for an upgrade to perform arbitrary code execution. The attackers used this to retrieve their locked funds as part of their malicious update.

The voting system of Beanstalk permitted votes to be cast retroactively on any active BIPs thus allowing newly generated votes to apply to historical BIPs. The attackers submitted two smart contracts as proposals BIP-18 and BIP-19 ahead of time to satisfy the emergency commit time threshold of 24 hours. BIP-18 proposed to donate $250k in USDC to the Ukrainian Crypto Donation. It also contained a call to an init() function on an address that had no code at that time. The second BIP (BIP-19) was the exploit with a function labelled “InitBip18”, to create a false trail.

The bad guys waited for the time threshold to pass, and no monitored if anyone noticed the malicious contracts they had deployed. As soon as the time had passed they took out the flashloans and acquired 70% of the voting power. Now they could trigger the emergencyCommit() function on the fake OIP-18 (really OIP-19) and minted 36mm $BEAN plus they drained all the wETH from the contract.

Next, they removed liquidity from Curve, repaid their loans on Aave, and sent the wETH to TornadoCash plus the 250k USDC to Ukraine Crypto Donation, before closing the transaction. Boom! In one fell swoop, Beanstalk was dead, the attackers made out with a huge 8-figure sum of ETH and a comparatively small donation.

250,000 USDC went to Ukraine Crypto Donation
250,000 USDC went to Ukraine Crypto Donation

What went wrong from a governance POV?

Beanstalk’s smart contracts were audited and no technical vulnerability was exploited. Instead, the attackers used a flaw in the governance logic of the protocol.

BEAN3CRV-f and BEANLUSD-f had just recently been added to the Beanstalk protocol with governance rewards. Uniswap checks if a transaction on an LP is already closed and doesn’t reflect the weight of holdings until they are fully committed. But Curve does not. This way the Curve LP tokens presented a flashloan vulnerability that Beanstalk was not responsible for, but could have checked.

Intimate knowledge of the way that Beanstalks checks asset prices, as well as how DEXs report holdings were required for this sophisticated attack. Gigabrains and gigaballs as crypto Twitter has written.

Gigabrain meme courtesy of NooNe0x.eth
Gigabrain meme courtesy of NooNe0x.eth

Basic flashloan protection is simple to implement in Solidity. There are exactly 0 (in words: zero, lol ) confirmations on an active flashloan.

Require(block.timestamp.sub(_lastActionTimestamp)> 0, ‘Flashloan’);

Is all that’s needed to prevent an unfinished transaction from participating in votes.

Flash loans allow attackers to get almost unlimited numbers of tokens, and when these can be converted into voting power, malicious proposals can be rammed through. Just ask Beanstalk.

Another big mistake was that retroactive voting was possible. This is not an easy fix, because voting power would have to be checked with snapshots at the time of each proposal, which is possible but requires larger amounts of processing and storage, both of which are very expensive on Ethereum. Off-chain solutions incur provider lock-in, and sometimes closed-source software usage.

Conclusion

Governance needs to figure out technical, game-theoretical, and political attacks before malicious actors do.

Since DAOs need to codify their governance mechanism these insights are reflected in the production code as well. Technical audits are not enough but should be performed on a regular basis.

I’m not aware of anyone offering game-theoretical and political audits for governance. That’s a massive opportunity for builders. LFG!!

The Beanstalk hack was not the first attempt to use flashloans to buy voting power but the one with the biggest loot.

I fully expect attackers to search for other protocols with this vulnerability and exploit them in short order. If you, dear reader work in governance the onus is firmly on you to make sure you don’t suffer Beanstalks fate.

If you’re interested in thinking through game-theoretical aspects of your DAO or protocol please reach out. Always happy to help.

BUIDL to save the world :)

Subscribe to Flipside Governance
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.