How to exploit the same vulnerability of MetaPool in two different ways (Nerve Bridge / Saddle Finance) — What you see is not what you get
On April 30th 2022, an attacker exploited the same vulnerability in the Nerve Bridge Incident to attack the Saddle Finance. In total, 4,900 Ether were under attack. Fortunately, 1,360 Ether of them were successfully saved by us. The detailed description of this incident can be found from the official post mortem.
Although the same vulnerability was exploited, the attack method is different from the previous one. As the new attack method is not as straightforward as one may expect, we believe it deserves more efforts to perform a deep investigation. In this report, we first briefly illustrate the vulnerability, and then review the original attack method of the Nerve Bridge Incident. After that, we focus on the Saddle Finance Incident by dissecting the attack process to demystify the new attack method.
0x1. About the Deployed Contracts
The relevant contract addresses are listed in the following:
- The victim MetaSwap contract: 0x824dcd7b044d60df2e89b1bb888e66d8bcf41491
- The vulnerable MetaSwapUtils contract: 0x88Cc4aA0dd6Cf126b00C012dDa9f6F4fd9388b17
It is worth noting that the shown code of the MetaSwapUtils contract associated with the verified MetaSwap contract is NOT equivalent to the deployed MetaSwapUtils contract, whose address is specified in the
Settings, as follows:
So do not be confused with these two MetaSwapUtils contracts:)
0x2. Vulnerability Analysis
The vulnerable contract belongs to MetaPool, which has been discussed in detail in the previous blog. In short, MetaPool was originally designed by Curve to allow a single coin to be pooled with all the coins in another (base) pool without diluting its liquidity. It’s essentially a pool consisting of a stable-coin and a LP token of the base pool, which consists of several other stable-coins. There is a concern for the MetaPool design, i.e., the MetaPool is basically a stable-coin pool maintaining prices for stable-coins, while the LP token of a base stable-coin pool is NOT a stable-coin.
In fact, the price of a LP token of a base stable-coin pool can be obtained by invoking the
getVirtualPrice function of the base pool, and its price steadily increases with the accumulation of fees charged by the base pool. To handle that, MetaPool scales up the LP token's reserve before calculating the price, as shown in the below.
Accordingly, if a user swaps a LP token for a stable-coin, the amount of the LP token will be scaled up before calculating the price. Alternatively, if a user swaps a stable-coin for a LP token, the calculated amount of the LP token will be scaled down before transferring and bookkeeping.
The above code snippet of the
swapUnderlying function is used to swap between the stable-coin in the MetaPool and those stable-coins in the base pool. As shown in the two red rectangles, the function scales up the amount of incoming LP token and scales down the amount of LP token being exchanged.
However, the implementation of the
swap function is inconsistent with that of the
swapUnderlying function. Specifically, the root cause of the vulnerability is due to the flawed calculation implemented in the
swap function (i.e., the
_calculateSwap function) which does not properly scale down and up the amount of the LP token. As shown in the below, the left side gives the vulnerable code of MetaPool, while the right side gives the fixed version.
The attacker of the Nerve Bridge Incident exploited the inconsistency between the
swap function and
swapUnderlying function. (Note that Nerve Bridge is a fork project of Saddle Finance.) After that, Saddle Finance fixed the vulnerability and re-deployed the new version (i.e., V2) of the MetaSwapUtils library immediately.
Unfortunately, due to some unknown reason, the sUSD V2 MetaPool on Ethereum was still deployed with the old and vulnerable MetaSwapUtils library. As a result, this vulnerability was successfully exploited by an attacker once again on April 30th. Interestingly, unlike the attack method used in the Nerve Bridge Incident, a different way was adopted to attack the vulnerable MetaPool.
0x3. The Original Attack Method of the Nerve Bridge Incident
We re-use the below figure (see the the previous blog) to review the original attack method.
Since the vulnerable
swap function does not scale down the amount of the LP token (Nerve 3-LP) being exchanged, the exchanged amount (36,959) in the Step 3 is larger than normal. Then the attacker invokes the
swapUnderlying function (without vulnerability) to sell the 36,959 Nerve 3-LP (in the Step 4 and 5) for 51,494 fUSDT, which profits 1,143 fUSDT.
Reasonably, the profit can be explained as: the attacker harvests more Nerve 3-LP in the Step 3, and then the attacker sells them in a “normal” price by leveraging the inconsistency between the
swap function and the
0x4. The New Attack Method of the Saddle Finance Incident
The attacker of the recent Saddle Finance Incident used a different way to attack the same vulnerable
swap function without involving the
swapUnderlying function. Here we take one attack transaction as a concrete example to illustrate the process.
Intuitively, it seems that there should not exist any profit, as any implication of Step 3 and Step 4 would be counteracted. Specifically, in Step 3, the attacker could swap out more saddleUSD due to the lack of scaling down the LP token (i.e., saddleUSD) being exchanged. However, in Step 4, the attacker would inevitably swap out LESS sUSD, because the vulnerable
swap function does not scale up the amount of incoming saddleUSD before calculating the price.
However, as shown in the above figure, the attacker profited 2,059,771 sUSD through the pair of swaps in Step 3 and Step 4. To figure out the reason of the profit, we have to dive into the pricing mechanism and perform a deep investigation to understand the attack process.
0x4.1 The Pricing Mechanism
The MetaPool of Saddle Finance inherits Curve’s pricing formula:
The graph of the function (when n equals to 2) is shown as the blue curve in the below figure. (The design of the formula can be found in Curve StableSwap Whitepaper.)
Here comes the question: how does the MetaPool calculate the price for each swap by using this formula?
Let’s say n is 2, and a user uses dx0 token0 to swap dx1 token1. We can simulate the calculation process for dx1. In each swap,
A can be treated as a constant, and
D is the only variable that can affect the price curve. In fact,
D is increased with the accumulation of the fees charged by the pool. Specifically, the calculation process can be summarized as the following three steps:
- Step I: Put the current pool’s reserves (x0 and x1) into the formula to calculate the current
D, which determines the current price curve.
- Step II: Let the x0 increase dx0, and put the current
Dand x0 into the formula to calculate the new x1.
- Step III: Then, dx1 is the difference between the new x1 and the old x1.
If token0 is the LP token of a base pool, then the Step II becomes:
Here baseVirtualPrice / 1e18 is around 1.0033 during the attack. Alternatively, if the token is the LP token of a base pool, then the Step III becomes:
To understand how
D affects the price curve, we also use an example to describe it. Say a user first swaps dx0 token0 for dx1 token1, and then swaps dx_1dx1 token1 for dx0′ token0.
As shown in the above figure, since Step ② charges fees for the first swap,
D is increased to shift the price curve upwards (from the black curve to the blue one). Furthermore, the figure clearly describes the reason why dx0′ is smaller than dx0.
0x4.2 The Attack Analysis
To analyze the reason of the profit, we deployed the vulnerable and fixed MetaSwapUtils libraries locally and used the state of the victim pool at that moment to simulate the attack. Furthermore, during this simulation, we also recorded a few values that can help to understand the attack process, i.e.,
A is 10,000, x_sUSD is 8,130,463, x_saddleUSD is 9,688,608, and
D is 17,818,392 at that moment.
The above figure describes the process of the profitable pair of swaps:
- Swap-I: swap 14,800,272 sUSD for 9,657,586 saddleUSD
- Swap-II: swap 9,657,586 saddleUSD for 16,860,043 sUSD
Specifically, Swap-I can be divided into the following two steps:
- ①: Swap 14,800,272 sUSD for 9,625,654 saddleUSD. Now,
Dis increased as 17,931,435 (due to the charged fees).
- ②: Since the vulnerable MetaPool does not scale down the amount of exchanged saddleUSD, the pool losses 31,932 saddleUSD. The losses decrease
Das 15,736,195, which further shifts the price curve down (from the black curve to the gray one).
Similarly, Swap-II can also be divided into two steps:
- ③: Since the price curve is shifted down, the same 9,625,654 saddleUSD can swap out 16,891,906 sUSD that is far more than the cost: 14,800,272 sUSD.
- ④: Since the vulnerable MetaPool does not scale up the amount of incoming saddleUSD before calculating the price, there is 31,863 sUSD left in the MetaPool, which shifts the price curve up (from the gray curve to the blue one). Nevertheless, the pair of swaps still profits 2,059,771 sUSD.
Obviously, the above analysis clearly elaborates why the attacker could harvest the profit by using the new attack method. Besides, it seems that the original attack method seems to be more efficient than the new one due to the sUSD left in the MetaPool in Swap-II. Of course, the attacker could launch multiple attacks to drain the pool, which have been observed in the wild.
0x5. Some Take Away
The investigation suggests that the root cause for the profits in the two incidents is the same. Specifically, the first swap (which swaps for the LP token) decreases
D of the vulnerable MetaPool, which further shifts its price curve down. That shifting greatly affects the subsequent pricing and is the main reason for the subsequent profit.
The BlockSec Team focuses on the security of the blockchain ecosystem, and collaborates with leading DeFi projects to secure their products. The team is founded by top-notch security researchers and experienced experts from both academia and industry. They have published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, and released detailed analysis reports of high-impact security incidents.