【翻訳記事】Flashbots Auctionを用いたハッキングからの資金回収事例の紹介

2023/03/26・

noob botter

【翻訳記事】Flashbots Auctionを用いたハッキングからの資金回収事例の紹介

この記事は、特別に許可を得て、Rocket PoolのシニアSolidityエンジニアであるKane Wallmann氏コンテンツを日本語に翻訳して配信しています。イメージ画像については、著作権の都合上、翻訳元記事の画像から差し替えている場合があります。

前書き

Flashbots Auctionは素晴らしいです。イーサリアムのネイティブユーザーが、かつては事実上不可能だったことを可能にするのです(少なくとも、大規模なイーサリアムマイニングプールを複数運営していないユーザーにとってはそうでした)。MEV extraction botに注目が集まっていることを考えると、Flashbots Auctionの唯一の利点だと考えても許せますね。

しかし今週は、ハッキングされたアカウントからENSトークンの塊(当時約13000USドルの価値)を回収し、正当な所有者に戻すために使用しました。

楽しそうでしょう?その方法をご紹介します。

物語の始まり

ある日、自暴自棄になったtwitterユーザーのLittleBitPrinceは、12語のシードフレーズを公開ツイートで投稿しました。

彼は当時、ハッキングされた直後で、すべての資金とNFTを盗まれました。ハッカーが最後の残り滓まで自分のウォレットから持ち逃げするのを阻止するために、他の誰でも自分の資産にアクセスできるように、自分のシードフレーズを世界中に発信しました。

当時のツイート内容は下記でした。

LittleBitPrince
私のイーサリアムアカウントの1つがハッキングされました:
アドレスは0xcaC8E5397C09d1b1503Ab45A5fc7F8428BCf6DE5です。
私のトークンとNFTはすべてなくなりました。請求できるENSトークンは260個あり、ハッカーが目を覚ます前に手に入れることができれば、それを手に入れることができます。私のシードフレーズは次の通りです:program deny train foot scrap marble anxiet [xxxx] [xxxx] です。どういたしまして。

これらは彼の本当のシードワードやアドレスではありませんが(現在はツイートが削除されているため)、実際の投稿ではユーザーは最後の2つの単語を図のように編集しています。

当時、ハッカーは、表向き、このアカウントが多額のENSエアドロップを受け取る適格があることを知りませんでした。このアカウントに被害者がETHを送金してClaimの代金を支払おうとすると、ハッカーによって即座に狙撃されることになったようです。そして、仮にENSが何らかの形でClaimされたとしても、それらもすぐにハッカーに送られてしまう可能性が高かったようです。

*LittleBitPrinceさんのツイートが削除されたのを見て、投稿の許可を得たのですが、「大丈夫だからポストしないでね」と言われたました(ので詳しい情報はボカして配慮しつつ、公益性に重点を置いて投稿しています)。

ステップ1:シードフレーズを復元する

被害者は、何らかの理由で、自分の種明かしのフレーズの最後の2つの単語をツイートから除外しました。リカバリーする前に、まずその2つの単語が何なのかを調べなければなりませんでした。シードフレーズの仕組みをすでに知っている(あるいは気にしない)人は、このステップを読み飛ばしてください。

最後の2つの単語を取り除くと、解読するのが信じられないほど難しい課題に見えるかもしれません。何しろ、英語には50万語以上の単語があるのですから。

でも実は、そうでもないんです。シードフレーズの仕組みを簡単に説明しましょう。

暗号資産の世界で広く使われているシードフレーズは、BIP-39というビットコインの改善提案に由来しています。2048個の単語が特定の順序で並んでいるセットが既にあります(検索の効率化のためにアルファベット表記になっています)。

各ワードは11ビットのデータを表します。単語リストへの単語のインデックスはビットに変換され、これらのビット列のそれぞれが連結されます。例えば、”program “という単語は、リストの1375番目の単語です(0インデックス)。2進数で1375は10101011111です。つまり、これが最初の11ビットになります。

これ以降の単語に同じ処理を適用すると、132ビットのデータができあがります。132ビットのデータのうち、最初の128ビットはエントロピーに使われ、最後の4ビットはチェックサムとして使われます。

この128ビットのエントロピーから秘密鍵に至るまでには、さらにいくつかのステップがあるのですが、この話とは関係ないので詳細は省略します。もっと詳しく知りたい方はBIP-44を読んでみてください。

つまり、最後の2つの単語を削除しても、失われるエントロピーはわずか18ビットということです。

つまり、ブルートフォースアタックでは、262,144通りの組み合わせを試すだけでいいのです。これは、最新のハードウェアであれば容易なことです。

翻訳者注
ブルートフォースアタックとは、攻撃者がシステムやアカウントに不正にアクセスするために使用する試行錯誤の手法のことです。パスワードや暗号化キーのすべての可能な組み合わせを、正しいものが見つかるまで体系的に試行錯誤するものです。このタイプの攻撃は、時間とリソースを必要としますが、ターゲットが短いパスワードや単純なパスワードなど、セキュリティ対策が脆弱な場合に成功することがあります。

