Counting the tactics of DeFi oracle attacks, these six security measures you need to know
Translator's Note: Price oracles are a crucial part of decentralized finance (DeFi), but it is precisely the vulnerabilities that may exist in this segment that have led many DeFi projects to suffer catastrophic losses, some amounting to tens of millions of dollars. So what exactly are these attacks? How can we prevent them? In this article, renowned white hat hacker and Paradigm research partner Samczsun analyzes five real cases of price oracle attacks and summarizes six methods to defend against such attacks.
At the end of 2019, I published a post titled "Taking Undercollateralized Loans for Fun and for Profit," in which I described an economic attack on an Ethereum dApp that relied on accurate price data for one or more tokens. Now it is the end of 2020, and unfortunately, many projects have made similar mistakes, with a recent example being Harvest Finance, which suffered a hack that resulted in a collective loss of $33 million for protocol users.
While developers have become familiar with vulnerabilities like reentrancy, oracle manipulation is clearly not a frequently considered issue. In fact, attacks based on reentrancy vulnerabilities have decreased over the past few years, while the exploitation rate of price oracle manipulation is now on the rise. Therefore, I believe it is time to publish some definitive information about price oracle manipulation.
This article is divided into three parts. In the first part, we will provide a simple introduction to oracles and oracle manipulation for those unfamiliar with the topic. The second part consists of some case studies (where we will review past vulnerabilities and attack events related to oracles), which those who want to test their knowledge can skip. Finally, we summarize some techniques that developers can use to protect their projects from oracle manipulation.
Real-World Oracle Manipulation
Let's go back to December 1, 2015 (Wednesday). Imagine you are David Spargo, and you are at a Peking Duk concert in Melbourne, Australia. You want to go to the backstage lounge to meet the band members in person, but two security guards are blocking your way, and they won't let an ordinary person in.
You wonder how the guards would react if you acted like a relative of the Peking Duk band. Since family members are definitely allowed backstage, all you need to do is convince the guards that you are a relative of the band. After thinking for a moment, you come up with a plan that can only be described as "genius" or "absolutely crazy."
After quickly arranging everything, you confidently walk up to the guards. You introduce yourself as David Spargo, a family member of Peking Duk, and when the guards ask you for proof, you show them irrefutable evidence—Wikipedia.
The guards wave you through and ask you to wait a moment. One minute passes, two minutes pass… After five minutes, you wonder if you should run before the law enforcement shows up. Just as you are about to give up, Peking Duk member Reuben Styles walks over, and you walk into the lounge with him. The band is impressed by your cleverness, and you all share a few beers together, later sharing what happened that night on their Facebook page.
What is a Price Oracle?
A price oracle can refer to anything you consult for price information. When Pam asks Dwight about the cash value of one Schrute buck, Dwight acts as a price oracle.
On Ethereum, everything is a smart contract, including price oracles. Therefore, it is more useful to distinguish how price oracles obtain price information. In one method, you can simply get existing off-chain price data from a price API or exchange and bring it on-chain. In another method, you can calculate the real-time price by consulting an on-chain decentralized exchange (DEX).
Both options have their pros and cons. Off-chain data typically reacts more slowly to volatility, which can be good or bad depending on the purpose for which you are trying to use it, and this method usually requires a few privileged users to push data on-chain, so you must trust that these individuals will not act maliciously or be coerced into pushing bad updates. On-chain data does not require any privileged access and is always up to date, but this means it can be easily manipulated by attackers, leading to catastrophic failures.
What Problems Might Arise?
Let's look at a few examples of significant financial losses in DeFi projects caused by issues with price oracle integration.
1. Synthetix sKRW Oracle Failure
Synthetix is a derivatives platform that allows users to access assets like other currencies. To achieve this, Synthetix at the time relied on a custom off-chain price feed implementation, with price data published on-chain at fixed intervals. These prices allowed users to go long or short on supported assets.
On June 25, 2019, a price feed report that Synthetix relied on stated that the price of the Korean won was 1,000 times higher than the real exchange rate. Due to an error elsewhere in the price oracle system, this price was accepted by the system and published on-chain, after which a trading bot quickly entered and exited the sKRW market.
Although the Synthetix team was able to negotiate with the trader to return the funds (in exchange for a bounty), overall, the bot had the ability to earn over $1 billion in profit.
Synthetix correctly implemented the price oracle and obtained prices from multiple sources to prevent traders from predicting price changes before they were published on-chain. However, an isolated upstream price feed failure led to a devastating attack. This illustrates the risks of using off-chain data for price oracles: you do not know how the price is calculated, so your system must be carefully designed to handle all potential failure modes correctly.
Undercollateralized Lending
As mentioned above, I published an article in September 2019 outlining the risks associated with using on-chain data price oracles. While I strongly recommend reading that article, it is lengthy and filled with technical details that may be difficult to understand. Therefore, I will provide a simplified explanation here.
Imagine you put decentralized lending on the blockchain. Users can deposit assets as collateral and borrow other assets, with the maximum amount they can borrow determined by the value of the assets they deposit. Suppose a user uses ETH as collateral and then borrows USD, with the current price of ETH at $400 and a collateralization ratio of 150%.
If the user deposits 375 ETH, the value of their collateral is $150,000, and according to the rule that for every $1.5 of collateral, $1 can be borrowed, the user can borrow up to $100,000 from the system.
Of course, on the blockchain, it is not as simple as just stating that 1 ETH is worth $400, because a malicious user could simply claim that 1 ETH is worth $1,000 and take all the money from the system. Therefore, developers are likely to integrate spot prices from decentralized exchanges (DEX) like Uniswap, Kyber, or others.
At first glance, this seems like the right approach. After all, whenever you want to buy or sell ETH, the price on Uniswap is always roughly correct, as arbitrageurs quickly correct any discrepancies. However, it turns out that the spot prices on decentralized exchanges (DEX) can be very inaccurate during trading, as shown in the example below.
Consider the storage function of Uniswap, where prices are calculated based on the amount of reserve assets, but the assets held in reserve change as users trade between ETH and USD. What happens if a malicious user executes trades before and after borrowing from the platform?
Before the user borrows, they buy 5,000 ETH at a price of $2 million, and now the price calculated by the Uniswap exchange is 1 ETH = $1,733.33. Now, their 375 ETH can be used as collateral to borrow a maximum of $433,333.33 in assets. Finally, they exchange the $2 million back for 5,000 ETH, which resets the price, and your lending platform loses $333,333.33.
This case study illustrates the most common mistake encountered when using decentralized exchanges (DEX) as price oracles—attackers have almost complete control over the price during the trade, and trying to read that price accurately is like trying to read the weight on a scale before the settlement is completed.
You might end up with the wrong number, which could cost you a lot of money depending on the situation.
2. Synthetix MKR Manipulation
In December 2019, Synthetix suffered another price oracle manipulation attack, notably crossing the boundary between on-chain and off-chain data.
A Reddit user named u/MusaTheRedGuard observed that an attacker was making some very suspicious trades with sMKR and iMKR. The attacker first went long on MKR by purchasing sMKR, then bought a large amount of MKR from the Uniswap ETH/MKR asset trading pair. After waiting for a while, the attacker exchanged their sMKR for an iMKR short position and sold their MKR back to Uniswap. They then repeated this process.
Behind the scenes, the trades the attacker made through Uniswap allowed them to move the price of MKR on Synthetix at will. This was likely because the off-chain price feed that Synthetix relied on actually depended on the on-chain price of MKR, and arbitrageurs did not have enough liquidity to reset the market to optimal conditions.
This incident illustrates a fact: Even if you think you are using off-chain price data, you may still be using on-chain price data, and you may still encounter complex attacks due to using this data.
3. bZx Hack Incident
In February 2020, bZx suffered two hacks within a few days, resulting in losses of approximately $1 million. You can find an excellent technical analysis article about the two hacks written by palkeo here, but we will focus only on the second attack here.
In the second hack, the attacker first used ETH to purchase almost all of the sUSD on Kyber, then the attacker bought a second batch of sUSD from the Synthetix platform and deposited it on bZx. Using sUSD as collateral, the attacker borrowed the maximum amount of ETH they were allowed, then sold the sUSD back to Kyber.
If you noticed, this is essentially the same low-collateral lending attack but using different collateral and different decentralized exchanges.
4. yVault Vulnerability
On July 25, 2020, I reported a vulnerability in the new yVault contract launched by yEarn. You can read the official announcement about this vulnerability here, but I will briefly summarize it below.
The yVault system allows users to deposit tokens and earn yields without needing to manage them themselves. Internally, this vault tracks the total amount of minted yVault tokens and the total amount of underlying tokens deposited. The value of a single yVault token is determined by the ratio of minted tokens to deposited tokens. Any earnings generated by the vault are distributed among all minted yVault tokens (and thus among all yVault token holders).
The first yVault allows users to earn USDC yields by providing liquidity to the Balancer MUSD/USDC reserve pool. When users provide liquidity to Balancer, they receive BPT as a receipt (which can be used to redeem assets in the pool). Therefore, the yVault calculates its value based on the redeemable MUSD/USDC amount corresponding to its BPT.
This seems like a correct implementation, but unfortunately, similar to the principles mentioned earlier—during trading, the state of the Balancer pool is unstable and therefore untrustworthy. In this case, due to the joint curve chosen by Balancer, users exchanging between USDC and MUSD will not receive a 1:1 exchange rate, but will actually leave some MUSD in the reserve pool. This means that the value of BPT can temporarily inflate, allowing attackers to manipulate the price at will and subsequently drain the vault.
This event indicates that price oracles are not always reliable, and developers need to be vigilant about what kind of data they are ingesting and consider whether that data can be easily manipulated by non-privileged users.
5. Harvest Finance Hack
On October 26, 2020, an anonymous user attacked the Harvest Finance reserve pool using a technique (which you may have guessed by now). You can read the official analysis report here, but I will summarize it for you again: the attacker executed trades to lower the price of USDC in the Curve pool, then entered the Harvest pool at the reduced price, reversed the previous trades to restore the price, and exited the Harvest pool at a higher price. This ultimately led to a loss of $33 million for users.
How to Protect Yourself?
Now that I hope you understand price oracle manipulation attacks, if you do not take appropriate defensive measures, attackers may deceive your protocol into handing over money to them. While there is currently no one-size-fits-all solution, there are some solutions that have worked in other projects, and perhaps one of them will suit you.
Recommendation 1: Don't Jump into Shallow Markets
Just like jumping into the shallow end of a swimming pool can be painful, jumping into a shallow market can also lead to significant losses that could change your life forever. Before considering specific price oracle plans, consider whether the liquidity of the token is sufficient to ensure integration with your platform.
Recommendation 2: A Bird in the Hand is Worth Two in the Bush
Seeing potential exchange rates on Uniswap can be tempting, but nothing is final until you actually click the "trade" button and put the tokens in your wallet. Similarly, the best way to determine the exchange rate between two assets is to exchange the assets directly. This method is good because there are no reversals and no assumptions. However, for protocols like lending platforms, this may not work because these protocols need to retain the original assets.
Recommendation 3: Almost Decentralized Oracles
One problem with relying on on-chain data oracles is that their updates can be a bit too fast. If that's the case, why not introduce a little human delay? Write a contract that updates itself with the latest prices from a decentralized exchange (like Uniswap) but requires a few privileged users. Now, even if attackers can manipulate the price, they cannot take advantage of it.
This method is simple to implement and can yield quick results, but it also has some downsides—during blockchain congestion, you may not be able to update prices as quickly as you would like, and you are still vulnerable to sandwich attacks. Additionally, your users now need to trust that you will keep the price updates.
Recommendation 4: Increase Minimum Delay
Manipulating price oracles is a time-sensitive operation because arbitrageurs are always watching and looking for opportunities to optimize any suboptimal markets. If attackers want to minimize risk, they would want to complete the two trades needed to manipulate the price oracle in a single transaction, so that arbitrageurs have no chance to jump in between. As a protocol developer, if your system supports it, implementing a minimum delay between user entry and exit from the system is sufficient.
Of course, this may affect composability, and collaboration between miners and traders is increasing. In the future, malicious actors may execute price oracle manipulation across multiple transactions because they know miners will ensure that no one can jump in between and profit from it.
Recommendation 5: Time-Weighted Average Price (TWAP)
Uniswap V2 introduced a time-weighted average price (TWAP) oracle for on-chain developers to use. This documentation details the exact security guarantees provided by this oracle. Generally speaking, for large asset pools that have not experienced chain congestion for a long time, this TWAP oracle is highly resistant to oracle manipulation attacks. However, due to the nature of its implementation, it may not respond quickly enough during moments of market volatility, and it is only applicable to token assets with on-chain liquidity.
Recommendation 6: M-of-N Reporter Mechanism
Sometimes people say that if you want something done right, do it yourself. But what if you gathered N trusted friends to submit what they believe to be the correct on-chain price and then take the M best answers to form the current price?
Many large projects today use this method: Maker runs a price feed operated by trusted entities, Compound created Open Oracle, and there are oracle reporters like Coinbase, while Chainlink aggregates price data from Chainlink operators and publicly displays this data on-chain. Keep in mind that if you choose to use one of these solutions, you are now delegating trust to third parties, and your users must do the same. Requiring reporters to manually post the latest information on-chain also means that during periods of high market volatility and blockchain congestion, price updates may not be completed in a timely manner.
Conclusion
Price oracles are an important but often overlooked component of DeFi security. Safely using price oracles is challenging, and there are many ways to catch you and your users off guard. In this article, we discussed some examples of price oracles being manipulated and identified that reading price information during trades may be unsafe (leading to potentially catastrophic financial losses). We also discussed some techniques that other projects have adopted in the past to defend against price oracle manipulation. Ultimately, each situation is unique, and you may find yourself unsure whether you are using price oracles correctly. If that is the case, feel free to seek advice.
Special thanks to Dan Robinson and Georgios Konstantopoulos for reviewing this article.