Analysis of the Beluga Protocol Exploit

5 min read

Learn how the Beluga Protocol was exploited, resulting in a loss of funds worth $175,000.

TL;DR#

On October 13, 2023, the Beluga Protocol was exploited across multiple transactions on the Arbitrum chain, resulting in a loss of funds worth approximately $175,000.

Introduction to Beluga Protocol#

Beluga Protocol is a multichain stableswap AMM that aims to solve the problem of liquidity fragmentation with seamless cross-chain swaps.

Vulnerability Assessment#

The root cause of the exploit is price manipulation of the underlying assets.

Steps#

Step 1:

We attempt to analyze one of the attack transactions executed by the exploiter.

Step 2:

Let's understand the liquidity to be withdrawn via the withdrawFrom function of the affected contract.

The liability to burn becomes a dynamic value, and thus would be changed if the asset liability or the total supply values were to change.

/**
 * @notice Calculates fee and liability to burn in case of withdrawal
 * @param asset The asset willing to be withdrawn
 * @param liquidity The liquidity willing to be withdrawn
 * @return amount Total amount to be withdrawn from Pool
 * @return liabilityToBurn Total liability to be burned by Pool
 * @return fee The fee of the withdraw operation
 */
function _withdrawFrom(Asset asset, uint256 liquidity) private view returns (uint256 amount, uint256 liabilityToBurn, uint256 fee, bool enoughCash) {
  liabilityToBurn = (asset.liability() * liquidity) / asset.totalSupply();
  require(liabilityToBurn > 0, "INSUFFICIENT_LIQ_BURN");

  fee = _withdrawalFee(_slippageParamK, _slippageParamN, _c1, _xThreshold, asset.cash(), asset.liability(), liabilityToBurn);

  // Get equilibrium coverage ratio before withdraw
  uint256 eqCov = getEquilibriumCoverageRatio();

  // Init enoughCash to true
  enoughCash = true;

  // Apply impairment in the case eqCov < 1
  uint256 amountAfterImpairment;
  if (eqCov < ETH_UNIT) {
    amountAfterImpairment = (liabilityToBurn).wmul(eqCov);
  } else {
    amountAfterImpairment = liabilityToBurn;
  }

  // Prevent underflow in case withdrawal fees >= liabilityToBurn, user would only burn his underlying liability
  if (amountAfterImpairment > fee) {
    amount = amountAfterImpairment - fee;

    // If not enough cash
    if (asset.cash() < amount) {
      amount = asset.cash(); // When asset does not contain enough cash, just withdraw the remaining cash
      fee = 0;
      enoughCash = false;
    }
  } else {
    fee = amountAfterImpairment; // fee overcomes the amount to withdraw. User would be just burning liability
    amount = 0;
    enoughCash = false;
  }
}

Step 3:

The swap function will then update the liability value, but use the Oracle price to perform the token exchange.

/**
 * @notice Swap fromToken for toToken, ensures deadline and minimumToAmount and sends quoted amount to `to` address
 * @param fromToken The token being inserted into Pool by user for swap
 * @param toToken The token wanted by user, leaving the Pool
 * @param fromAmount The amount of from token inserted
 * @param minimumToAmount The minimum amount that will be accepted by user as result
 * @param to The user receiving the result of swap
 * @param deadline The deadline to be respected
 * @return actualToAmount The actual amount user receive
 * @return haircut The haircut that would be applied
 */
function swap(
  address fromToken,
  address toToken,
  uint256 fromAmount,
  uint256 minimumToAmount,
  address to,
  uint256 deadline
) external ensure(deadline) nonReentrant whenNotPaused returns (uint256 actualToAmount, uint256 haircut) {
  require(fromToken != address(0), "ZERO");
  require(toToken != address(0), "ZERO");
  require(fromToken != toToken, "SAME_ADDRESS");
  require(fromAmount > 0, "ZERO_FROM_AMOUNT");
  require(to != address(0), "ZERO");

  IERC20 fromERC20 = IERC20(fromToken);
  Asset fromAsset = _assetOf(fromToken);
  Asset toAsset = _assetOf(toToken);

  // Intrapool swapping only
  require(toAsset.aggregateAccount() == fromAsset.aggregateAccount(), "DIFF_AGG_ACC");
  (actualToAmount, haircut) = _quoteFrom(fromAsset, toAsset, fromAmount);
  require(minimumToAmount <= actualToAmount, "AMOUNT_TOO_LOW");

  fromERC20.safeTransferFrom(address(msg.sender), address(fromAsset), fromAmount);
  fromAsset.addCash(fromAmount);
  toAsset.removeCash(actualToAmount);
  toAsset.addLiability(_dividend(haircut, _retentionRatio));
  toAsset.transferUnderlyingToken(to, actualToAmount);

  emit Swap(msg.sender, fromToken, toToken, fromAmount, actualToAmount, to);
}

Step 4:

Therefore, an attacker could deposit USDT tokens and then use the swap between USDT and USDC_E to update the asset liability.

Due to the stable coin price deduced from the Oracle, the ratio of the USDC_E to USDT swap is consistent, but the withdrawal amount gets impacted, letting the attacker spend less USDT to withdraw back larger amounts of profits.

Step 5:

The attacker has since then transferred 113.3 ETH, worth approximately $175,681, to this address and then laundered them into MEXC.

Aftermath#

The team hasn't shared any details of the incident on their social media platforms at Twitter (X) at the time of this writing, but has sent an on-chain message to the exploiter to return the user's funds for a white-hat bounty reward of 20% of the stolen assets.

Solution#

In the wake of this exploit on Beluga Protocol, we find it crucial to emphasize the significance of utilizing reputable, multifaceted, and robust oracle solutions. The unfortunate price manipulation incident, stemming from such a vulnerability, highlights the importance of creating resilient systems that can anticipate and guard against malicious actors.

In scenarios like these, where price manipulation stands central, it becomes imperative to leverage oracle systems like ChainLink, which amalgamate data from numerous sources to provide accurate price feeds. Time-weighted average prices (TWAPs) play a pivotal role in ensuring price stability, mitigating abrupt, and likely manipulative, price changes. Adequate liquidity within pools, which the oracle leans on for price determination, is also a cardinal element in maintaining a fortified defense against manipulation.

While the above measures are paramount, vulnerabilities, at times, may still surface. This is where Neptune Mutual steps in to protect the end-users. If the team associated with Beluga Protocol had established a dedicated cover pool with us prior to the exploit, the aftermath of the incident could have been notably alleviated. At Neptune Mutual, we understand the intricacies of the DeFi landscape, which is why we offer coverage to users who might face losses from vulnerabilities in smart contracts.

With Neptune Mutual, users are spared the hassle of submitting a detailed proof of losses. As soon as an incident is confirmed and resolved via our incident resolution mechanism, we prioritize quick compensation disbursement, providing prompt support to the victims.

Our marketplace operates on numerous popular blockchain networks, including EthereumArbitrum, and the BNB chain. This extensive reach allows us to serve a broad spectrum of DeFi users, offering protection from potential vulnerabilities and enhancing their faith in the ecosystem.

Reference Source Ancilia

By

Tags