How Was EraLend Exploited?

5 min read

Learn how EraLend on zkSync was exploited, resulting in a loss of $2.76 million.

TL;DR#

On July 25, 2023, EraLend was exploited due to a smart contract vulnerability, resulting in a loss of approximately $2.76 million.

Introduction to EraLend#

EraLend is a lending and borrowing protocol on zkSync.

Vulnerability Assessment#

The root cause of the vulnerability is a read-only reentrancy issue caused by the incorrect implementation of EraLend's LP oracle.

Steps#

Step 1:

We attempt to analyze this, and the other attack transactions executed by the exploiter.

Step 2:

The LP oracle of EraLend used the pool reserve amounts to determine the LP price.

Step 3:

Due to this, the pool executed the callback before the reserves were updated, and the attacker was able to re-enter with the reserves, which were not updated yet, to manipulate the oracle price.

Step 4:

The attacker called the `burn` function to burn their own LP tokens while borrowing ctokens in the reentrancy, with the borrow amount calculated based on the SyncSwap reserves during the time of the attack. As the reserves were not updated before the reentrancy call, the calculated amount used the value of the pre-burn reserves.

function burn(bytes calldata _data, address _sender, address _callback, bytes calldata _callbackData)
    external
    override
    nonReentrant
    returns (TokenAmount[] memory _amounts)
  {
    ICallback.BaseBurnCallbackParams memory params;

    (params.to, params.withdrawMode) = abi.decode(_data, (address, uint8));
    (params.balance0, params.balance1) = _balances();
    params.liquidity = balanceOf[address(this)];

    // Mints protocol fee if any.
    // Note `_mintProtocolFee` here will checks overflow.
    bool _feeOn;
    (_feeOn, params.totalSupply) = _mintProtocolFee(params.balance0, params.balance1, 0);

    // Calculates amounts of pool tokens proportional to balances.
    params.amount0 = params.liquidity * params.balance0 / params.totalSupply;
    params.amount1 = params.liquidity * params.balance1 / params.totalSupply;
    //require(_amount0 != 0 || _amount1 != 0);

    // Burns liquidity and transfers pool tokens.
    _burn(address(this), params.liquidity);
    _transferTokens(token0, params.to, params.amount0, params.withdrawMode);
    _transferTokens(token1, params.to, params.amount1, params.withdrawMode);

    // Updates balances.
    /// @dev Cannot underflow because amounts are lesser figures derived from balances.
    unchecked {
      params.balance0 -= params.amount0;
      params.balance1 -= params.amount1;
    }

    // Calls callback with data.
    // Note reserves are not updated at this point to allow read the old values.
    if (_callback != address(0)) {
      // Fills additional values for callback params.
      params.sender = _getVerifiedSender(_sender);
      params.callbackData = _callbackData;

      ICallback(_callback).syncSwapBaseBurnCallback(params);
    }

    // Updates reserves and last invariant with up-to-date balances (after transfers).
    _updateReserves(params.balance0, params.balance1);
    if (_feeOn) {
      invariantLast = _computeInvariant(params.balance0, params.balance1);
    }

    _amounts = new TokenAmount[](2);
    _amounts[0] = TokenAmount(token0, params.amount0);
    _amounts[1] = TokenAmount(token1, params.amount1);

    emit Burn(msg.sender, params.amount0, params.amount1, params.liquidity, params.to);
  }

Step 5:

The attacker took a flash loan of approximately 14,080,109 USDC and 7,566 ETH and deposited them in the SyncSwapVault vault. The read-only reentrancy manipulated the Oracle price, letting the attacker borrow more assets than they were supposed to.

Step 5:

The loan against this borrowed amount was repaid using the calculations from the updated reserves; thus, the difference amount after repaying the flash loan was held as profits for the attacker.

Step 6:

The stolen funds have so far been bridged to eight different wallets on the Ethereum, Arbitrum, and Optimism chains.

Aftermath#

Following the incident, the team acknowledged the occurrence of the exploit and stated that the threat had been contained. Only USDC deposits on EraLend were affected by this incident, and all the other assets remained secure and unaffected, including SyncSwap USDC/ETH LP supplied on EraLend. They immediately took action to suspend all borrowing operations, and advised against depositing USDC. Additionally, they have also reduced the interest rate of the USDC pool to protect affected borrowing positions from potential liquidation.

The team is working with partners and security firms to determine the next course of action for recovering the funds for their protocol users.

Solution#

The EraLend exploit underscores the critical importance of robust security measures and comprehensive smart contract auditing in DeFi protocols. The attack exploited a read-only reentrancy vulnerability in EraLend's LP oracle, leading to price manipulation and significant loss of funds.

In order to safeguard against exploits originating due to reentrancy attack, a mutex can be used to make the function of a smart contract non-reentrant. Incomplete non-reentrant mutex, on the other hand, can result in cross-contract or cross-function reentrancy.

If reentrancy locks are already in place, numerous security solutions may be applicable for the security of the project. The reentrancy locks can be made public so that developers can choose whether or not to revert in the event that the lock is activated. If the lock is active, revert in the view function.

The DeFi protocols also need to ensure their price oracles are correctly implemented. This includes a broad set of sources to ensure pricing accuracy and resilience against manipulation. Utilizing oracles that take advantage of multiple data points can help ensure a more accurate reflection of asset prices. It is also crucial to have adequate liquidity in any pools that the oracle relies on for price determination. Attacks of this nature leading to Oracle price manipulation could have been regulated to a greater extent using data providers like ChainLink.

However, despite the most meticulous precautions, some attacks may still succeed. This is where solutions like Neptune Mutual can play a critical role in managing the aftermath of such incidents. If the team associated with EraLend had established a dedicated cover pool with Neptune Mutual, the impact of this exploit could have been significantly curtailed. Neptune Mutual offers coverage for users who have suffered losses of funds or digital assets due to smart contract vulnerabilities, utilizing their 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 EthereumArbitrum, 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 BlockSec

By

Tags