Appearance
Puppet V2 チャレンジ:価格操作を巧みに利用し、DVT 資産を救出せよ
かつての「Puppet」チャレンジで、オラクルの脆弱性を突いた手法を覚えていますか? Damnvulnerabledefi の開発者たちはその教訓を踏まえ、さらに進化した Puppet V2 を用意しました。今回は Uniswap V2 を価格オラクルとして採用し、推奨ライブラリも使用されています。一見すると、完璧に見える設計です。
チャレンジ目標
あなたの初期資産:
- 20 ETH
- 10,000 DVT
一方で、Puppet V2 プールには:
- 1,000,000 DVT
👉 あなたの目標は、このすべての DVT を recovery アカウントへ移動させること
仕組みの理解:Uniswap V2 と Puppet V2
■ Uniswap V2 Library.sol
このライブラリは、Uniswap の基本機能を提供します:
sortTokens:トークン順序の正規化pairFor:ペアアドレス計算getReserves:流動性取得quote:価格見積もりgetAmountOut / getAmountIn:スワップ計算(手数料込み)getAmountsOut / getAmountsIn:多段スワップ対応
■ PuppetV2Pool.sol
このチャレンジの中核となる貸付プール:
borrow(uint256 borrowAmount)
- 指定量の DVT を借りる
- そのために 3倍相当の WETH を担保として預ける必要あり
calculateDepositOfWETHRequired(uint256 tokenAmount)
- 必要な WETH を計算
- Uniswap の価格を使用
_getOracleQuote
- Uniswap のリザーブから価格算出
潜在的な脆弱性:価格オラクルの限界
Uniswap V2 は通常は信頼できる価格を提供しますが:
👉 短時間の極端な価格変動には弱い
問題の核心
- 価格は「リザーブ比率」に依存
- リザーブは「スワップ」で操作可能
👉 つまり価格は操作可能
攻撃戦略:価格操作
初期状態
自分:20 ETH + 10,000 DVT
プール:1,000,000 DVT
Uniswap:
- 10 WETH
- 100 DVT
ステップ1:価格を崩壊させる
👉 目的:DVT の価格を極端に下げる
方法:
10,000 DVT をすべて売却(DVT → WETH)
プール内:
- DVT 増加
- WETH 減少
👉 結果:
👉 DVT の価格が暴落
ステップ2:借入コストを激減
価格が下がると:
calculateDepositOfWETHRequiredの結果も低下
👉 必要な担保が激減
ステップ3:全量借りる
solidity
uint256 amountToBorrow = token.balanceOf(address(pool));
pool.borrow(amountToBorrow);
👉 ほぼタダ同然の WETH で
👉 1,000,000 DVT を取得
ステップ4:回収
solidity
token.transfer(recovery, amountToBorrow);
コントラクト(MyContract)の流れ
1. ETH → WETH
solidity
weth.deposit{value: msg.value}();
2. DVT 回収
solidity
token.transferFrom(player, address(this), dvtValue);
3. DVT → WETH スワップ
solidity
router.swapExactTokensForTokens(...)
👉 価格操作の核心
4. 借入
solidity
pool.borrow(amountToBorrow);
5. 回収
solidity
token.transfer(recovery, amountToBorrow);
重要ポイントまとめ
- Uniswap は価格オラクルとして完全ではない
- リザーブ操作 = 価格操作
- オンチェーン価格は攻撃対象になり得る
- 担保モデルは価格依存のため脆弱
結論
Puppet V2 は一見安全に見えますが:
👉 「リアルタイム価格」を信じすぎた設計が破綻の原因
攻撃者は:
- DEX を操作し
- 価格を歪め
- 借入条件を崩壊させる
そして最終的に:
👉 巨大な資産を低コストで奪取
このチャレンジは、DeFi における重要な教訓を示しています:
「価格は真実ではない。流動性によって作られる幻想である。」