Prevent Flash Loan Attacks

Abstract

A comprehensive list of historic defi attacks, the attack vectors, and how to prevent them.

This site is dedicated to information about flash loan attacks (which are usually just price oracle attacks) and how to stop them. For a study done by the Imperial College London, check out this deep dive on the historic bZx flash loan (oracle manipulation) attack for more information. The first step you can take, is to use decentralized oracles when working with data, a number of the projects that had issues moved themselves to Chainlink Price Feeds and are now secure. This is an open source project.

If you’d like to help fund Gitcoin grants for research, please consider donating.

Historic Attacks

ProtocolReferenceDateValue (USD)TransactionCauseFix
bZx (1)QuantStampFeb-15-2020 01:38:57 AM +UTC$350kEtherscanPump & ArbitrageUnclear
bZx (2)CoinTelegraphFeb-18-2020 03:13:58 AM +UTC$600kEtherscanOracle AttackChainlink Integration
Origin ProtocolCoinDeskNov-17-2020 12:47:19 AM +UTC$7MEtherscanRe-entrancy AttackUnclear
Harvest.FinanceCoinDeskOct-26-2020 02:58:16 AM +UTC$24MEtherscanOracle AttackUnclear
Value DefiCoinDeskNov-14-2020 03:36:30 PM +UTC$6MEtherscanOracle AttackChainlink Integration
AkropolisCoinDeskNov-12-2020 12:04:41 PM +UTC$2MEtherscanRe-EntrancyRe-Entry Check added
Cheese BankCoinTeleGraphNov-14-2020 03:36:30 PM +UTC$6MEtherscanOracle AttackUnclear
CompoundDecryptNov-26-2020 08:55:16 AM +UTC$89MEtherscan (one of many)Oracle AttackUnclear
MakerDAOPost MortemNov-25-2020 10:46:00 PM +UTC----Oracle AttackUnclear
Warp FinanceSummaryDec-17-2020 11:24:41 PM +UTC$7.76Methtx.infoOracle Attack--

Project Post Mortems

What is A Flash Loan?

A flash loan is a loan that is only valid within one blockchain transaction. Flash loans fail, if the borrower does not repay its debt before the end of the transaction borrowing the loan. That is, because a blockchain transaction can be reverted during its execution, if the condition of a repayment is not satisfied.

We can then see some easy attack vectors using this tool.

  1. Take out a massive loan (token A) from a protocol supporting flash loans
  2. Swap token A for token B on a DEX, dumping the price of A
  3. Deposit the purchased token B as collateral on a DeFi protocol that uses the above DEX as its sole price feed, and borrow even more with this manipulated price
  4. Use a portion of borrowed token A to fully pay back the original flash loan and keep the remaining tokens, (profiting here)

Types of attacks

Oracle Manipulation

This seems to be the #1 cause of attacks at the moment, by far. What is important to note, is that decentralized exchanges are not decentralized oracles. Using Uniswap, Sushiswap, or Curve to get pricing information to execute trades is pulling data from potocols whose price depends soley on liquidity. Looking at the infamous ground zero bZx attack that sparked this wave of attacks, we can see exactly what happens. These flash loans are used to crash and manipulate the price of these decentralized exchanges, which most projects deemed safe to use. The issue here relies in the fact that these protocols prices depend entirely on liquidity.

See the above section for what something like this would look like.

Prevention

The easiest way to solve this is to use decentralized oracles. Chainlink Price Feeds are the leading decentralized oracle provider, and you can see that the vast majority of the protocols end up adding Chainlink to fix these attacks. If the data (price or otherwise) you're looking for isn't there yet, you can always request new decentralized networks or build your own.

Prevention Example

Let's take a look at some malicious pseudo-code, pretend these are each ERC20s.

1uint256 priceOfMyGovernanceTokenInETH = dexTokenETHPairPrice;
2myGovernanceToken.transfer(msg.sender, priceOfMyGovernanceTokenInETH)

This right here should be the easiest red flag on the planet. If you ever do a transfer based on a centralized price oracle, you're asking to get owned. One way or another.

Check the Chainlink documentation for decentralized price feeds

