2023年8月3日、Arbitrum上のMEVボットが攻撃を受け、80万ドルの損失が発生しました。この攻撃の根本原因は、不十分なユーザー入力検証でした。
MEVボットとそのコントラクト間の複雑なやり取りを考慮すると、DeFiプロトコルにおいて、オープンソースではないことがセキュリティを保証するわけではないということが浮き彫りになりました。
背景
MEVボット
MEVボット(最大抽出可能価値ボット:Maximum Extractable Value Bot)は、ブロックチェーン上の収益機会を特定し、実行するように設計されています。 これは、保留中のトランザクション(つまり、メモリプール内のトランザクション)やオンチェーンの状態を分析することで、アービトラージを通じて利益を生み出します。
一般的なフロントランニングやサンドイッチ攻撃を行うMEVボットとは異なり、今回の攻撃対象となったMEVボットは、三角アービトラージや債務清算といった戦略の実行に焦点を当てていました。これらのボット自体は、AMM(自動マーケットメーカー)の価格安定化を助け、レンディングプロトコルの清算を補助して円滑な運用を支えており、DeFiエコシステムの健全な機能に欠かせない要素となっています。
フラッシュローン
フラッシュローンは、DeFiエコシステムにおけるユニークなイノベーションであり、無担保融資の形態の一つです。同じトランザクション内で期限内に返済することを条件に、最大で10億ドルもの資金を担保なしで借り入れることができます。もし期限内に返済が完了しなければ、トランザクションは最初からなかったかのように巻き戻されます。
このメカニズムは一般的に、アービトラージや他のDeFi戦略を活用して、市場の一時的な非効率性から利益を得るために使用されます。
脆弱性
概要
ユーザー入力パラメータの検証が不十分であったため、攻撃者は「偽のフラッシュローン提供者(FakeFlashloanProvider)」を組み込むことができました。Vaultコントラクトはこの提供者を利用してフラッシュローンを開始しました。その後、おそらくフラッシュローンの決済を行うために、Vaultコントラクトは偽の提供者に対してトークンの承認(Approval)を行ってしまい、結果としてVaultから不正に資産が転送される事態となりました。
詳細
悪用されたコントラクトは以下の通りです:
Vault:被害を受けたコントラクト 0xd614927acfb9744441180c2525faf4cedb70207f。「Vault」として機能し、準備金を提供したり、AAVEやBalancerといった他のプロトコルからフラッシュローンを促進したりします。Arbitrage Bot:脆弱なコントラクト 0x8db0efee6a7622cd9f46a2cf1aedc8505341a1a7。「Arbitrage Bot」として機能し、「Vault」コントラクトで借り手(Borrower)の役割を担っています。
「Arbitrage Bot」内の関数 0x0582f20f() が、アービトラージを開始するためのメインのエントリポイントです。
この関数はまず「Vault」の borrow() を呼び出して元の元本を取得し、次にキャルデータ(calldata)で指定された外部コントラクトに対して検証なしで delegatecall を実行し、アービトラージのロジックを実行します。
function 0x0582f20f(...) {
...
v67, /* uint256 */ v68 = address(0xd614927acfb9744441180c2525faf4cedb70207f).borrow(address(v39), address(v9[0]), v29).gas(msg.gas);
...
// 0x4da91757 = swap(address,address,address,uint256,uint256,uint256,address)
MEM[MEM[64] + 32] = uint224(address(MEM[0 + v4[v69]])) | 0x4da9175700000000000000000000000000000000000000000000000000000000;
v82 = address(v76 >> 96).delegatecall(MEM[(MEM[64]) len 228], MEM[(MEM[64]) len 0]).gas(msg.gas);
...
v189 = v170.refund(0x410085df, address(v9[0]), address(v39), v68, address(v9[0]), v29, v186, 4 + MEM[64] + (varg2.length << 5) - (4 + MEM[64]) + 192).gas(msg.gas);
...
}
続いて、「Vault」内の 0x512b7351() を呼び出し、攻撃者の FakeFlashloanProvider コントラクトに対してフラッシュローンを開始します。
関数 0x512b7351() は msg.sender が許可リストに含まれていることを要求しますが、前の delegatecall によってチェックが回避されてしまいました。これは非常に重要なステップです。
function 0x512b7351(...) public nonPayable {
...
if (_borrow[msg.sender] >= 1) {
v0 = !_refund;
}
require(v0, Error('BBVault: FORBIDDEN'));
...
v38 = v23.length;
v39 = v23.data;
_refund = keccak256(v23);
...
<FakeFalshloanProvider>.flashloan(...);
...
}
フラッシュローンのコールバック中、「Vault」の executeOperation() は、まず借り入れた資産を「Arbitrage Bot」MEVボット 0x8db0ef に転送し、次にその 0x7fe3ba8b() を呼び出します。
function executeOperation(...) {
...
require(_refund == keccak256(v3.data), Error('BBVault: STATUS'));
Token.transfer(ArbitrageBot, amountBorrowed);
<ArbitrageBot>.call(0x7fe3ba8b...);
}
「Arbitrage Bot」はこの外部呼び出しを信頼し、受け取った資産を FakeFlashloanProvider に送り返します。
しかし、「Vault」はこれを認識できず、executeOperation() の最後にフラッシュローンを返済するためとして、依然として FakeFlashloanProvider に対して承認(Approval)を与えたままの状態になります。
攻撃プロセス
攻撃トランザクション: 0x864c8cfb8c54d3439613e6bd0d81a5ea2c5d0ad25c9af11afd190e5ea4dcfc1f
攻撃者は「Arbitrage Bot」の 0x0582f20f() を呼び出し、それが攻撃者のコントラクトへのデリゲートコールを実行します。


