2026年1月25日、SwapNetとAperture FinanceがEthereum、Arbitrum、Base、BSCに展開したコントラクトを標的とした一連の不正なトランザクションを検知し、総損失額は1,700万ドルを超えました。大まかに言うと、両方のインシデントの根本原因は単純であり、当初のアラートで既に概要が説明されています [1、2]: 入力検証が不十分なため、被害者コントラクトが任意の呼び出し機能を露呈し、攻撃者が既存のトークン承認を悪用して transferFrom を呼び出し、資産を流出させることが可能になりました。
しかしながら、被害者コントラクトの両セットはクローズドソースであり、逆コンパイルすると数千行のコードに拡張され、深くネストされた複雑な分岐ロジックを持つため、分析の難易度が著しく増加します。さらに、影響を受けたプロジェクトがリリースした事後分析 [3、4] は主に修復と回復に焦点を当てており、根本的な技術的詳細に関する議論は限定的でした。その結果、脆弱な呼び出しパスがどのように構築されたのか、そして既存のチェックがなぜ悪用を防げなかったのかといった、いくつかの重要な疑問が未解決のまま残っています。
本レポートでは、逆コンパイルされたバイトコードとオンチェーン実行トレースに基づいた、より詳細な技術分析を提供します。ソースコードの欠如は可視性を制限しますが、バイトコードレベルの分析は脆弱なロジックを再構築するのに十分であり、高レベルのアラートからはすぐに明らかにならないコントラクト設計に関するいくつかの興味深い観察結果を明らかにします。
まずSwapNetインシデントの詳細な分析から始め、次にAperture Financeインシデントの詳細な分析を行います。
SwapNetインシデント
背景
SwapNet [5] は、AMMやプライベートマーケットメーカーを含む複数のオンチェーンソースからの流動性を集約することで、最適なスワップルートを見つけるように設計されたDEXアグリゲーターです。このプロトコルは、ユーザーがスワップ実行時にカスタムルーターまたはプールを指定することも可能にし、さらなる柔軟性を提供します。
原因分析
このインシデントは、ユーザー提供の入力検証が不十分であることが原因で、攻撃者は「任意のパラメーター」で transferFrom() 呼び出しをトリガーできるようになりました。その結果、以前に被害者コントラクト(例: 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e)に承認されていた資産が攻撃者に転送される可能性がありました。
逆コンパイルされたバイトコードに基づくと、関数 0x87395540() は、重要な入力に対して適切な検証が欠けているようです。期待されるルーターまたはプールアドレスをトークンアドレス(例: USDC)に置き換えることで、コントラクトは誤ってトークンを有効な実行ターゲットとして扱います。これにより、攻撃者が制御するcalldataで低レベルの呼び出しが実行されます。
結果として、被害者コントラクトは approvedAsset.transferFrom(victim, attacker, amount) の形式で呼び出しを実行し、攻撃者は承認されたすべての資産を詐取できるようになりました。
攻撃フロー
SwapNetに対して複数の攻撃が観測されました。ここでは、Baseトランザクション 0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57 を例として使用します。
攻撃者は単に悪意のある入力で被害者コントラクトの関数 0x87395540() を呼び出しました。この呼び出しは2つの主要なステップで構成されます。
- 主要な内部変数(例:
v51)がUSDCに設定され、意図されたルーティングロジックがバイパスされました。
- 攻撃者が制御するcalldataを使用して低レベルの呼び出しが実行され、
USDC.transferFrom()が呼び出されて承認されたすべてのUSDCが流出しました。
損失概要、トランザクション、および影響を受けたコントラクト
SwapNetインシデントは、複数のチェーンにわたって約1,341万ドルの損失を引き起こしました。以下の表は、主要なエクスプロイトトランザクションと関与した被害者コントラクトアドレスをまとめたものです。
| チェーン | Txハッシュ | 損失 |
|---|---|---|
| Base | 0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57 | 1,300万ドル |
| Base | 0x6ce28d386c852241fdb5b5e32424fc8fe0ec7846023e62be8b31a34023e03bf6 | 29,000ドル |
| BSC | 0x6d49748bb1c5c127c5e8adca075caf37510b9e1a065f357706aaab632033d834 | 15,000ドル |
| Arbitrum | 0x25c08b3882ade18cbbda81521afff7239c0e91d050f6c178968802cb1b2e2b04 | 29,000ドル |
| チェーン | コントラクトアドレス |
|---|---|
| Base | 0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e |
| BSC | 0xc7C6d9154CB1f2c3eCA469D410bD757486798C96 |
| Arbitrum | 0xc3b93F41fC85405B1bD9d135eE822Bdd90e54ef4 |
Aperture Financeインシデント
背景
Aperture Finance [6] は、ユーザーに代わってUniswap V3 LPsなどの集約された流動性ポジションを管理するDeFiプロトコルです。そのクローズドソースコントラクト(例: 0xD83d960deBEC397fB149b51F8F37DD3B5CFA8913)は、ユーザーがネイティブトークンを使用してUniswap V3ポジションを作成および管理することを可能にします。
UniswapV3ポジション作成の意図されたワークフロー
0x67b34120() 関数を介してUniswap V3ポジションを作成する際、コントラクトは意図された3段階のワークフローに従います。
-
ネイティブトークンをラップする
-
内部関数
0x1d33()を介してネイティブトークンをスワップする -
UniswapV3ポジションを作成する
問題はステップ2で発生します。0x1d33() は低レベルの呼び出しを実行してカスタムスワップを行いますが、その際、重要なパラメーター(例: 呼び出しターゲットとcalldata)はユーザー制御であり、検証が不十分であるため、意図しない外部呼び出しが可能になります。詳細は次のセクションで説明します。
原因分析
SwapNetの場合と同様に、Aperture Financeインシデントは、低レベルの呼び出しにおける入力検証が不十分であることが原因です。0x67b34120() が呼び出されると、内部関数 0x1d33() は、呼び出しターゲットまたは関数セレクターに対して厳密な制約を強制することなく、ユーザーが提供したcalldataを使用して低レベルの呼び出しを実行します。
下の図に示すように、低レベルの呼び出しをトリガーするために使用されるcalldataは、純粋に攻撃者の入力に基づいています。
これにより、攻撃者は approvedToken.transferFrom(victim, attacker, amount) が被害者コントラクトのコンテキストで実行されるような悪意のあるcalldataを構築できます。その結果、ERC20トークンだけでなく、承認されたUniswap V3ポジションNFTも詐取される可能性があります。
攻撃フロー
Aperture Financeに対して複数の攻撃が観測されました。ここでは、Ethereumトランザクション 0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a を代表的な例として使用します。
-
攻撃者は攻撃コントラクト
0x5c92884dFE0795db5ee095E68414d6aaBf398130を作成しました。 -
攻撃コントラクトは、悪意のある入力と100 weiのETH(すなわち
msg.value==100)で関数0x67b34120()を呼び出しました。a) ネイティブETHは、関数
WETH.deposit()を介してWETHにラップされました。
b) 内部関数 `0x1d33()` が呼び出されて低レベルの呼び出しを実行しました。このステップでは、`WBTC.transferFrom(victim, attacker, amount)` の呼び出しが被害者コントラクトのコンテキストで実行され、攻撃者は承認されたトークンを詐取できるようになりました。なお、関数 `0x1d33()` の最後で残高チェックが通過しました。具体的には、関数 `0x1d33()` は残高の変更を、攻撃者が指定したスワップ出力値(すなわち `varg2.word2`)と比較しました。その結果、何も受け取らなかったため、正常に実行されました。
3. 最後に、関数 `NonfungiblePositionManager.mint()` が呼び出され、100 weiのWETHを使用して攻撃者のポジションが作成されました。
興味深い発見
通常と異常なポジション作成トランザクションを比較すると、両方とも同じ承認者(例: OKX DEX: TokenApprove)にトークンを承認しているものの、異なるルーターアドレス(すなわち DexRouter および WBTC)を指定していることが観察されました。これは、コントラクトが承認された承認者の検証を強制する一方で、実際の実行ターゲットの検証に失敗している可能性があり、任意の呼び出しを介して悪用可能な重大なギャップが残されていることを示唆しています。
損失概要、トランザクション、および影響を受けたコントラクト
Aperture Financeインシデントは、複数のチェーンにわたって約367万ドルの総損失をもたらしました。以下の表は、主要なエクスプロイトトランザクションと関連する被害者コントラクトアドレスをまとめたものです。
| チェーン | コントラクトアドレス |
|---|---|
| Ethereum | 0xD83d960deBEC397fB149b51F8F37DD3B5CFA8913 |
| Base | 0x09243cBbd312d73eE671C1e3B473FBdFd91b2324 |
| Arbitrum | 0xaf34783A7160ba1bEd6DC94dFB6740f73076bb32 |
| Arbitrum | 0xD83d960deBEC397fB149b51F8F37DD3B5CFA8913 |
| Arbitrum | 0xccc715f90B31ca93eCEA9FA22F9A70ac02486e12 |
| Arbitrum | 0x09D9632cAdf1AdF4B87D67460397Fbb8c62C5fAa |
結論
SwapNetとAperture Financeのインシデントは異なるプロトコルとチェーンに影響を与えましたが、両方のケースにおける根本的な問題は複雑ではありません。それは、トークン承認を保持するコントラクトでの、ユーザー制御の低レベル呼び出しと不十分な入力検証の組み合わせでした。これらのインシデントは、コントラクト設計における柔軟性は、厳格な呼び出し制約との慎重なバランスを取る必要があることを思い出させてくれます。特に、外部レビューが限定的なクローズドソースシステムではなおさらです。
参照
[1] https://x.com/Phalcon_xyz/status/2015614087443697738
[2] https://x.com/Phalcon_xyz/status/2015624519898234997
[3] https://meta.matcha.xyz/SwapNet-Incident-Post-Mortem
[4] https://x.com/ApertureFinance/status/2015938720453820752
[6] https://x.com/ApertureFinance
BlockSecについて
BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号コンプライアンスプロバイダーです。私たちは、顧客がコード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、リアルタイムでの攻撃遮断、インシデント分析、不正資金追跡を行い、プロトコルとプラットフォームのライフサイクル全体にわたってAML/CFT義務を履行するのを支援する製品とサービスを構築しています。
BlockSecは、著名なカンファレンスで複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションの複数のゼロデイ攻撃を報告し、複数のハッキングを阻止して2000万ドル以上を救済し、数十億ドルの暗号通貨を確保してきました。
-
公式ウェブサイト: https://blocksec.com/
-
公式Twitterアカウント: https://twitter.com/BlockSecTeam



