On March 17, 2023, due to a vulnerability in the price oracle, Paraspace became the target of a hacker attack. After the hacker's three failed attempts, the BlockSec Phalcon system intervened timely, saving over 5 million dollars worth of ETH through a rescue operation. Details and anecdotes about the rescue can be found at this link.

Background
ParaSpace (now known as Parallel Finance) is an NFT lending platform that allows you to utilize NFTs and ERC-20 tokens as collateral. By depositing these assets into ParaSpace, users can borrow ERC-20 tokens, offering the opportunity to generate returns on NFTs without the need to sell them.
One of the interesting features of ParaSpace is ApeStaking, which enables the auto-compounding of APE rewards. Users who engage in ApeStaking on the ParaSpace platform receive cAPE—a cToken representation of APE—which they can then contribute to the lending pool.
Specifically, ParaSpace allows cAPE to be used as collateral for borrowing assets like USDC and WETH. Users can deposit cAPE tokens to obtain pcAPE shares. The collateral value is calculated by multiplying the amount of pcAPE with the rebasingIndex of the pcAPE token, as folllows:
  function _scaledBalanceOf(address user, uint256 rebasingIndex)
      internal
      view
      returns (uint256)
  {
      return super.scaledBalanceOf(user).rayMul(rebasingIndex);
  }
The pcAPE token's rebasingIndex is determined by the lastRebasingIndex function, which, in turn, invokes the getPooledApeByShares() function. The latter calculates the index using the formula sharesAmount.mul(_getTotalPooledApeBalance()).div(totalShares). The function _getTotalPooledApeBalance() represents the total value of APE tokens in the pools, while totalShares refers to the total supply of cAPE tokens.
  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);
      }
  }
The _getTotalPooledApeBalance() function is designed to provide the total sum of stakeAmount, rewardAmount, and bufferBalance as sourced from the ApeCoinStaking contract. More precisely, stakeAmount refers to the number of APE tokens that have been staked in the cAPE position within the ApeCoinStaking contract.
  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;
Vulnerability Analysis
Unfortunately, the earlier mentioned rebasingIndex is vulnerable to manipulation, which can artificially inflate the collateral value of the cAPE token.
Specifically, within the ApeCoinStaking contract, the depositApeCoin() function is designed to increase the stakedAmount for a given position. An exploit can occur if APE tokens are deposited into the cAPE position using the depositApeCoin() function, as this action can lead to an inflated output from the _getTotalPooledApeBalance() function, resulting in a distorted 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();
  }
Attack Analysis
With the vulnerability identified, the mechanics of the attack become relatively straightforward. The attacker inflated the collateral value by depositing APE tokens through the depositApeCoin function, setting cAPE as the recipient, and then proceeded to borrow more tokens.
Take BlockSec's blocking transaction as an example, there are five key steps:
- Obtaining a flash loan of approximately 47,352 wstETH. Supplying about 46,018 wstETH to borrow cAPE through the creation of multiple contracts.
- Depositing approximately 12,880,000 cAPE to the protocol as collateral.
- Trading about 1,205 wstETH for roughly 492,124 APE and withdraw 1,839,999 cAPE to APE tokens.
- Depositing 2,332,214 APE tokens into the cAPE position by invoking ApeCoinStaking.depositApeCoin(), which changes the protocol's APE stakedAmount from 851,662 to 3,183,876, an increase of approximately 373%.
- Leveraging the inflated collateral to borrow substantial amounts of various assets (i,e USDC, WETH) for profit.
Summary
In the ParaSpace incident, the price of pcAPE was manipulated by the attacker using a flashloan, exploiting the use of spot prices for calculation. This incident highlights the critical need for vigilance when incorporating price oracles into DeFi protocols, underscoring the necessity of ensuring that a protocol's price oracle is resistant to manipulation.
Moreover, given the escalating prevalence of security breaches in Web3, it is crucial for protocols to establish threat monitoring and prevention systems that remain active throughout their operational phases, not merely rely on pre-launch code audits. As demonstrated by the ParaSpace incident, BlockSec's Phalcon, which is capable of automatically blocking crypto hacks, managed to intervene ahead of the attacker, successfully safeguarding user assets.
Read other articles in this series:
- Lead-In: Top Ten "Awesome" Security Incidents in 2023
- #1: Harvesting MEV Bots by Exploiting Vulnerabilities in Flashbots Relay
- #2: Euler Finance Incident: The Largest Hack of 2023
- #3: KyberSwap Incident: Masterful Exploitation of Rounding Errors with Exceedingly Subtle Calculations
- #4: Curve Incident: Compiler Error Produces Faulty Bytecode from Innocent Source Code
- #5: Platypus Finance: Surviving Three Attacks with a Stroke of Luck
- #6: Hundred Finance Incident: Catalyzing the Wave of Precision-Related Exploits in Vulnerable Forked Protocols
- #8: SushiSwap Incident: A Clumsy Rescue Attempt Leads to a Series of Copycat Attacks
- #9: MEV Bot 0xd61492: From Predator to Prey in an Ingenious Exploit
- #10: ThirdWeb Incident: Incompatibility Between Trusted Modules Exposes Vulnerability



