2023年3月17日、価格オラクルの脆弱性により、Paraspaceはハッカーの標的となりました。ハッカーによる3回の試行が失敗した後、BlockSecのPhalconシステムがタイムリーに介入し、救済措置により500万ドル以上のETHを救出しました。救済の詳細や逸話については、こちらのリンクをご覧ください。

背景
ParaSpace(現在はParallel Financeとして知られています)は、NFTやERC-20トークンを担保として利用できるNFTレンディングプラットフォームです。これらの資産をParaSpaceに預け入れることで、ユーザーはERC-20トークンを借り入れることができ、NFTを売却することなくNFTから収益を生み出す機会を提供します。
ParaSpaceの興味深い機能の1つは、APE報酬の自動複利計算を可能にするApeStakingです。ParaSpaceプラットフォームでApeStakingに参加したユーザーは、APEのcToken表現であるcAPEを受け取り、それをレンディングプールに提供できます。
具体的には、ParaSpaceではcAPEをUSDCやWETHなどの資産を借り入れるための担保として使用できます。ユーザーはcAPEトークンを預け入れることでpcAPEシェアを取得できます。担保価値は、pcAPEの量にpcAPEトークンのrebasingIndexを乗算して計算されます。
function _scaledBalanceOf(address user, uint256 rebasingIndex)
internal
view
returns (uint256)
{
return super.scaledBalanceOf(user).rayMul(rebasingIndex);
}
pcAPEトークンのrebasingIndexはlastRebasingIndex関数によって決定され、これは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コントラクトから取得したstakeAmount、rewardAmount、bufferBalanceの合計を提供するように設計されています。より具体的には、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()関数を使用してcAPEポジションにAPEトークンが預け入れられた場合、_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のブロックされたトランザクションを例に取ると、5つの主要なステップがあります。
- 約47,352 wstETHのフラッシュローンを取得。複数のコントラクトを作成してcAPEを借り入れるために、約46,018 wstETHを供給。
- プロトコルに約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は、攻撃者に先駆けて介入し、ユーザー資産を無事保全することに成功しました。
このシリーズの他の記事を読む
- リードイン:2023年のトップ10「恐ろしい」セキュリティインシデント
- #1:Flashbotsリレーの脆弱性を悪用したMEVボットの収穫
- #2:Euler Financeインシデント:2023年最大のハッキング
- #3:KyberSwapインシデント:極めて微妙な計算による丸め誤差の巧妙な悪用
- #4:Curveインシデント:コンパイラエラーが正規のソースコードから不正なバイトコードを生成
- #5:Platypus Finance:幸運な一撃で3回の攻撃を生き残る
- #6:Hundred Financeインシデント:脆弱なフォークされたプロトコルにおける精度関連エクスプロイトの波を触媒
- #8:SushiSwapインシデント:不器用な救済試行がコピーキャット攻撃の連鎖につながる
- #9:MEVボット0xd61492:捕食者から獲物へ、巧妙なエクスプロイト
- #10:ThirdWebインシデント:信頼されたモジュールの間の互換性が脆弱性を露呈