1import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
2// constructor and getLatestPrice function truncated
3uint256 priceOfMyGovernanceTokenInETH = getLatestChainlinkPrice();
4myGovernanceToken.transfer(msg.sender, priceOfMyGovernanceTokenInETH)

Reentrancy

A reentrancy attack can occur when you create a function that makes an external call to another untrusted contract before it resolves any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the effects were resolved.

Prevention

Leave external transactions to the last parameter. These are the harder ones to prevent, but below is a simple example of what should be done.

Whenever possible, use the built-in transfer() function. It only sends 2300 gas with the external call, making reentrancy almost impossible. Since that will give you just enough gas to write to a log.

You could alternativly add a mutex, or a variable that puts a lock on calling the function or working with the variables until the work with them is done. You don't need to do all of these tips, but you do need to do at least one of these tips.

Prevention Example

This code:

1function withdraw() external {
2 uint256 amount = balances[msg.sender];
3 require(token.transfer(msg.sender, amount)());
4 balances[msg.sender] = 0;
5}

Should be changed so that the external token transfer call happens after the balance is updated to 0.

1function withdraw() external {
2 uint256 amount = balances[msg.sender];
3 balances[msg.sender] = 0;
4 require(token.transfer(msg.sender, amount)());
5}

Additionally, you could do something like:

1bool public mutex = false;
2
3function withdraw() external {
4 require(!mutex);
5 mutex = true;
6 uint256 amount = balances[msg.sender];
7 balances[msg.sender] = 0;
8 require(token.transfer(msg.sender, amount)());
9 mutex = false;
10}

The DAO attack is an example of the reentrancy attack as well, and is also considered the mother of not just defi, but decentralized attacks in general on the ETH chain.

Race Conditions / Front Running

Since everything on-chain is public information, an attacker can watch transactions on-chain and look for those that would be detrimentalto the atacker, and make a transaciton with a higher gas price to occur before that transaction goes through. For example, they notice a whale is about to dump a token that the attacker holds, so the attacker pays extra gas to dump theirs first. This is known as "front running" in traditional finance, you could also think of it as a race condition because there can be scenarios where it's more complicated than this exmaple, but still boiled down to this. Reentrancy technical falls under this category.

Prevention

The best way to prevent against these is with a commit-reveal scheme. This is when a project sends a transaction that goes through and is accepted, but is hashed or encrypted. Only after the transaction has concluded that they send a "reveal" phrase that decodes the transaction. This method prevents both miners and users from frontrunning transactions as they cannot determine the contents of the transaction. Transactional value however, cannot be commit-revealed, making this far less effective in the defi world. This is another very difficult type of attack to prevent.

Bancor had run into this issue but fixed it before it was exploited.

#TODO looking for projects that use commit-reveal.

Pump and Arbitrage

Pump and arbitrage attacks are difficult to find, some even saying they are less "attacks" and more "the system working as intended". Liquidity is an important part of any and all processes, so when a whale spikes or crashes a price, does that really reflect the true value of that crash/spike? It's hard to say.

Prevention

Prevention at the moment hangs around preventing anyone from being able to cause these spikes. Sometimes, coordinated attacks from social groups can be enough to pump and dump a price of an asset.

Additional Important Notes

It's important to note that their are a LOT more vulnerabilities than what we are covering here. These are just the major issues we've seen in defi. This blog does a great job of outlining many of these attacks, and showing how to prevent them. Various Known Attacks

Anther great resource that goes over additional attacks. Additional security considerations.

It's unclear if auditors should be catching these, or if developers and orgainizations are taking shortcuts, or if people are just "apeing" into projects before thinking. We are all leanring

More details, notes threads

This will be a work in progress for us to get better at standards here. At the moment, I'm not sure how to address some of the liquidity issues. If you have a protocol that depends heavily on liquidity of another platform, you could very well be vulnerable.

Akropolis Hack Tweet Thread

bZx First attack

bZx Both attacks

Reentry attacks

Using call over transfer or send

Security Best Practices

Various Known Attacks

Avoid Known Pitfalls

Bypassing Timelocks

Process Quality Reviews

Taking undercollateralized loans for fun and profit