Understanding the Hope Money Exploit

4 min read

Learn how Hope Money was exploited, resulting in a loss of assets worth 528 ETH.

TL;DR#

On October 18, 2023, Hope Money was exploited on the Ethereum Mainnet, resulting in a loss of 528 ETH, worth approximately $835,000.

Introduction to Hope#

The HOPE ecosystem provides a comprehensive set of use cases for the HOPE token, including swap, lending, custody, clearing, and settlement, while incentivizing users to participate in the ecosystem and community governance.

Vulnerability Assessment#

The root cause of the exploit is the loss of precision during smart contract operations.

Steps#

Step 1:

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

Step 2:

The attacker opens a position in HopeLend by initially taking a flash loan of 2,000 WBTC, and thus the flashloan function in the Pool contract adds the flash loan fees to the reserve's liquidityIndex.

Step 3:

The exploiter is able to manipulate the `liquidityIndex` of the hEthWBTC contract from 1e27 to 7,560,000,001e27, which results in a precision loss.

/**
 * @notice Handles repayment of flashloaned assets + premium
 * @dev Will pull the amount + premium from the receiver, so must have approved pool
 * @param reserve The state of the flashloaned reserve
 * @param params The additional parameters needed to execute the repayment function
 */
function _handleFlashLoanRepayment(DataTypes.ReserveData storage reserve, DataTypes.FlashLoanRepaymentParams memory params) internal {
  uint256 premiumToProtocol = params.totalPremium.percentMul(params.flashLoanPremiumToProtocol);
  uint256 premiumToLP = params.totalPremium - premiumToProtocol;
  uint256 amountPlusPremium = params.amount + params.totalPremium;

  DataTypes.ReserveCache memory reserveCache = reserve.cache();
  reserve.updateState(reserveCache);
  reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
    IERC20(reserveCache.hTokenAddress).totalSupply() + uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
    premiumToLP
  );

  reserve.accruedToTreasury += premiumToProtocol.rayDiv(reserveCache.nextLiquidityIndex).toUint128();
  reserve.updateInterestRates(reserveCache, params.asset, amountPlusPremium, 0);

  IERC20(params.asset).safeTransferFrom(params.receiverAddress, reserveCache.hTokenAddress, amountPlusPremium);

  ILendingGauge lendingGauge = IAbsGauge(reserveCache.hTokenAddress).lendingGauge();
  if (address(lendingGauge) != address(0)) {
    lendingGauge.updateAllocation();
  }

  IHToken(reserveCache.hTokenAddress).handleRepayment(params.receiverAddress, params.receiverAddress, amountPlusPremium);

  emit FlashLoan(params.receiverAddress, msg.sender, params.asset, params.amount, DataTypes.InterestRateMode(0), params.totalPremium, params.referralCode);
}

Step 4:

As a result of the precision loss in operations, the attacker borrowed a significant amount of assets from other markets and redeemed all WBTC collateral to take away their share of profits.

/**
 * @notice Divides two ray, rounding half up to the nearest ray
 * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
 * @param a Ray
 * @param b Ray
 * @return c = a raydiv b
 */
function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
  // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
  assembly {
    if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
      revert(0, 0)
    }

    c := div(add(mul(a, RAY), div(b, 2)), b)
  }
}

Step 5:

The original attacker was front-run by a MEV bot, and they split the proceeds equally, paying a bribe of 263 ETH.

Aftermath#

The team acknowledged the occurrence of the incident and stated that the attack resulted in a loss of approximately 528 ETH, out of which 263.91 ETH were bribed by the frontrunner to a Validator managed by Lido.

The team is actively reaching out to the parties involved in hopes of recovering the stolen assets.

Solution#

To address the Hope Money exploit, a swift, all-encompassing upgrade of security protocols is crucial, starting with an in-depth audit of the smart contracts, focusing especially on lending, flash loans, and critical variable management. This scrutiny is vital to ensure the precision of calculations and to thwart potential manipulative activities that could instigate similar future violations.

Concurrently, the platform must roll out sophisticated precision safeguarding protocols. This entails setting stringent constraints on numerical operations to prevent precision loss and establishing rigorous controls on pivotal variables that influence lending algorithms. These robust measures are essential to intercept any manipulative transactions or operations, thus protecting users' assets against complex threats.

Precision in numerical operations is critical, particularly for calculations involving ratios, rates, or percentages, necessitating the allowance for larger numerators in fractions. The sequence of operations also demands attention; calculations necessitating both multiplication and division should always prioritize multiplication to preserve precision. A recommended practice is to elevate variables to a higher precision, conduct all calculations, then revert to the required precision.

Given Solidity's lack of support for floating-point numbers, developers resort to fixed-point arithmetic for decimal numbers. This technique involves scaling values (e.g., multiplying by 10^18 for Ether), executing operations in this scaled integer form, and then scaling down as needed. Though effective in maintaining precision, this approach demands meticulous management of the scaling factor, underscoring fixed-point arithmetic's role in precision retention without incurring risks.

Despite the highest levels of diligence, the ever-evolving landscape of digital assets and smart contracts means that the risk of exploits can never be completely eradicated. This inherent unpredictability underscores the critical importance of protective measures like those offered by Neptune Mutual. If Hope Money had established a collaborative framework with us to create a dedicated cover pool, the financial repercussions of this exploit could have been substantially mitigated. These cover policies function as a financial cushion, offering users a means to recoup potential financial losses due to vulnerabilities within smart contracts.

Partnering with Neptune Mutual eliminates the need for users to undergo the often cumbersome process of providing intricate proof of loss. Once an incident has been verified and resolved through our incident resolution framework, our protocol stresses the importance of quickly paying out compensations, which helps those who were affected financially right away.

Functioning across multiple blockchain networks, such as EthereumArbitrum, and the BNB chain, Neptune Mutual is committed to providing its protective umbrella to a broad spectrum of participants in the DeFi space. Our unyielding commitment to user security fosters stronger trust within the DeFi ecosystem and reinforces confidence, especially following significant security incidents like the one encountered by Hope Money.

Reference Source BlockSec

By

Tags