Curve Pool Reentrancy Exploit Postmortem

Curve Pool Reentrancy Exploit Postmortem

Curve Pool Reentrancy Exploit Postmortem

Curve

Curve

Jul 30, 2023

Summary

  • A bug within older versions of the Vyper compiler caused a failure in a security feature used by a limited set of Curve pools (see affected pools below).

  • As a result attackers were able to drain the affected pools of their tokens.

  • The exploit comes directly at the expense of Curve liquidity providers for these affected pools, although Curve is attempting to contact exploiters and recover user funds.

  • Thanks to a white hat, a portion of tokens from one affected pool were recovered by the DAO.

  • The Curve eDAO cannot pause Curve pools or handle user funds in any way. However, it can kill CRV gauge emissions to Curve pools. It is expected the eDAO will kill gauge emissions to all affected pools.

  • For more information:


Exploit Overview

On July 30th between 13:10 and 22:00 UTC, a series of exploits resulted in loss of funds from several Curve pools. The source of the vulnerability in each case is a malfunctioning reentrancy lock in certain versions of Vyper.

Affected pools and amount extracted by each exploit:

  • pETH/ETH | 6,106.65 WETH (~$11m)

  • msETH/ETH | 866.55 WETH (~$1.6 m) and 959.71 msETH (~$1.8m)

  • alETH/ETH | 7,258.70 WETH (~$13.6 m) and 4,821.55 alETH (~9m)

  • CRV/ETH | 7,193,401.77 CRV (~$5.1m at time of exploit), 7,680.49 WETH (~$14.2m), and 2,879.65 ETH (~$5.4m)

Another pool potentially affected is Arbitrum’s Tricrypto pool. Auditors and Vyper devs could not find a profitable exploit, but Curve is advising LPs to exit that pool as a precaution.

On July 30th at 16:44 UTC, Vyper official Twitter acknowledged a bug affecting Vyper versions 0.2.15, 0.2.16, and 0.3.0. The compiler had a bug which negated the reentrancy protection. This bug was patched in version 0.3.1 (accidentally, as the bug remained unknown until July 30th). All contracts after 0.3.1 are in general without this bugged reentrancy guard.

Source: Twitter

The bug was introduced in this PR: #2391

The bug was patched in PRs: #2439 and #2514

Curve's contracts become vulnerable when making a raw_call to send native tokens. The affected Curve pools were each using one of the aforementioned Vyper versions and are paired with native ETH. Tokens using the ERC-777 standard have also been affected, although Curve pools involving these tokens have all largely been deprecated (e.g. pBTC and HOME). ERC-777 adds callbacks, making those pools susceptible to the same reentrancy issue. Pools paired with WETH have not been affected.

There appear to be multiple actors involved with the exploits and at least one is working with the Curve team to recover funds. 2,879.65 ETH (~$5.4m) extracted from the CRV/ETH pool by c0ffeebabe.eth has been returned to the Curve team.

Arbitrum Tricrypto Alert

The Arbitrum Tricrypto pool might be at risk of being exploited. While security researchers have not identified a profitable exploit, Curve is recommending users to exit this pool in an abundance of caution.

Source: Twitter

pETH/ETH Pool Exploit

Exploit tx

On July 30th at 13:10 UTC, an exploit occurred that drained pETH from the Curve pool. The attacker swapped pETH to WETH for a profit of 6,106.65 WETH (~$11m).

Source: Phalcon Explorer

The exploit logic was as follows:

  • The exploiter opened an 80,000 WETH flash loan from Balancer and unwrapped all to ETH.

  • They provided 40,000 ETH as liquidity to the Curve pETH/ETH pool and received 32,431.41 pETH-ETH LP tokens.

  • 3,740 pETH and 34,316 ETH was removed from the pool by burning 32,431.41 pETH/ETH pool LP tokens.

  • They again provided 40,000 ETH as liquidity to the Curve pETH/ETH pool, minting 82,182 more LP tokens.

  • Another 1,184.73 pETH and 47,506.53 ETH was withdrawn by burning 10,272.84 Curve LP tokens.

  • 4,924 pETH was swapped for 4,285 ETH within the Curve pool.

  • 86,106.65 ETH was wrapped to WETH.

  • 80,000 WETH repaid to Balancer to return the flash loan.

  • 6,106.65 WETH ~$11 million was retained as profit.

The reentrancy occurred between the remove liquidity and add liquidity actions. A pool depositor should ordinarily be entitled to pool tokens equal to the product of the total pool token supply and the the fraction of the depositor’s supplied liquidity. However, due to the reentrancy bug, the allotted pool tokens were calculated with the pre-burn balances. This allowed the exploit to infinite mint pool tokens to create a fraudulent claim on the entire contents of the pool.

