
Crucial MX500 SSDファームウェア更新メカニズムにおける3つの脆弱性の概念実証エクスプロイト。ATAコマンドを介したバッファオーバーフローと潜在的なコード実行を可能にします。
対象となるデバイスは、MX500シリーズSSDです。これらのSSDは、Sillicon-Motion SM2259 コントローラによって制御されています(初期のバッチでは旧コントローラの Sillicon-Motion SM2258 が搭載されていましたが、このドキュメントの主な焦点は新しい方です)。
SM2259 は、ARCアーキテクチャベースの32ビット・リトルエンディアンCPUを搭載した4チャンネルSATA 6Gb/sマイクロコントローラです。
このドキュメントの時点で適用可能な最新ファームウェアである M3CR046 を観察したところ、いくつかの問題が特定され、静的および動的の両方で確認されました。
すべての問題は、コントローラのファームウェア更新メカニズム、具体的には ATA PIO DOWNLOAD-MICROCODE (0x92) コマンドのマイクロコントローラハンドラ、特にオフセット方式でファームウェアをダウンロードするロジック(サブコマンド 0x03 および 0x0E に対応)で特定されました。
このドキュメントで扱うすべてのバグは、x86_64 CPUを搭載したPC上で、NY112 フラッシュチップを搭載し M3CR046 ファームウェアを実行する SM2259H-AC コントローラを搭載したCrucial MX500 500GB SSD(CT500MX500SSD1)で検証されました。
ファームウェアコードはベースアドレス 0x80020000 にマッピングされており、脆弱なATAハンドラはアドレス 0x80024A9C にあります。便宜上、関数の逆コンパイル版は resources/download_microcode_handler.c にあります。
技術的な詳細を扱いたくない方や、結論だけを理解したい方は、下のFAQセクションを参照してください。
M3CR046 には複数のファームウェアイメージが含まれており、ファームウェア更新メカニズムによって適切なイメージが選択されます(実際に使用されているフラッシュチップや、その他のハードウェア特性に依存する可能性があります)。このドキュメントでは、最初のファームウェアバリアントの詳細を扱います(これは、私たちの特定のドライブでサポートされているファームウェアであり、したがってそのファームウェアバリアントのみをテストできたためです)。ただし、このドキュメントで提示するバグはすべてのファームウェアバリアントに適用されるようですが、再現する際の実際の詳細にはいくつかの違いがある可能性があります。
この問題は、送信される最初のチャンクが 0x200 セクタより大きい場合に関するものです。ATAコマンドハンドラの中、特にチャンクサイズがセクタサイズより大きく、かつ送信される最初のチャンクである場合に実行されるロジックを見てみましょう:

これは、次のオフセット(この場合、これまでに単一のチャンクしか送信していないため、チャンクの長さ(セクタ単位))と、lower_bound_fw_offset という名前の変数に基づいて、いくつかの変数を設定します。lower_bound_fw_offset は、入力ダウンロードイメージ内でファームウェアイメージが見つかると期待されるブロックオフセット(つまり、セクタ粒度でのオフセット)です。これはファームウェアバリアントごとにハードコードされた値であり、私たちのケース(最初のバリアント)では 0 に等しくなります。
この場合、some_index の減算結果を計算する際にアンダーフローが発生し、some_index が 0xFFFF もの高い値になります。これは予期しない動作です。データをダウンロードバッファに移動するロジックに基づくと:

コピー元のソースアドレスは、some_index に対して計算された予期しない値を考えると、有効ではない可能性があることがわかります。
最初のチャンクが 0x200 セクタより大きいサイズでファームウェア更新要求を送信して動的にテストすると、コントローラはハングし、元の要求に対する応答すら送信しません。これは一貫しており、容易に再現できます。
これは、計算されたソースアドレスへの無効な参照が原因で発生し、その後例外がトリガーされてコントローラがハングする可能性があります。これは証明されておらず、ハングを説明するかもしれない推測に過ぎません。
入力ダウンロードイメージ(M3CR046 用)はサイズが 0x242400 バイトで、このイメージの中には3つの内部ファームウェアイメージがあり、ファームウェア更新プロセスの後に最終的にフラッシュに書き込まれるのはそのうちの1つだけです。各イメージのサイズは 0xC0C00 バイト(または 0x606 セクタ)です。
つまり、ファームウェア更新メカニズムが入力ダウンロードイメージから正しいファームウェアコピーを抽出するとき、そのサイズが 0xC0C00 バイトを超えないことを検証する必要があります。
コントローラは確かにそれを試みますが、予期しない動作につながる可能性のあるいくつかのコーナーケースがあります。次のスニペット(前のバグとコードを共有しています)を見てみましょう:

現在のチャンクが 0x200 セクタより大きく、それがシーケンス内の最初のチャンクでない場合、0x200 セクタ(0x40000 バイト)が一度にコピーされます。次に、ファームウェアイメージの合計サイズが higher_bound_fw_offset(この場合、ファームウェアサイズは正確にこれであるべきなので、0x606 セクタ)を超える場合に、コピーするバイト数から超過バイトを切り詰めることを目的としたチェックがあります。
このロジックは全体的に理にかなっていますが、欠陥があります。送信された最後のチャンクが原因で次のオフセットが高くなりすぎ、超過バイト数が 0x200 セクタ(または 0x40000 バイト)を超える場合、curr_bytes_to_copy は「負」の値になり、約 ~4GB (~0xFFFFFFFF) にアンダーフローします。前に見たように、この変数はダウンロードバッファに転送されるバイト数を決定するために使用されます。
r_maybe_some_efficient_data_transfer の中を見てみると、次のコードが表示されます:

つまり、コピーサイズは 32MB に切り詰められます(元の ~4GB のコピーサイズから)。しかし、それでも大きな数値であり、0x40000000 から始まるメモリ範囲のサイズが 32MB 未満の場合、未定義の動作を引き起こす可能性もあります。
オフセット 0x600 に到達するようにATAチャンクを送信し、その後アンダーフローをトリガーするためにサイズ 0x207 セクタの大きなチャンクを送信して動的にテストすると、おそらくコピー中の無効なメモリアクセスが原因で、コントローラは再びハングします。
このバグは前のバグよりも興味深いです。というのも、制御された上書きはできませんが(むしろ、コントローラをハングさせる例外を引き起こす可能性のある大きな上書きです)、データをダウンロードバッファに移動する関数が、クラッシュする前にその大量のデータを実際に転送できた場合(メインメモリ内のダウンロードバッファの直後にあるメモリ範囲を上書きします)、例外ハンドラの動作が上書きされたデータに基づいて変更される可能性があります。これは、例えば、例外ハンドラが上書きされた領域からポインタを読み取り、それにジャンプする場合に起こり得ます(この特定のケースは特に可能性が高いわけではありませんが、さらに研究を重ねれば、そのような何かが発見されるかもしれません)。
前述のように、ダウンロードイメージのサイズは 0x242400(または 0x1212 セクタ)です。ファームウェアは、次のオフセットが 0x1212 セクタを超えないことを検証することにより、転送されたイメージの合計サイズがこのサイズを超えないことを検証します。このチェックは理にかなっていますが、次のオフセットの計算に欠陥があります:

現在のオフセットが 0x600 セクタで、次に処理されるATAコマンドのサイズが十分に大きい場合(たとえば、ATA標準で許可されている 0xFC00 セクタ)、次のオフセットはラップアラウンドし、前述のチェックが正しく機能しなくなります:

言い換えると、通常のケースでは、ファームウェア更新メカニズムはそのステートマシンをリセットしてエラーを返すでしょうが、非常に大きなチャンクを送信した場合、その処理を継続することになります。 次のコードスニペットは、転送がどのように行われるかを示しています:

ここで、転送するセクタ数が 0x200 セクタより大きく、現在のチャンクが最初のチャンクでない場合、0x200 セクタが一度にダウンロードバッファにコピーされることを思い出してください。これは非常に興味深いです。なぜなら、ダウンロードバッファを超えて約 0x200 セクタ(または 0x40000 バイト)をコピーし、メインメモリ内のデータを上書きできることを意味するからです。たとえば、現在のオフセットが 0x605 セクタで、チャンクサイズとして 0xF9FB セクタを指定した場合、ラップアラウンドにより __next_offset は 0 になります。コピーが開始されるソースインデックスは 0 で、curr_bytes_to_copy は 0x40000 の値になります。現在オフセット 0x605 セクタにいるので、g_blocks_copied は 0x605 の値になります。現在のオフセットが確かに有効であり(次のオフセットも同様)、ダウンロードバッファへのコピー操作がトリガーされ、ダウンロードバッファの終端をわずかに超える 0x40000 バイト弱の大規模な上書きが発生します。
これは強力なプリミティブであり、はるかに多くのコントローラのバッファオーバーフローを可能にします(前のケースのようにコントローラを即座にクラッシュさせることはありません)。また、前のバグよりもはるかに高い確実性でコード実行につながる可能性があります(それでも、メインメモリ内のダウンロードバッファの後に正確に何が配置されているかについて、攻撃の特性を決定するためにさらなる調査が必要です)。
これらのバグはすべて、SG_IO インターフェースを介した標準のLinux SCSIドライバを使用して、Ubuntu 22.04 64ビットマシン上で検証されました。この特定のドライバでバグ #3 を再現するには、ヒュージページを有効にし、大きな要求のために1GBのページを1つ割り当てる必要があることに注意してください。その理由は、このドライバはATA要求全体が連続した物理メモリブロック内にあることを必要とするように見えるためです。要求のサイズは約 ~30MB に近いため、2MB ページでは不十分であり、したがってテストシステムで利用可能な次の(そして最後の)サイズは 1GB ページとなります。
ただし、これはこれをトリガーするために必要なステップであることを意味するわけではないことにも注意してください。というのも、私たちがまだ取り上げていない大きなATA要求を送信できる他の回避策があるかもしれないからです。ヒュージページを有効にすることは、単にこのバグを確認するための最速の方法でした。これ以外に、これらすべてのバグをトリガーするために必要な唯一の前提条件は、ATAパケットを送信するための必要な権限(通常は、コントローラと通信するPCへのrootアクセス)です。
前述のように、バグはUbuntu 22.04 64ビットマシンで検証されたため、コンパイルプロセスは同様のマシンで行う必要があります。他のディストリビューションやオペレーティングシステムについては保証されません。
ビルドするには、プロジェクトのルートディレクトリで次のコマンドを実行します:
cmake -B build && make
ビルドプロセスでは3つのバイナリがビルドされ、そのすべてが build ディレクトリに CVE_MX500_BUG_1、CVE_MX500_BUG_2、CVE_MX500_BUG_3 という名前で配置されます。これらはそれぞれ、バグ #1、バグ #2、バグ #3 をトリガーするソースファイルに対応しています。
各バイナリは、MX500 SSDのデバイスパスを受け取ることを想定しており、root権限で実行する必要があります。例:
sudo ./build/CVE_MX500_BUG1 /dev/sda
それは、潜在的な攻撃者の最終目標によります。彼らが望むのがドライブのストレージへの完全な読み取り/書き込みアクセスだけなら、PC内に侵入できるのであればそれで十分です。しかし、その攻撃者がさらに何歩か先へ進みたい場合はどうでしょうか? ドライブのファームウェアがデジタル署名されている場合、バグ #3 により攻撃者はファームウェアの署名検証をバイパスし、悪意のあるペイロードをドライブのファームウェアに挿入することができます。一度侵入すると、そのようなペイロードは非常にうまく隠され、ドライブのフォーマット後も生き残り、コントローラのファームウェア更新後も生き残ることさえ確実にできます。そのようなペイロードが実際に何ができるかは、このドキュメントの範囲外であるため、議論されません。
おそらく答えは大きなNOでしょう。実際にそのような攻撃を実行するために必要な研究開発の量は非常に高く、(非常に可能性が高いですが)非常に深刻な脅威アクターによってのみ可能でしょう。政府から指名手配されている場合を除いて、これが何らかの形であなたに影響を与えることは極めて考えにくいです。
ベンダーは、数か月にわたってこれらの問題に関する複数の電子メールに返答しませんでした。CVEが実際に公開されるためには、割り当てるCNAに公開リンクを提供する必要があります。残念ながら、情報を非公開で送信してもそれは機能しません。
このドキュメントで言及されているバグは、2024年5月に最初に発見されました。それ以降、Micronには(公式のセキュリティ電子メールを介して)複数回連絡しましたが、同社からの返答はありませんでした。2024年7月にMITREに通知され、2024年8月にCVEが割り当てられました。2024年8月末に、このリポジトリは公開されました(その数日後にCVEはMITREによって承認されました)。
M3CR046 より古いM3CR04Xファームウェアはもはやダウンロードできないため、影響を受けるかどうかは不明ですが、推測するならば、はいと言えるでしょう。 さらに古いバージョン、たとえば M3CR033 に関しては、静的解析に基づくと、非常に類似したバグが存在するようです。
問題のコントローラである SM2259 は、他のベンダーのSSDにも組み込まれています。ベンダーがファームウェアコードの一部を変更している可能性はありますが、これらのバグ(または非常に類似したバグ)が他のベンダーのSSDにも存在する可能性は間違いなくあると言えます。
このCVEはMITREによって公開されています。また、NVDによっても分析されており、CVSS 3.0スコアは 6.7(中)です。
説明の中の不正確さや誤りを発見した場合、またはこれらのバグを再現するのに問題がある場合は、log1kxd at gmail.com までご連絡ください。