Understanding Conic Finance Exploit

5 min read

Learn how Conic Finance was exploited, resulting in a loss of approximately $4.1 million.

TL;DR#

On July 21, 2023, Conic Finance was exploited due to a smart contract vulnerability, resulting in a loss of approximately $3.2 million.

Introduction to Conic Finance#

Conic Finance is a platform built for liquidity providers to easily diversify their exposure to multiple Curve pools.

Vulnerability Assessment#

The root cause of the vulnerability is the price manipulation of assets caused by read-only reentrancy.

Steps#

Step 1:

We attempt to analyze the attack transaction executed by the exploiter.

Step 2:

The Conic ETH Omnipool utilized `CurveHandlerV3` to ascertain whether a reentrant call was active. This check was triggered solely when the pool being accessed contained ETH, confirmed by calling the `_isETH` function.

/// @notice Validates if a given Curve pool is currently in reentrancy
/// @dev Reverts if it is in reentrancy
/// @param _curvePool Curve pool to validate
function reentrancyCheck(address _curvePool) external override {
  if (!_isETH(_curvePool)) return;
  require(!isReentrantCall(_curvePool), "CurveHandler: reentrant call");
}

Step 3:

The team assumed that their Curve v2 pools with ETH had the ETH address listed among their coins. Nevertheless, it turned out that they were using the WETH address instead. Consequently, the `_isETH` function incorrectly returned false, allowing the reentrancy guard of the rETH pool to be bypassed.

Step 4:

By circumventing the reentrancy check, the attacker gained the ability to manipulate the price of the rETH Curve LP token. This manipulation was employed to deceive the ETH Omnipool into minting a greater amount of cncETH LP tokens than it should have for their deposits.

Step 5:

The issue with the read-only reentrancy can be found in this vulnerable remove_liquidity function, in which when the attacker removes liquidity from the Curve pools, it will burn the LP token and make a reentrancy call to the attacker.

@external
@nonreentrant('lock')
def remove_liquidity(_amount: uint256, min_amounts: uint256[N_COINS],
                     use_eth: bool = False, receiver: address = msg.sender):
    """
    This withdrawal method is very safe, does no complex math
    """
    lp_token: address = self.token
    total_supply: uint256 = CurveToken(lp_token).totalSupply()
    CurveToken(lp_token).burnFrom(msg.sender, _amount)
    balances: uint256[N_COINS] = self.balances
    amount: uint256 = _amount - 1  # Make rounding errors favoring other LPs a tiny bit

    for i in range(N_COINS):
        d_balance: uint256 = balances[i] * amount / total_supply
        assert d_balance >= min_amounts[i]
        self.balances[i] = balances[i] - d_balance
        balances[i] = d_balance  # now it's the amounts going out
        coin: address = self.coins[i]
        if use_eth and coin == WETH20:
            raw_call(receiver, b"", value=d_balance)
        else:
            if coin == WETH20:
                WETH(WETH20).deposit(value=d_balance)
            response: Bytes[32] = raw_call(
                coin,
                _abi_encode(receiver, d_balance, method_id=method_id("transfer(address,uint256)")),
                max_outsize=32,
            )
            if len(response) != 0:
                assert convert(response, bool)

    D: uint256 = self.D
    self.D = D - D * amount / total_supply

    log RemoveLiquidity(msg.sender, balances, total_supply - _amount)

Step 6:

The attacker initially took a flash loan of 20,550 rETH, 3,000 CbETH, and 28,504 WETH and deposited this liquidity using this Vyper Contract. When a call to the remove_liquidity function was invoked, the LP tokens were then burned and ETH was transferred to the attacker, which triggered the fallback function.

Step 7:

This fallback function then called the withdraw function from the ConicEthPool contracts, and because the remove_liquidity function was still in process, it led to the manipulation of the rETH-f.totalsupply value, allowing the attacker to gain approximately 1724 ETH.

Step 8:

The attackers repeatedly executed this attack by engaging in a cycle of deposits and withdrawals at a favorable exchange rate, consequently draining funds from the Omnipool.

Step 9:

The exploiter has since transferred all of the exploited funds to this address and is parked there at the time of this writing.

Aftermath#

The team initially acknowledged the occurrence of the exploit on their ETH Omnipool and stated that they have disabled deposits on the Conic front end while working on identifying its root cause.

They identified the root cause of the exploit and deployed a fix to the affected contracts. The team also sent an on-chain message to the attacker with hopes of discussing the possibility of the retrieval of the stolen funds.

Following the exploit on the ETH Omnipool, the crvUSD Omnipool was also exploited. A total of $934,000 was stolen from this contract, giving the attacker a profit of approximately $300,000. In response to this, the team immediately enforced maximum safety measures and temporarily shut down all Omnipools.

Solution#

The Conic Finance exploit highlights the importance of robust security measures and comprehensive smart contract auditing. The attack exploited a smart contract vulnerability related to read-only reentrancy, resulting in a significant loss of funds.

To prevent such exploits, it's crucial to implement proper reentrancy locks and security measures in smart contracts. Publicly available reentrancy locks can provide developers with the option to revert in case the lock is activated during execution. Additionally, regular smart contract audits by independent third-party auditors can help identify vulnerabilities and recommend mitigation strategies before attackers can exploit them.

While we cannot prevent the occurrence of all hacks, the impact of this attack could have been significantly reduced if Conic Finance had established a dedicated cover pool with Neptune Mutual. Neptune Mutual offers coverage to users who suffer losses due to smart contract vulnerabilities through our innovative parametric policies.

Users who purchase our parametric cover policies do not need to provide evidence of their loss to receive payouts. Once an incident is confirmed and resolved through our incident resolution system, payouts can be claimed immediately. Our marketplace is available on multiple popular blockchain networks, including Ethereum, Arbitrum, and the BNB chain, providing coverage to a diverse array of DeFi users and bolstering their confidence in the ecosystem.

Moreover, Neptune Mutual's security team would have evaluated the platform for various security considerations, including DNS and web-based security, frontend and backend security, intrusion detection and prevention, and more. Such comprehensive evaluations can provide valuable insights into enhancing the overall security of the platform.

Reference Source Conic

By

Tags