シードフレーズを復元する最も効率的な方法は、これらの組み合わせをすべてループしてチェックサムを計算し、秘密鍵を導き出し、結果のイーサリアムアドレスを比較することです。しかし、当時の私には競合となる人間がたくさん居て、時間との戦いであったため、これをスクリプトに符号化するのに30分以上かかるかもしれないので、少し効率の悪い方法を取ることにし、私がすでに自由に使えるツールを使って、ほんのわずかな時間で以下のスクリプトを以下にまとめることにしました。

まとめたもの
const HDWallet = require(‘ethereum-hdwallet’)
const bip39 = require(‘bip39’);const fs = require(‘fs’)const seedStart = ‘program deny train foot scrap marble anxiety oblige hybrid clean’const words = fs.readFileSync(‘./words.txt’).toString(‘utf-8’).split(‘\r\n’)for(let x = 0; x < words.length; x++){
for(let y = 0; y < words.length; y++){
const mnemonic = seedStart + ‘ ‘ + words[x] + ‘ ‘ + words[y]
const valid = bip39.validateMnemonic(mnemonic)if(valid){
const wallet = HDWallet.fromMnemonic(mnemonic)
const address = wallet.derive(`m/44’/60’/0’/0/0`).getAddress().toString(‘hex’)

if(address.toLowerCase() === ‘caC8E5397C09d1b1503Ab45A5fc7F8428BCf6DE5’.toLowerCase()) {
console.log(mnemonic)
process.exit()
}
}
}
console.log(x + ‘ of ‘ + words.length)
}

1つのシードフレーズは多くのキーペアを生成することができ、私は、アカウントが最初のパス(すなわち`m/44’/60’/0’/0/0`)に由来すると時間節約の仮定をしました。幸い、その仮定は正しかったです。そうでなければ、シードフレーズの解明にもっと時間がかかっていたでしょう。

このスクリプトは、チェックサムビットもブルートフォースしているようなものなので、上記の理想的な解決策よりも時間がかかります。しかし、既存のライブラリのおかげで、私はこのスクリプトをわずか5分で組み立てることができました。ブラックハットが競っていたかもしれないので、ここでは1分1秒が大切です。この方法で4,194,304通りの組み合わせをテストしても比較的早く(5分以内)、このナイーブなブルートフォーサーなコードを書くために節約した時間は、効率の低下を簡単に補うことができました。

ステップ2:資金を回収する

シードフレーズと秘密鍵を手に、私は被害者のENSトークンの回収に乗り出しました。当時は、ハッカーがこのアドレスに送信されたETHを盗み見るボットを持っているかどうか、はっきりわかりませんでした。

しかし、これはかなり一般的な設定であり、ツイートから、そうでなければ被害者はアカウントを手放さなかっただろうと推測しました。つまり、アトミックにETHを送り、ENSを請求し、ENSを私がコントロールするアカウントに戻す必要があったのです。

ここで、Flashbots Auctionsの登場です。

Flashbots Auctionsは、ユーザーがトランザクションのバンドルをマイナーに個人的に提出するもので、これらのトランザクションがパブリックのmempoolには決して現れず、フロントランナーやサンドイッチボットに分析されることもなく、提供された順番にまとめられることが保証されています。

これらの特性は、さまざまなユースケース(MEV extractionはその代表的なもの)で非常に有用ですが、この状況では、ハッカーのボットを回避するためにどのように使用できるか、おわかりいただけると思います。

Flashbotsは、バンドルの準備と送信に必要なほとんどの作業を処理するjavascriptライブラリを提供しています。彼らが提供するサンプルコードは、優れた出発点であり、まさに私がここでバンドル発射を可能な限り迅速に行うために使用したものです。

このサンプルコードにおいて、設定値を入力する以外に、私たちが修正する必要がある部分は以下です。

サンプルコード
const signedTransactions = await flashbotsProvider.signBundle([
{
signer: wallet,
transaction: legacyTransaction
},
{
signer: wallet,
transaction: eip1559Transaction
}
])

`signBundle`は、提出したいトランザクションを(署名者とともに)マイニングしてほしい順番で含む配列を受け取ります。このリカバリーが機能するためには、3つのトランザクションを実行する必要がありました。

  1. ハッキングしたアカウントに、次の2つのステップを実行するのに十分なETHを入金
  2. ENSトークンのエアドロップをClaim
  3. ENSを自分のコントロール下にあるアカウントに送金

最初のものは簡単で、以下のオブジェクトだけで、説明できるはずです。

説明
{
from: myAddress,
to: hackedAddress,
gasPrice: GAS_PRICE,
gasLimit: 21000,
chainId: CHAIN_ID,
value: ethers.utils.parseEther(‘0.06’),
nonce: myNonce++
}

