
Blastチェーン9700万ドル争奪戦、ある国のハッカーが鈍った?
TechFlow厳選深潮セレクト

Blastチェーン9700万ドル争奪戦、ある国のハッカーが鈍った?
Munchablesプロジェクトが攻撃を受け、約9700万ドルがリスクにさらされたが、一体何が起きたのか?
執筆:CertiK
背景
BlastはBlurの創設者Pacman(Tieshun Roquerre、aka.鉄順)が立ち上げたEthereum Layer2ネットワークであり、2月29日にメインネットを起動しました。現在、約19,500 ETHおよび640,000 stETHがBlastのメインネットにステーキングされています。
攻撃対象となったプロジェクトMunchablesは、Blastが主催するBigbangコンテストで優勝した高品質なプロジェクトです。
Blast公式は、BlastメインネットにETHをステーキングしているユーザーに対して通常のポイントを付与しています:

DeFiプロジェクトへの参加を促進するために、Blast公式は優良なプロジェクトを選定し推奨しており、ユーザーがETHをDeFiに再ステーキングすることで、より速いポイント獲得速度とゴールドポイントを得ることができます。そのため、多くのユーザーがBlastメインネットにステーキングされたETHを新規DeFiプロジェクトに投入しています。
しかし、こうしたDeFiプロジェクトの成熟度やセキュリティは未だ検証段階にあり、これらのコントラクトが数千万ドルから数億ドル規模のユーザー資産を適切に保護できるかどうかは不透明です。

事件概要
Blastのメインネット稼働からわずか1か月も経たない2024年3月21日、SSS Token(Super Sushi Samurai)を対象とした攻撃が発生しました。このトークンコントラクトには転送ロジックのバグがあり、攻撃者は特定アカウントのSSSトークン残高を無限に増加させることが可能でした。その結果、プロジェクト側は1,310 ETH以上(約460万ドル相当)の損失を被りました。
そして、SSSトークン攻撃から1週間も経たないうちに、さらに大規模な攻撃がBlast上で発生しました。Munchablesプロジェクトが攻撃され、17,413.96 ETH(約6,250万ドル)が一挙に持ち去られました。
この攻撃取引発生から30分後、プロジェクト側のコントラクト内にあった73.49 WETHも別のアドレスへと盗み出されました。
この時点で、プロジェクトのコントラクトアドレスにはまだ7,276 WETH、7,758,267 USDB、4 ETHが残っており、これらすべてが攻撃者の手に落ちる危険に晒されていました。合計で約9,700万ドルの資産がリスクにさらされていたのです。
事件直後、X(Twitter)で有名なオンチェーン探偵zachXBTは、今回の攻撃の根本原因が「ある国のハッカー」の雇用にあったことを指摘しました。
それでは、「ある国のハッカー」がどのようにしてほぼ1億ドル規模の攻撃を成し遂げたのか、詳細を見ていきましょう。

現場の再現
被害者の声明
[UTC+0] 2024年3月26日21時37分(攻撃発生から5分後)、Munchables公式がX(Twitter)上で攻撃を受けたことを公表しました。

オンチェーン探偵ZachXBTの調査によると、彼らの開発者の一人が「ある国のハッカー」だったためです。aavegotchiの創設者coderdannnもX(Twitter)で次のように述べています。「Aavegotchiの開発チームPixelcraft Studiosは2022年に、Munchablesの攻撃者を短期間雇用してゲーム開発の仕事を依頼したことがありますが、彼の技術は非常に粗末で、まさに『ある国のハッカー』そのものでした。わずか1か月で解雇しました。彼は私たちに友人を紹介しようともしましたが、その人物もおそらく同様のハッカーだったでしょう。」
今回の攻撃によりコミュニティのユーザーに甚大な損害が出たため、私たちは即座にオンチェーン調査を開始しました。では、この「ある国のハッカー」がどのような手法で攻撃を実行したのか詳しく見ていきましょう。
第一現場
[UTC+0] 2024年3月26日21時32分、17,413.96 ETHを巻き込んだ攻撃が発生しました。
Blastscanにより、この攻撃トランザクションを確認できます:
https://blastscan.io/tx/0x9a7e4d16ed15b0367b8ad677eaf1db6a2a54663610696d69e1b4aa1a08f55c95

被害を受けたコントラクト(0x29..1F)はプロキシコントラクトであり、ユーザーのステーキング資金が保管されています。攻撃者はこのステーキングコントラクトのunlock関数を呼び出し、すべての権限チェックをパスしたうえで、コントラクト内の全ETHを攻撃者アドレス1(0x6E..c5)に送金しました。

一見すると、攻撃者がwithdraw操作に似たunlock関数を呼び出して、被害コントラクト(0x29..1F)上の大部分のETHを引き出したように見えます。
プロジェクト側の財布がロックされていなかったのでしょうか?
被害コントラクト(0x29..1F)のunlock関数には2つの関連チェックがあります。一つずつ見てみましょう。
まず、権限チェックの処理内で、コントラクト(0x16..A0)のisRegisteredメソッドを呼び出し、現在のmsg.sender、すなわちハッカーのアドレス1(0x6E..c5)が登録済みかどうかを確認しています:


結果は:チェックを通過しています。

ここにはコントラクト(0x16..A0)とその最新ロジックコントラクト(0xe7..f1)が関係しています。
[UTC+0] 2024年3月24日08時39分(攻撃発生の2日前)、コントラクト(0x16..A0)のロジックコントラクトがアップグレードされました。

ロジックコントラクトアップグレード取引:
https://blastscan.io/tx/0x9c431e09a80d2942319853ccfdaae24c5de23cedfcef0022815fdae42a7e2ab6
ロジックコントラクトは0xe7..f1に更新されました。
初期のロジックコントラクトアドレスは以下で確認でき、0x9e..CDです。
https://blastscan.io/tx/0x7ad050d84c89833aa1398fb8e5d179ddfae6d48e8ce964f6d5b71484cc06d003

ここで我々は、ハッカーがプロキシコントラクトのロジック実装を変更し、0x9e..CDから悪意のある0xe7..f1へと置き換えて、権限チェックを回避したのではないかと疑いました。
本当にそうなのでしょうか?
Web3.0の世界では推測や他人の話に頼る必要はありません。技術を理解すれば、自分で答えを導き出せます。
我々は両方のコントラクト(非オープンソース)を比較したところ、初期の0x9e..CDと更新後の0xe7..f1には明確な違いがあることがわかりました:
0xe7..f1のinitialize関数の一部実装:

0x9e..CDのinitialize関数の一部実装:

攻撃者は初期のロジックコントラクト(0x9e..CD)内で、自身のアドレス(0x6e..c5)をregister状態にしており、他の2つの攻撃者アドレス0xc5..0d、0xbf..87も同様にregisterされています。また、それらのfield0は初期化時のブロックタイムに設定されており、field0の用途については後ほど説明します。
実は、我々の予想とは正反対に、真正にバックドアを含んでいたのはむしろ最初から存在していたロジックコントラクトであり、後に更新されたものは正常なものだったのです!
待ってください。このアップグレードは[UTC+0] 2024年3月24日08時39分(攻撃発生の2日前)に行われており、つまりこの事件以前にロジックコントラクトはすでにバックドアのない正常な状態に更新されていたはずです。なぜその後も攻撃者が攻撃を成功させることができたのでしょうか?
これはdelegatecallの仕組みによるものです。実際の状態変数の保存先はコントラクト(0x16..A0)内であるため、ロジックコントラクトがバックドアのない0xe7..f1に更新されても、(0x16..A0)内の変更されたストレージスロットは元に戻りません。
実際に確認してみましょう:

コントラクト(0x16.....A0)の対応するスロットには確かに値が存在しています。
これにより、攻撃者はisRegisteredメソッドのチェックを通過できたのです:

攻撃者はその後、バックドア付きコントラクトを正常なコントラクトに差し替えて目くらましを図ったのです。しかし、その時点ですでにバックドアは埋め込まれていたのです。
さらに、unlock処理にはもう一つのチェックがあります:
ロック期間のチェックです。これはロックされた資産が満期前に引き出されることを防ぐためのものです。

攻撃者は、unlockが呼び出されるときのブロック時間(block.timestamp)が、要求されているロック満了時間(field3)よりも大きくなるよう確保する必要があります。
このチェックには、被害コントラクト(0x29..1F)とそのロジックコントラクト0xf5..cdが関係しています。
[UTC+0] 2024年3月21日11時54分(攻撃発生の5日前)の取引において、
https://blastscan.io/tx/0x3d08f2fcfe51cf5758f4e9ba057c51543b0ff386ba53e0b4f267850871b88170

被害コントラクト(0x29..1F)の初期ロジックコントラクトは0x91..11であることがわかります。しかし、わずか4分後の
https://blastscan.io/tx/0xea1d9c0d8de4280b538b6fe6dbc3636602075184651dfeb837cb03f8a19ffc4f

に、0xf5..cdへとアップグレードされています。
両方のコントラクトを比較すると、攻撃者は前回と同様に、initialize関数を改ざんしていたことがわかります。
0xf5..cdのinitialize関数の一部実装:

0x91..11のinitialize関数の一部実装:

明らかに同じ手法が使われており、自身のETH保有量とロック解除時間を改ざんした上で、正常なコントラクトに差し替えて目くらましを図っています。プロジェクト側やセキュリティ研究者がデバッグを行う際には、ロジックコントラクトはすべて正常に見えるため、問題の核心を見抜くのがさらに困難になります。特にコントラクトが非オープンソースの場合、なおさらです。
ここまでで、17,413 ETHを巻き込んだこの取引がどのように実行されたのかがわかりました。しかし、この事件の背後にある情報はこれだけでしょうか?
上記の分析で、攻撃者がコントラクト内部に3つのアドレスをハードコードしていることがわかりました:
0x6e..c5(攻撃者アドレス1)
0xc5..0d(攻撃者アドレス2)
0xbf..87(攻撃者アドレス3)
しかし、前述の攻撃取引では0x6e..c5しか確認できません。他の2つのアドレスはどこで何をしたのでしょうか?また、address(0)、_dodoApproveAddress、_uniswapV3Factoryにはどのような秘密が隠されているのでしょうか?
第二現場
まずは攻撃者アドレス3(0xbf..87)を見てみましょう。同じ手法で73.49 WETHを盗み出しています:
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233
また、ガス代の出所アドレス(0x97..de)は、0xc5..0d(攻撃者アドレス2)と0xbf..87(攻撃者アドレス3)の両方に手数料を提供しています。