次に hack_contract_2 が victim(被害者)の関数 0x512b7351() を呼び出します。0x512b7351() は msg.sender が許可リストにあることを要求しますが、前述の delegatecall によってチェックが回避されます。

victim は攻撃者の FakeFlashloanProvider コントラクトを呼び出し、フラッシュローンされたすべての資産を victim に転送して、その executeOperation() を呼び出します。

「Arbitrage Bot」の 0x7fe3ba8b() は再び 攻撃者のコントラクト への delegatecall を実行し、今度はすべての資産を攻撃者に送り返します。
この時点で、攻撃者のフラッシュローン提供者が貸し出した資産は返済されたことになります。

被害者(Vault)は、フラッシュローンを返済する意図で FakeFlashloanProvider を承認(approve)します。

攻撃者はこの承認を悪用し、transferFrom を使って被害者から資金を抜き取ります。
セキュリティに関する推奨事項
非オープンソースコードはセキュリティを保証しない
非オープンソースで難読化されたコードであればセキュリティが確保されると考えるのは誤解です。今回のMEVボットの事件は、機密保持がエクスプロイト(脆弱性攻撃)に対する防御にはならず、むしろ開発者に誤った安心感を与えてしまう可能性があることを示しています。
厳格な入力検証
フラッシュローンやスワップのコールバックといった標準的なインターフェースを扱う際には特に、すべてのコントラクトの相互作用とキャルデータを細心の注意を払って検証することが不可欠です。データ整合性とセキュリティの確保は、コントラクトの設計と実装において最優先事項であるべきです。
シリーズのその他の記事を読む:
- 導入: 2023年の「素晴らしい」セキュリティインシデント・トップ10
- #1: Flashbots Relayの脆弱性を悪用したMEVボットの収穫
- #2: Euler Financeインシデント: 2023年最大規模のハッキング
- #3: KyberSwapインシデント: 非常に巧妙な計算を用いた丸め誤差の巧みな悪用
- #4: Curveインシデント: コンパイラのバグにより不正なソースコードから欠陥のあるバイトコードが生成される
- #5: Platypus Finance: 幸運にも3度の攻撃を生き延びる
- #6: Hundred Financeインシデント: 脆弱なフォークプロトコルにおける精度関連の悪用の波を誘発
- #7: ParaSpaceインシデント: 業界で最も危険な攻撃を阻止するための時間との戦い
- #8: SushiSwapインシデント: 不器用な救済の試みが模倣攻撃の連鎖を招く
- #10: ThirdWebインシデント: 信頼済みのモジュール間の互換性欠如が脆弱性を生む