ENSのエアドロップがどのように機能するのか実はよくわからなかったので、次は少し厄介なことになりそうでした。

スマートコントラクトを調べて時間を浪費したくはなかったのです。なので、ハッキングしたアカウントをMetaMaskにインポートし、ENSのクレームウェブサイトでClaimプロセスを行いました。MetaMaskの確認ウィンドウが表示される最後のステップまでずっとプロセスに従い、そこから以下の入力データをコピーしました。

このデータを使って、2つ目のトランザクションオブジェクトを用意しました(データは切り捨て)。

トランザクションオブジェクト
{
from: hackedAddress,
to: ‘0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72’,
gasPrice: GAS_PRICE,
gasLimit: 150897,
chainId: CHAIN_ID,
nonce: hackedNonce++,
data: ‘0x761229030000000000000000…’
}

0xC1836…はENSトークンとairdrop claimのコントラクトアドレスです。最後の取引は比較的簡単で、上記と同様のプロセスでトークン転送を行いました。

コード
{
from: hackedAddress,
to: ‘0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72’,
gasPrice: GAS_PRICE,
gasLimit: 100000,
chainId: CHAIN_ID,
nonce: hackedNonce++,
data: ‘0xa9059cbb0000000000000000…’
}

ガス代は、現在の市場価格よりも高い値を選びました。マイナーにとってバンドルが受け入れられるだけの価値があればよく、同じMEVの機会を得るために他のボットと競争する必要はないからです。

この条件であれば、多額の賄賂となるガス代は必要ないでしょう。

バンドルが準備できたので、次はシミュレーションを行います。これは、トランザクションのどれかが失敗するかどうかを知ることができ、この時点まで準備したものがすべて正しいかどうかを確認するための良いサニティチェックとなります。

Flashbotsが提供するサンプルコードでは、以下のようなことを行っています。

サンプルコード
const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlock)
// Using TypeScript discrimination
if (‘error’ in simulation) {
console.warn(`Simulation Error: ${simulation.error.message}`)
process.exit(1)
} else {
console.log(`Simulation Success: ${JSON.stringify(simulation, null, 2)}`)
}

シミュレーションが失敗した場合、その理由と終了について有益な情報を提供してくれます。

私の場合、すべてうまくいったので、バンドルに発射して、指をくわえて見ていました。この時、手のひらに汗をかきながら、端末の出力をじっと見て、良い結果が出るかどうかを確認します。数ブロックの間に、私のバンドルは含まれており、私のアカウントに260ENSがありました。成功です!

結果的に上記のツイートを発見してからアカウントにENSが入るまで、30分弱かかりました。

ステップ3:資金の返却

自分の口座に安全に資金が入ったので、LittleBitPrinceに良い知らせを連絡する時が来ました。彼の話にはいくつか奇妙な点があり、また他の情報も見つけたので詳細は省きますが、要するに、このENSトークンが彼のものであることを、返す前に絶対に確かめたかったのです。

もしかしたら、彼はそのシードワードをどこからか手に入れて、本当の持ち主ではなく、誰かがそれを返してくれることを望んでいたかもしれません。このような場合、用心に越したことはありません。

私はツイッターのDMで彼に連絡を取りました。また、ハッキングが行われるずっと前に、このアカウントがやりとりしていたアドレスもいくつか探し出しました。重要なのは、もしハッカーがこのアカウントを支配していたなら、今頃は確実に資産を使い果たしているだろうと思われるほどの資産が含まれていたことです。私はLittleBitPrinceに、このアカウントで提供したメッセージに署名するよう依頼しました。すべてが確認された後、私はENSトークンを彼に送金し、その結果、彼は大喜びしたのです。

読んでくれてありがとうございます。よかったらtwitterをフォローしてください。

以上で翻訳は終了です。いかがでしたでしょうか?

FlashBotsが用意するホワイトハット資産救出フォーム

上記の解決策について、FlashBotsのチームに問い合わせたところ、上記を自身でできない人向けに資産救出フォームを用意しているようです。

フォームによると、下記を条件としているようです。(必ずフォームをご自身で読んでください。Webサイトやフォームでシードフレーズを要求することはありません。)

  • ガス代と技術料が救助可能な資産の価値を下回るため、1000ドル以下の救助には協力できません。
  • 救出のためのマイナーへの支払いに必要なガス代をご負担くださいますようお願いいたします。(TX手数料)
  • 救出が成功した場合、複雑さとリスクのある資金に応じて、救出した額の5~10%の手数料が発生します。
  • 私たちは、取得した秘密鍵(他人のウォレット)から資産を取り戻そうとする悪意のあるユーザーを支援することはありません。
  • ハッカーが見逃した資金や時間差でロックされた資金のみ救出することができます。
  • このフォームの全てをしっかりと読み込むこと。

詳細については、FlashBotsのDiscordに入っていただきご自身でお問い合わせください。

ニュース/解説記事

Enable Notifications OK No thanks