#7:ParaSpace事件:与时间赛跑,阻止行业最严峻的攻击

ParaSpace事件:与时间赛跑,阻止行业最关键的攻击

#7:ParaSpace事件:与时间赛跑,阻止行业最严峻的攻击

2023年3月17日,由于价格预言机存在漏洞,Paraspace成为黑客攻击的目标。在黑客三次尝试失败后,BlockSec的Phalcon系统及时介入,通过一次救援行动挽回了价值超过500万美元的ETH。有关救援的细节和轶事可以在此链接找到。

点击此处阅读推文

背景

ParaSpace(现称Parallel Finance)是一个NFT借贷平台,允许用户将NFT和ERC-20代币用作抵押品。通过将这些资产存入ParaSpace,用户可以借入ERC-20代币,从而有机会在不出售NFT的情况下从NFT中获利。

ParaSpace的一个有趣功能是ApeStaking,它支持APE奖励的自动复投。在ParaSpace平台上进行ApeStaking的用户将收到cAPE——即APE的cToken表示——然后他们可以将这些cAPE存入借贷池。 具体来说,ParaSpace允许将cAPE用作抵押品来借入USDCWETH等资产。用户可以通过存入cAPE代币来获得pcAPE份额。抵押品价值的计算方法是将pcAPE的数量乘以pcAPE代币的rebasingIndex,如下所示:

  function _scaledBalanceOf(address user, uint256 rebasingIndex)
      internal
      view
      returns (uint256)
  {
      return super.scaledBalanceOf(user).rayMul(rebasingIndex);
  }

pcAPE代币的rebasingIndexlastRebasingIndex函数确定,该函数又调用getPooledApeByShares()函数。后者使用sharesAmount.mul(_getTotalPooledApeBalance()).div(totalShares)的公式计算指数。_getTotalPooledApeBalance()函数表示池中APE代币的总价值,而totalShares指的是cAPE代币的总供应量。

  function lastRebasingIndex() internal view override returns (uint256) {
    return ICApe(_underlyingAsset).getPooledApeByShares(WadRayMath.RAY);
  }

  function getPooledApeByShares(uint256 sharesAmount)
      public
      view
      returns (uint256)
  {
      uint256 totalShares = _getTotalShares();
      if (totalShares == 0) {
          return 0;
      } else {
          return
              sharesAmount.mul(_getTotalPooledApeBalance()).div(totalShares);
      }
  }

_getTotalPooledApeBalance()函数旨在提供从ApeCoinStaking合约中获取的stakeAmountrewardAmountbufferBalance的总和。更具体地说,stakeAmount指的是在ApeCoinStaking合约中的cAPE仓位中已质押的APE代币数量。

  function _getTotalPooledApeBalance()
      internal
      view
      override
      returns (uint256)
  {
      (uint256 stakedAmount, ) = apeStaking.addressPosition(address(this));
      uint256 rewardAmount = apeStaking.pendingRewards(
          APE_COIN_POOL_ID,
          address(this),
          0
      );
      return stakedAmount + rewardAmount + bufferBalance;

漏洞分析

不幸的是,前面提到的rebasingIndex容易受到操纵,这会人为地夸大cAPE代币的抵押品价值。

具体来说,在ApeCoinStaking合约中,depositApeCoin()函数旨在增加给定仓位的stakedAmount。如果使用depositApeCoin()函数将APE代币存入cAPE仓位,则可能发生利用,因为此操作可能导致_getTotalPooledApeBalance()函数的输出膨胀,从而导致rebasingIndex失真。

  function depositApeCoin(uint256 _amount, address _recipient) public {
      if (_amount < MIN_DEPOSIT) revert DepositMoreThanOneAPE();
      updatePool(APECOIN_POOL_ID);

      Position storage position = addressPosition[_recipient];
      _deposit(APECOIN_POOL_ID, position, _amount);

      apeCoin.transferFrom(msg.sender, address(this), _amount);

      emit Deposit(msg.sender, _amount, _recipient);
  }

  function _deposit(uint256 _poolId, Position storage _position, uint256 _amount) private {
    Pool storage pool = pools[_poolId];
    _position.stakedAmount += _amount;
    pool.stakedAmount += _amount.toUint96();
    _position.rewardsDebt += (_amount * pool.accumulatedRewardsPerShare).toInt256();
  }

攻击分析

在识别出漏洞后,攻击的机制就变得相对简单。攻击者通过depositApeCoin函数存入APE代币来夸大抵押品价值,将cAPE设为接收者,然后继续借入更多代币。

以BlockSec的阻止交易为例,有五个关键步骤:

  • 获得约47,352 wstETH的闪电贷。存入约46,018 wstETH,通过创建多个合约借入cAPE。
  • 向协议存入约12,880,000 cAPE作为抵押品。
  • 用约1,205 wstETH交易了约492,124 APE,并提取了1,839,999 cAPE到APE代币。
  • 通过调用ApeCoinStaking.depositApeCoin()将2,332,214 APE代币存入cAPE仓位,这使得协议的APE stakedAmount从851,662增加到3,183,876,增幅约为373%。
  • 利用夸大的抵押品借入大量各种资产(如USDC、WETH)以获利。

总结

在ParaSpace事件中,攻击者利用了即期价格的计算方式,通过闪电贷操纵了pcAPE的价格。该事件凸显了在DeFi协议中集成价格预言机时保持警惕的关键需求,并强调了确保协议的价格预言机能够抵抗操纵的必要性。

此外,鉴于Web3安全漏洞的普遍性不断增加,协议至关重要的一点是建立威胁监控和预防系统,这些系统应在其运营的整个阶段保持活跃,而不仅仅依赖于预发布代码审计。正如ParaSpace事件所示,BlockSec的Phalcon,能够自动阻止加密货币黑客攻击,设法在攻击者之前进行干预,成功地保护了用户资产。

阅读本系列其他文章:

Sign up for the latest updates