このガス供給元アドレス(0x97..de)の0.1 ETHは、owlto.finance(クロスチェーンブリッジ)から来ています。
0xc5..0d(攻撃者アドレス2)は手数料を受け取ったものの、直接的な攻撃は行っていません。しかし、実は裏で別の計画を担っていたのです。続けて見ていきましょう。
実は、公式が後に行ったレスキュートランザクションによると、被害コントラクト(0x29..1F)には当初73.49 WETHだけでなく、攻撃終了時にも7,276.5 WETHおよび7,758,267 USDBが残っていました。
レスキュートランザクション:
https://blastscan.io/tx/0x1969f10af9d0d8f80ee3e3c88d358a6f668a7bf4da6e598e5be7a3407dc6d5bb

本来、攻撃者はこれらの資産も盗み出すつもりでした。0xc5..0d(攻撃者アドレス2)はUSDBを盗むために用意されたアドレスです。

ここで_dodoApproveAddressは0x0000000000000000000000004300000000000000000000000000000000000003

これはUSDBのアドレスです

0xbf..87(攻撃者アドレス3)はWETHを盗むために使われました:

ここで_uniswapV3Factoryは0x0000000000000000000000004300000000000000000000000000000000000004

これはWETHのアドレスです

そして0x6e..c5(攻撃者アドレス1)はaddress(0)、すなわちネイティブ資産ETHを盗む役割を担っていました。
攻撃者はfield0を設定することで、以下のロジックを通じて対象資産を盗み出すことができました:


疑問点
なぜ攻撃者はすべての資産を盗み出さなかったのか?
理論的には、残りのWETHとUSDBもすべて盗み出すことが可能でした。
0xbf..87(攻撃者アドレス3)は73.49 WETHしか盗んでいません。実際には7,350 WETHすべてを持ち去ることも可能でした。また、0xc5..0d(攻撃者アドレス2)を使って7,758,267 USDBもすべて奪うことができました。なぜわずかなWETHだけで停止したのか、理由は不明です。これを解明するには、有名なオンチェーン探偵による内部調査が必要かもしれません。
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233

なぜ攻撃者は17,413 ETHをイーサリアムメインネットに移動しなかったのか?
ご存知の通り、Blastメインネットは中央集権的な手段でこれらのETHを阻止し、永久にそこに留めることで、実質的なユーザー損失を回避できる可能性があります。しかし、一度ETHがイーサリアムメインネットに入れば、もはや阻止することは不可能です。
我々は現在のBlastのクロスチェーンブリッジを評価しました。公式ブリッジは数量制限がないものの、14日の退出期間が必要であり、Blast公式が阻止計画を立てるのに十分な時間があります。
一方、サードパーティのブリッジはほぼリアルタイムでの到着が可能です。攻撃者のガス供給源も同様に迅速にクロスチェーンを行っています。なぜ攻撃者は即座にクロスチェーンしなかったのでしょうか?
実際、攻撃者は即座に(攻撃から2分以内に)クロスチェーンを実行していました:
https://blastscan.io/tx/0x10cf2f2b884549979a3a1dd912287256229458ef40d56df61738d6ea7d9d198f

資金は20秒でイーサリアムメインネットに到着しています。理論的には攻撃者は継続的にクロスチェーンを行い、ブリッジ側が人為的に介入する前に大量のETHを移動させることができました。

なぜ1回あたり3 ETHしか移動できなかったかというと、クロスチェーンブリッジの流動性制限があるためです。BlastからETHへの送金:

他にBlastをサポートするブリッジはさらに少ないです:

そしてこのクロスチェーン取引の後、攻撃者は他のクロスチェーン操作を続けませんでした。その理由は不明ですが、「ある国のハッカー」は資金の脱出準備を十分にしていなかったように見えます。
攻撃後の展開
コミュニティユーザーNearisbuildingの報告によると、彼は攻撃者のより多くの身元情報を突き止め、資金返還を促す努力をしました。
https://twitter.com/Nearisbuilding/status/1772812190673756548


最終的に、暗号コミュニティの注目と努力により、「ある国のハッカー」は身元暴露を恐れたのか、上記3つの攻撃者アドレスの秘密鍵をプロジェクト側に提供し、すべての資金を返還しました。プロジェクト側もレスキュートランザクションを実行し、被害コントラクトの資金をすべてマルチシグコントラクトに移管して保管しました。
TechFlow公式コミュニティへようこそ
Telegram購読グループ:https://t.me/TechFlowDaily
Twitter公式アカウント:https://x.com/TechFlowPost
Twitter英語アカウント:https://x.com/BlockFlow_News










