As we work on flash loan attack debriefs we can gather a lot of information about what could have been taken as preventative measures. It’s important to understand how these flash loans work before diving into root causes and analytics.
Flash loans are loans given out without collateral and returned in the same block that they were lent out. This is possible in the world of smart contracts, because the code can verify whether or not it will be able to return the loan at the end of the block. If yes, it’s able to lend as much as the user needs. This is where the attack vectors come in.
We can draw from a study done by academics at the Imperial College London that looks at two notorious attacks in particular. Their main finding is as follows:
Flash loan attacks are typically Price Oracle 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. Here’s basically how it works:
- User takes a massive loan out
- User uses that loan, to buy out all the liquidity on one side of a liquidity pool, causing a massive drop in price
- This price is being used by another protocol to determine or execute some code, ie, they peg the price of their asset to the protocols asset. So the user swaps for next to nothing for a massive profit.
- Then pays back the original loan, and leaves with massive gains
What auditors and software engineers need to do, is make sure they don’t get pricing or data that rely on DEXes. Uniswap is a decentralized exchange NOT a decentralized pricing oracle, they are each a centralized data point, and using them as such is dangerous to a point where in the last 2 months, ~5 protocols have been hacked for over $30M combined.
Prevention
This all being said, to prevent these attacks from happening, you need to make sure that when you’re getting pricing information, or any data at all, it needs to come from decentralized oracles and get the data from decentralized Chainlink Price Feeds, if it’s price data. For any other data you need to get your data from a decentralized network of Chainlink Oracles. And as an engineer, anyone can customize their oracle network to make it as wide or narrow as they like.
This all being said, at this point, there is enough information out there that if a protocol gets hacked and that protocol paid an auditor, that auditor needs to be held accountable as well, as missing centralized price oracles in audit reviews is going to make this keep happening. Many projects who have been hacked have integrated Chainlink price feeds as there backbone for data reliability and have since stabilized.
The second most popular attack is reentrancy attacks
To quote:
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.
Luckily, we know that fallback functions have been patched to use only 2300 gas, so this can be helpful here. Consensys recommends using call
instead of transfer
or send
to protect yourself against these.
However, I’ve also seen the exact opposite suggested. I think using transfer
is actually better. Just as long as you set the balance to 0 before you make an external call. Always try to make the external contract call after all the other important pieces are in-tact.
Other notes
It should be mentioned that there are some “hacks” that will also fix this issue. Making all your important transactions happen over the course of 2 or more blocks can be a hacky way to get around some of this. Also you will always want to use safeMath
when working with massive integers, and need to make sure your overflows don’t break your protocol.
What other security pieces should we look at?
I want to make a preventdefiattacks.com
instead to make it more general, since we’ve seen a lot of attacks not using flash loans.