At time of writing, the funds are held in the JPEGd_69 Exploit wallet address.

The pETH/ETH Curve pool contained ~$21m TVL at the time of the incident, and all funds have since been drained or otherwise removed by users. While the emergency DAO is unable to take action to pause the pool or influence user funds in any way, it is able to freeze CRV gauge emissions to the pool. This action is expected shortly.

msETH/ETH Pool Exploit

Exploit tx

On July 30th at 14:50 UTC, an exploit occurred that drained msETH from the Curve pool. The attacker retained a profit of 866.55 ETH (~$1.6m) and 959.71 msETH (~$1.8).

Source: Phalcon Explorer

The exploit logic was as follows:

  • The exploiter opened an 10,000 WETH flash loan from Balancer and unwrapped all to ETH.

  • They provided 5,000 ETH as liquidity to the Curve msETH/ETH pool and received 4,984.26 alETH-ETH LP tokens.

  • 959.71 msETH and 4,036.22 ETH was removed from the pool by burning 4,984.26 msETH/ETH pool LP tokens.

  • They again provided 5,000 ETH as liquidity to the Curve msETH/ETH pool, minting 11,192.69 more LP tokens.

  • 2,260.72 msETH/ETH LP token was burned to withdraw 6,830.50 ETH (this was a removeLiquidityOne call).

  • 10,866.72 ETH was wrapped to WETH.

  • 10,000 WETH repaid to Balancer to return the flash loan.

  • 866.55 WETH (~$1.6 m) and 959.71 msETH (~$1.8m) was retained as profit.

The reentrancy occurred between the remove liquidity and add liquidity actions. A pool depositor should ordinarily be entitled to pool tokens equal to the product of the total pool token supply and the the fraction of the depositor’s supplied liquidity. However, due to the reentrancy bug, the allotted pool tokens were calculated with the pre-burn balances. This allowed the exploit to infinite mint pool tokens to create a fraudulent claim on the entire contents of the pool.

At the time of writing, the c0ffeebabe.eth address appears to be a whitehat and is working with the Curve team to recover funds (as seen in on-chain messages here and here). The address was also involved with the CRV/ETH pool exploit described below and has returned proceeds from that. However, the address is still in possession of the msETH and ETH attained from the msETH/ETH pool exploit.

The msETH/ETH Curve pool contained ~$4.1m TVL at the time of the incident, and essentially all funds have since been drained or otherwise removed by users. While the emergency DAO is unable to take action to pause the pool or influence user funds in any way, it is able to freeze CRV gauge emissions to the pool. This action is expected shortly.

alETH/ETH Pool Exploit

Exploit tx

On July 30th at 15:34 UTC, an exploit occurred that drained alETH from the Curve pool. The attacker retained a profit of 7,258.70 ETH (~$13.6m) and 4,821.55 alETH (~9m).

Source: Phalcon Explorer

The exploit logic was as follows:

  • The exploiter opened an 40,000 WETH flash loan from Balancer and unwrapped all to ETH.

  • They provided 20,000 ETH as liquidity to the Curve alETH/ETH pool and received 19,895.21 alETH-ETH LP tokens.

  • 4,821.55 alETH and 15,146.77 ETH was removed from the pool by burning 19,895.21 alETH/ETH pool LP tokens.

  • They again provided 20,000 ETH as liquidity to the Curve alETH/ETH pool, minting 34,277.05 more LP tokens.

  • 15,910.04 alETH/ETH LP token was burned to withdraw 32,111.92 ETH (this was a removeLiquidityOne call).

  • 47,258.70 ETH was wrapped to WETH.

  • 40,000 WETH repaid to Balancer to return the flash loan.

  • 7,258.70 WETH (~$13.6 m) and 4,821.55 alETH (~9m) was retained as profit.

The reentrancy occurred between the remove liquidity and add liquidity actions. A pool depositor should ordinarily be entitled to pool tokens equal to the product of the total pool token supply and the the fraction of the depositor’s supplied liquidity. However, due to the reentrancy bug, the allotted pool tokens were calculated with the pre-burn balances. This allowed the exploit to infinite mint pool tokens to create a fraudulent claim on the entire contents of the pool.

At the time of writing, the funds are held in the alETH Exploit wallet address.

Prior to the exploit, Alchemix was able to withdraw 8,027.35 alETH from the pool (tx). As disclosed in this Twitter post, Alchemix was in the process to remove 5,000 ETH from its AMO when they were frontrun by the exploiter. This has resulted in a partial loss of backing for alETH.

The alETH/ETH Curve pool contained ~$46m TVL at the time of the incident, and essentially all funds have since been drained or otherwise removed by users. While the emergency DAO is unable to take action to pause the pool or influence user funds in any way, it is able to freeze CRV gauge emissions to the pool. This action is expected shortly.

CRV/ETH Pool Exploit

There were 2 exploit transactions involved with the CRV/ETH pool (the second being a whitehat).

Exploit tx1

Exploit tx2

Exploit 1

On July 30th at 19:08 UTC, an exploit occurred that drained CRV and ETH from the Curve pool. The attacker retained 7,193,401.77 CRV (~$5.1m at time of exploit) and 7,680.49 WETH (~$14.2m) as profit.

Source: Phalcon Explorer

The exploit logic was as follows:

  • The exploiter opened a 10,000 WETH flash loan from Balancer and unwrapped all to ETH.

  • They provided 400 ETH as liquidity to the Curve CRV/ETH pool and received 9,804.78 CRV/ETH LP tokens.

  • Swap 500 ETH for 1,212,778.28 CRV.

  • Withdraw 517,589.04 CRV and 203.26 ETH from the pool, burning 4,674.13 CRV/ETH LP tokens.

  • Withdraw 1,730,367.33 CRV, burning 4,674.13 CRV/ETH LP tokens.

  • Swap 1,730,367.33 CRV for 639.89 ETH.

  • Repeat the process until CRV has been drained from the pool.

  • 7,193,401.77 CRV (~$5.1m at time of exploit) and 7,680.49 WETH (~$14.2m) was retained as profit.

The reentrancy occurred between the remove liquidity and add liquidity actions. A pool depositor should ordinarily be entitled to pool tokens equal to the product of the total pool token supply and the the fraction of the depositor’s supplied liquidity. However, due to the reentrancy bug, the allotted pool tokens were calculated with the pre-burn balances. This allowed the exploit to infinite mint pool tokens to create a fraudulent claim on the entire contents of the pool.

This exploit is unique because an arbitrage exploit was also conducted in addition to the "reentrancy guard bug" exploit (which affected all four pools). The reason for this is that the CRV-ETH pool is the only non-stable pool exploited in this event.

At the time of writing, the funds have been transferred to a CRV/ETH Exploit wallet address.

Exploit 2

On July 30th at 22:00 UTC, a second exploit occurred that drained 2,879.65 ETH remaining in the pool. This was a whitehat action by c0ffeebabe.eth and the proceeds were returned to the Curve team shortly thereafter (tx).

Source: Phalcon Explorer

The exploit logic was as follows:

  • The exploiter opened a 100 WETH flash loan from Balancer.

  • They swapped 70 WETH for 190,388.61 CRV on Uniswap.

  • Deposit 30,000 CRV to the pool.

  • Claim admin fees to Curve Pool Owner for 1,493.16 CRV/ETH LP tokens.

  • Swap 160,388.61 CRV for 2,949.65 WETH in the pool.

  • Claim admin fees to Curve Pool Owner for 83,647.01 CRV/ETH LP tokens.

  • 100 WETH repaid to Balancer to return the flash loan.

  • 2,879.65 ETH (~$5.4m) was retained from the exploit.

Following the first exploit, the pool had broken balances. It thought it contained a CRV balance, while in reality all CRV had been drained in the first hack. By depositing some CRV and calling claim_admin_fees, the pool balance could be synced. The hacker donated CRV to the pool, synced balances, and then exchanged a few CRV for most of the ETH.

At the time of writing, the funds have been returned to the Curve Deployer address in this tx.

The CRV/ETH Curve pool contained ~$47m TVL at the time of the incident, and most funds have since been drained or removed by users. The pool still contains some ETH, which existing LPs can withdraw by calling remove_liquidity_one_coin.

While the emergency DAO is unable to take action to pause the pool or handle user funds in any way, it is able to freeze CRV gauge emissions to the pool. This action is expected shortly.

Next Steps

Immediate next steps are to kill gauge emissions to the affected pools and create new plain pools for alETH, msETH, and pETH. New pools should be paired with WETH or the new ETH pool implementation. CRV already has a new Tricrypto pool paired with crvUSD and ETH that is not affected by the reentrancy bug.

The Curve team will continue to explore all avenues for the recovery of user funds and updates on the situation will be made on the social channels (Twitter, Telegram, and Discord).

© 2023 Llama Risk. All rights reserved.