Cisco Japan Blog

ReVault 攻撃に要注意。SoC が敵に転じるとき:詳細解説版

2 min read



この調査の概要については、『注目の脆弱性』をご覧ください。今回の投稿は技術的な内容に踏み込んだ詳細解説版です。調査プロセス全体を示すとともに、攻撃シナリオの背後にあるエクスプロイトについて技術的に解説します。

Dell ControlVaultpopup_icon は、「パスワード、生体認証テンプレート、セキュリティコードをファームウェア内に保存するセキュアバンクを提供する、ハードウェアベースのセキュリティソリューション」です。この機能はドーターボードとして実装されており、同ボード上でこれらのセキュリティ機能がファームウェア内で実行されます。Dell がこのドーターボードを Unified Security Hub(USH)と呼ぶのは、指紋リーダー、スマートカードリーダー、NFC リーダーなどの各種セキュリティ周辺機器を接続し、ControlVault(CV)を実行するためのハブとして利用されているためです。

ControlVault3 を調査対象にした理由

結果論ではありますが、振り返ってみると、ControlVault3 には調査対象とすべき正当な理由が次のようにいくつも存在します。

  • このデバイスについて公開されている調査結果がない。
  • セキュリティとログイン強化が目的のため、機密性の高い機能に使用される。
  • 非常に多くの Dell 製ラップトップに搭載されており、特にセキュリティレベルの強化を求める組織(金融、医療、政府系機関など)で導入されている可能性が高い。

しかし、この調査プロジェクトの本当のきっかけは、対象として「有望」と思われる特徴を見つけたことです。最初に目を引いたのは、ControlVault3 に関係する Windows サービスの大半が ASLR(アドレス空間配置のランダム化)に対応していないことでした。つまり、エクスプロイトが比較的容易で、コードベースに技術的負債が存在する可能性があります。さらに、セットアップバンドルには複数のドライバと、プレーンテキストと暗号化されたファームウェアが混在しているように見えるものが含まれていることから、さらなる調査を必要とする興味深い課題となっています。

計画立案

脆弱性調査プロジェクトを開始する際には、達成すべき内容についてある程度アイデアを持っていることが重要です。そこで、プロジェクトを進めるための指針となる計画を立てることにします。

  1. メインアプリケーションは暗号化されており、調査の目的はこのファームウェアに何が隠されているのかを確認することである。最初の課題の 1 つは、アプリケーション ファームウェアの復号方法を見つけることである。
  2. 今回は脆弱性調査プロジェクトなので、ControlVault とやり取りする方法とその攻撃対象領域を把握し、脆弱性を見つける必要がある。
  3. Windows サービスは ASLR なしで実行され、SYSTEM 権限を持っている。これらは、ローカル権限昇格(EoP)の単独の標的になり得るだけでなく、興味深いエクスプロイト経路を持つ可能性がある。

情報収集

情報収集はプロジェクト全体を通して行いましたが、ここではわかりやすくするために、初期の調査結果の一部を要約します。

ControlVault は Broadcom 製で、同社の 5820X チップシリーズpopup_iconを採用しています。厳密に言えば、今回は ControlVault3(または ControlVault3+)のみを対象としましたが、別のハードウェアを使用する ControlVault2 および ControlVault(1 は付いていませんが、おそらく初代にあたるもの)も存在していました。ControlVault に関する最初の言及は 2009 年から 2011 年に遡ります。

BCM5820X チップシリーズに関するオンライン調査ではほとんど成果が得られず、唯一注目に値する発見は NIST 認証popup_iconでした。この文書はチップのセキュリティ態勢を明確に示し、暗号モジュールの動作に関するインサイトをある程度提供しています。

その他の有用なリソースとしては、パワーユーザーが ControlVault について語るフォーラム投稿があります。特に、Linux での動作に関する議論では、ある投稿をきっかけに、公式の Linux サポート(ただし限定的)を提供するリポジトリpopup_iconが見つかりました。注目すべきは、このリポジトリ内の共有オブジェクトの 1 つである「libfprint-2-tod-1-broadcom.so」にデバッグシンボルが付属している点です。これは、ControlVault エコシステムをリバースエンジニアリングする際に役立ちます。

最後に、ラップトップに接続して ControlVault ファームウェアを実行する USH ボードの画像を以下に示します。

図 1:ControlVault を実行する USH ボードの写真

ラップトップ内部に接続すると、次のようになります(ボードが見えるようにバッテリを取り外しています)。

図 2:Dell Latitude ラップトップ内部の USH ボード(オレンジ色の線で囲まれた部分)

ControlVault3 バンドル内の興味深いファイル

ControlVault には多くのファイルが付属しています。すべてを一度に調査することはできませんが、特に注目すべきは「bin」フォルダと「firmware」フォルダです。前者には、ControlVault との通信に使用される主要なサービスおよび関連する共有オブジェクトが含まれており、後者はデバイスにデータをプッシュするために使用されます。

3ControlVault3 インストーラの bin フォルダと firmware フォルダ

firmware フォルダは特に興味深く、ControlVault デバイス上で実行されていると推測されるコードが含まれています。「strings」コマンドを実行するか 16 進エディタで開いてこれらのファイルの内容を確認すると、名前に「SBI」が含まれるファイルはプレーンテキストで、「bcmCitadelXXX」という名のファイルは圧縮または暗号化されているようです。すでに収集済みの情報から、「SBI」は「Secure Boot Image」の略で、デバイスのブートプロセスの初期段階の一部であると判明しています。そのため、「bcmCitadelXXX」ファイルは、SBI によって起動されるメインのアプリケーション ファームウェアであると推測できます。

ブートローダーのリバースエンジニアリング

SBI ファイルはプレーンテキストであり、Broadcom のドキュメントから ARM コードであることが判明しているため、お気に入りの逆アセンブラ/逆コンパイラでそれらのいずれかを調べれば、アプリケーション ファームウェア自体の処理方法がわかる可能性があります。

SBI ロードアドレスの特定

通常は、まずこのデータ BLOB のロードアドレスを特定します。今回のケースでは 0x2400CC00 です。データは 0x400 バイトのヘッダーで始まるため、コードの実際の開始位置は、ベースアドレス 0x2400D000 が妥当と考えられます。

この値を見つけるには、まず任意のアドレスにコードをロードし、次に絶対アドレス(文字列のポインタ、関数のアドレスなど)を探し、ファームウェアをリベースしながらすべてが一致するまで推測を繰り返します。SBI ファームウェアには多くの文字列が含まれているため、文字列が正しく参照されているときには、見つけるのは比較的簡単です。また、関数ポインタも便利です。都合のよいことに、関数ポインタの一部は、ARM のベクターテーブルが配置されているコードの先頭付近にあります。これにより、ロードアドレスが判明します。

図 4:SBI 内のベクターテーブルとコードの先頭

ソフトウェアアーキテクチャの確認

ここで、最初にどこに着目するかを決める必要があります。SBI の全体的なアーキテクチャを把握してその仕組みを理解するか、本来の目的を見失わずにアプリケーション ファームウェアの復号方法を見つけるかのいずれかです。実際には後者を選択しましたが、この後の説明がわかりやすくなるように少しだけネタばらしをします。

関数とパラメータの名前

このファームウェアはログにかなり依存しているため、関数名、変数、コード自体のロジックの詳細が漏洩する可能性があります。

このファームウェアは、Citadel RTOS というカスタムのリアルタイム オペレーティング システム(RTOS)を実行しているようです。また、CitadelRTOS のベースとして使用されたと思われる OpenRTOS を参照するデバッグ文字列も見つかりました。

さらに前述のように、Linux 実装にはホスト API のデバッグシンボルが付属しており、ControlVault で使用されるデータ構造と列挙値が数多く提供されます。

ファームウェアとの通信

SBI のリバースエンジニアリングについて詳しく説明する前に、ホスト(Windows)とファームウェアとの間で行われる通信について概要を示します。

基本的に、USH ボードはラップトップのマザーボードに接続され、デバイスマネージャでは USB デバイスとして表示されます。「cvusbdrv.sys」というドライバが、ユーザーランドから開くことができるデバイスファイルを作成します。デバイスの管理と通信には、以下に示す各種 DeviceIoControl コマンドを使用できます。

ドライバとの通信は、ユーザーランド API を使用すると容易です。特に「bcmbipdll.dll」ファイルは 160 以上の高レベル関数を実装しており、ファームウェアに特定のコマンドを送信できます。これらの関数には「cv_」というプレフィックスが付き(「cv_open」、「cv_close」、「cv_create_object」など)、「CV コマンド」と呼ばれています。これらのコマンドのいずれかが呼び出されると、背後で IOCTL_SUBMIT/IOCTL_RESULT が実行され、関連データが USB 経由でファームウェアに送信されます。

ファームウェアは、USB エンドポイントからデータを受信すると、データパケットを処理し、専用のコードパスにルーティングします。CV コマンドの場合、データは「CvManager/CvManager_SBI」という関数に渡され、この関数がコマンドを別の関数にディスパッチし、ディスパッチ先の関数がコマンドを実装します。

例:ControlVault との手動通信

簡単な Python スクリプトを使用して「bcmbipdll.dll」をロードし、その関数を呼び出すことができます。

たとえば次のコードは、ファームウェアのバージョン文字列を取得します。

図 5:CV のバージョン文字列を取得する Python スニペット

戻り値は次のとおりです。

図 6:cv_get_ush_ver から取得されたバージョン文字列

なお、ホスト API の Linux 実装(libfprint-2-tod1-broadcom/usr/lib/x86_64-linux-gnu/libfprint-2/tod-1/ libfprint-2-tod-1-broadcom.so)にはデバッグシンボルが付属しており、各 CV コマンドの呼び出しに関係するさまざまな構造体とパラメータを識別するために使用できます。

通信メカニズムについては「SYSTEM サービスのエクスプロイト」セクションで再度説明します。ここでは、アプリケーション ファームウェアの復号方法を見つけるという当初の目標に戻ります。

ファームウェアの復号メカニズムの確認

SBI 内の文字列を検索して、復号に関する言及の有無を確認します。

図 7:SBI ファームウェアの復号に関する文字列

上記のスクリーンショットに見られるように、USH_UPGRADE 機能には復号の失敗についての言及があります。実際、この機能はアプリケーション ファームウェアの復号に関連しています。USH_UPGRADE 機能は、次の 3 つの CV コマンドによって実装されています。

  • CV_CMD_FW_UPGRADE_START
  • CV_CMD_FW_UPGRADE_UPDATE
  • CV_CMD_FW_UPGRADE_COMPLETE

これらのコマンドは、「bcmbipdll.dll」の「cv_firmware_upgrade」関数によって実行されます。

このファームウェアの更新プロセスはやや複雑です。

  1. ホストはまず、「0123456789abcdef」を何度も繰り返しているだけの「bcm_cv_clear_scd.otp」というファイルをフラッシュメモリに書き込みます。これには、「cv_flash_update」関数を使用します。
  2. ホストが「cv_reboot_to_sbi」を呼び出して SBI モードで再起動します。
  3. ホストが CV_CMD_FW_UPGRADE_START コマンドを送信します。このコマンドは「ushFieldUpgradeStart」によって SBI で処理されます。
    1. SBI は、キーマテリアル(復号キー、IV、RSA 公開キーなど)を含むセキュアコード記述子(SCD)と呼ばれるものをフラッシュメモリからロードしようとしますが、SCD がない場合はハードコードされたデフォルト設定に戻ります。これはステップ 1 でフラッシュメモリに書き込んだ(消去した)ものです。

図 8:ushFieldUpgradeStart 中にハードコードされたデフォルトを使用

図:9 復号関数の呼び出し

    1. ホストは暗号化されたアプリケーション ファームウェアの最初の 0x2B0 バイトを送信します。この部分は、インストール予定のファームウェアのパラメータを定義する暗号化済みヘッダーです。
    2. SBI は、SCD のキーマテリアルまたはハードコードされたデフォルトを使用して、ヘッダーの復号(AES-CBC)、検証、暗号による検証を試みます。
    3. 検証に合格すると、SBI は新しいキーマテリアルを生成して SCD の別のセクションに保存し、このキーマテリアルを使用してファームウェアを暗号化形式で保存します。これは、ControlVault が使用する SoC がセキュア メモリ アクセス ユニット(SMAU)を備えており、それによって暗号化されたコードをメモリから直接実行(XIP)できるためです。
  1. 次に、ホストはファームウェアの残りを 0x100 バイトのチャンクに分割し、CV_CMD_FW_UPGRADE_UPDATE コマンドで送信します。このコマンドは「ushFieldUpgradeUpdate」関数によって SBI で処理されます。
    1. ファームウェアチャンクは同じ方法で復号されますが、コードはデフォルトの IV を使用する代わりに、SMAU デバイスのカスタム関数を使用して、復号するメモリブロックのアドレスに基づいて IV を生成します。
      注:このアプリケーション ファームウェアのベースアドレスは、逆から推測すると 0x63030000 です。

図 10:アドレスベースの IV の計算

    1. 復号されたブロックのローリングハッシュ(SHA256)が、今後の検証のために保存されます。
  1. 暗号化されたファームウェアの送信が完了すると、ホストは CV_CMD_FW_UPGRADE_COMPLETE コマンドを送信します。このコマンドは「ushFieldUpgradeComplete」関数によって SBI で処理されます。
    1. SBI は、検証済みのヘッダーと、ファームウェアページの復号中に計算されたローリングハッシュに基づいて、受信したファームウェアのシグネチャを検証します。
    2. 検証に合格すると、新しい SCD は、チップ OTP ヒューズに保存されているデバイスごとの AES キーを使用して暗号化され、フラッシュに書き込まれます。

幸い、「bcmsbiCitadelA0_1.otp」ファイルにハードコードされたキーは、アプリケーション ファームウェアの暗号化に使用されたものであり、上記のアルゴリズムを再実装すればアプリケーション ファームウェアを正常に復号できるため、第 2 の目的である脆弱性の探索に進むことができます。

攻撃対象領域のマッピングと脆弱性調査

復号したばかりのファームウェアイメージがあると、つい先走ってすべてのリバースエンジニアリングを始めたくなるところですが、難しい部分に着手する前に、いったん立ち止まって戦略を練るべきです。そこで、システムアーキテクチャと注目すべき領域を見ていくことにします。

図 11:システムアーキテクチャ

次のように、いくつかの角度から考えることができます。

  • ホストから悪意のあるデータを送信して、アプリケーション ファームウェアまたは SBI コードを破壊することはできるか。
  • ファームウェア自体を改ざんして誤動作させることはできるか。
  • 悪意のあるファームウェアイメージがホストを侵害する可能性はあるか。
  • ハードウェア周辺機器についてはどうか。侵害されたり、ファームウェアの侵害に使用されたりする可能性はあるか。

調査では、最初の 3 つの問いについて調べました。4 つ目は今後の調査プロジェクトの対象になる可能性があります。最初の問いに答えることができれば次の 2 つの問いも解決できるため、まずは最初から始めます。

アプリケーション ファームウェアの脆弱性の発見

アプリケーション ファームウェアは 150 を超える CV コマンドを受け入れます。これは大規模な攻撃対象領域であり、確認すべき点が数多くあります。これらのコマンドのほとんどは、「cv_open」コマンドによって「セッション」が確立済みであることを前提としています。やり取りが終了すると、「cv_close」関数によってセッションが終了します。これら 2 つの動作について説明します。

cv_open and cv_close

「cv_open」のプロトタイプは次のとおりです。

実装は以下のとおりです。

図 12:cv_open の呼び出し

メモリが割り当てられ(29 行目)、その後、セッションオブジェクトの最初の 4 バイトとしてタグ「SeSs」が書き込まれる(36 行目)ことがわかります。さらに処理が行われた後、セッションへのポインタがハンドルとしてホストに返されます(44 行目)。ポインタをハンドルとして使用するという選択は、ヒープアドレスをホストに漏らすため、すでにやや疑問ではありますが、ここでは次に進みます。

「cv_close」のプロトタイプは次のとおりです。

この関数は、「cv_open」から取得したセッションへのポインタを受け取り、次の処理を実行してセッションを閉じようとします。

  1. セッションを検証する(下記を参照)。
  2. 「SeSs」タグを消去する。
  3. メモリを解放する。

 

図 13:cv_close の実装

一方、「validate_session」関数は次の処理を実行します。

  1. 提供されたポインタが CV_HEAP 内にあることを確認する。
  2. 最初の 4 バイトが「SeSs」タグと一致することを確認する。
  3. 追加のチェックを行う(今回の調査とは無関係)。

図 14:セッションの検証

この処理が特に興味深いのは、ヒープ上に任意のデータを配置できれば、セッションを偽造して解放することが容易になり、その過程でヒープメモリが破壊される可能性があるためです。この問題は CVE-2025-25215popup_icon として報告されています。

予想したとおり、「cv_create_object」や「cv_set_object」などの関数を使用すれば、攻撃者の制御下にあるデータを実際にヒープ上に配置できます。ただし、「cv_create_object」が返すハンドルはヒープアドレスではなくランダムなため、これらのデータを見つけるのはやや面倒です。とはいえ、答えを導いてくれる「セッションオラクル」を作成すれば、本物のセッションと偽造されたセッションの両方を見つけることができます。そのためには、セッションハンドルを必要とするもののセッションが無効な場合に固有のエラーコードを返す多くの CV 関数のいずれかを利用します。たとえば「cv_get_random」を次のように使用できます。

図 15:cv_get_random の実装

セッションの「validate_session」チェックが不合格の場合、「cv_get_random」は CV_INVALID_HANDLE を返します。合格の場合は、CV_SUCCESS または CV_INVALID_OUTPUT_PARAMETER_LENGTH を返します。これにより、副次的影響を伴うことなく、有効と思われるセッションを識別できます。

アプリケーション ファームウェアのデバッグ文字列によると、使用されているヒープ実装は OpenRTOS の「heap_4.c」です。この時点では、標準的なヒープエクスプロイト手法でメモリの破壊を試みることもできましたが、より簡単にエクスプロイトできそうな脆弱性を探すことにしました。

securebio_identify

この関数は、名前に「cv」が付かない数少ない関数の 1 つですが、「cv_identify_feature_set_authenticated」経由で呼び出されます。これは、Windows Hello が指紋認証処理中に使用する WinBio フレームワークの実装の一部です。

オブジェクトへのハンドルを必要とするこの関数は、ハンドル取得後にその内容の一部をコピーします。

図 16:オブジェクトを取得してその内容をコピーする「securebio_identify」

データはオブジェクトのいずれかのプロパティからスタックバッファ「data2」にコピーされます。「memcpy」は、データをコピーするためにプロパティのサイズを使用します。その際、プロパティのサイズが「data2」バッファに収まると想定していますが、この想定は危険な場合があります。このプロパティが予想よりも大きい場合、スタックオーバーフローが発生する可能性があるためです。

実際には、「cv_create_object」で割り当てられたオブジェクトは、このプロパティのサイズを制限するためにチェックが行われるため、この方法では使用できません。しかし、ヒープデータを破損させることは可能なため、バグをトリガーする悪意のあるオブジェクトを偽造することができます。あるいは、他の正当な方法で悪意のあるオブジェクトをロードできる可能性もあります。たとえば「cv_import_object」が良い候補です。この関数は複雑なので、今回はヒープ破損のアプローチに焦点を当てましたが、このバグは CVE-2025-24922popup_icon として報告されています。

「securebio_identify」をエクスプロイトする一般的なアプローチは次のとおりです。

  1. 偽のヒープメタデータとそれに続く「SeSs」タグを含む大きなオブジェクトをヒープ上に作成します。
  2. 偽のセッションを見つけ、「cv_close」を使用して解放します。これにより、作成した大きなオブジェクトによってまだ使用されているにもかかわらず、ヒープメモリのチャンクが解放済みとしてマークされます。
  3. より小さなオブジェクトを割り当てます。このオブジェクトは、ステップ 1 の大きなオブジェクトの中に作った穴に割り当てられることになります。
  4. 「cv_set_object」を使用して大きなオブジェクトのデータを変更し、小さなオブジェクトのフィールドを破損させます。
  5. 破損した小さなオブジェクトを使用して、「securebio_identify」内部でスタックオーバーフローをトリガーします。このファームウェアには ASLR がないためガジェットを見つけるのが容易で、それを利用してこの関数をフルにエクスプロイトし、ファームウェア内で任意のコードを実行できます。
  6. オプション:大きなオブジェクトを出力バッファとして使用し、エクスプロイトによって生成されたデータを保存して、その内容をホストから取得します。

172 つのオブジェクトを重ね合わせて securebio_identify をエクスプロイト

この攻撃の例は次のセクションで使用します。

ファームウェアのその他の脆弱性

アプリケーション ファームウェアを調査したところ、「cv_send_blockdata」には境界外読み取りが、「cv_upgrade_sensor_firmware」には境界外書き込みが見つかりました。これらはそれぞれ CVE-2025-24311popup_iconCVE-2025-25050popup_icon として報告されています。これらの脆弱性については、さらなるエクスプロイトには使用しませんでした。

アプリケーション ファームウェアにおける任意のコードの実行:次のステップ

ここで目標リストに立ち戻ります。ファームウェア内でコード実行が可能になったので、この地点からホストへの攻撃を試みることができます。より強力で有意義な攻撃を行うには、まずファームウェアを恒久的に改ざんする方法を見つける方が面白いと思われます。では、やってみましょう。

図 18:セキュアブートプロセス

この図は、ControlVault のブートプロセスを示しています。

  1. BootROM が SBI を検証します。
  2. SBI が OTP メモリからキーを取得して SCD を復号します。
  3. 復号された SCD から SBI が必要なキーマテリアルをロードし、暗号化されたファームウェアをその場で実行するように SMAU を設定します。
  4. アプリケーション ファームウェアが実行されます。

驚くべきことに、ブート時にはアプリケーション ファームウェアの暗号による検証は行われません。暗号による検証はファームウェアの更新プロセス中にのみ行われます。アプリケーション ファームウェアのセキュリティは、主に OTP キーと SCD に保存されているキーマテリアルのセキュリティに依存しています。しかし、ファームウェア上でコードを実行できるようになったため、このキーマテリアルを漏洩させることが可能ではないでしょうか。

sotp_read_key

「sotp_read_key」は、Broadcom チップの OTP メモリからキーマテリアルを読み取るための内部関数です(つまり CV 関数ではありません)。具体的には、SCD の暗号化と認証に使用される AES キーと HMAC キーを取得できます。

19OTP キーのダンプのデモ

デバイスの OTP キーを取得すると、SCD BLOB の復号や新しい SCD BLOB の偽造が可能になります。この点が特に興味深いのは、「cv_flash_update」関数を使用して任意の SCD BLOB をフラッシュメモリに書き込めるからです。

RSA の公開キーと秘密キーのペアを独自に作成し、SCD の公開キーを先ほど作成したものと置き換えることができます。ファームウェアの更新時には、新しい RSA 公開キーがファームウェア検証に使用されます。このようにすると、ファームウェアファイルを改ざんしてデバイスにインストールできるようになります。

このプロセスが機能することを確認するために、ファームウェアを改ざんして、Windows から USB 記述子が要求されたときに任意のメッセージを送信するようにします。

図 20:改ざんされた ControlVault が返した悪意のある USB 記述子

ファームウェアの改ざん

「cv_fingerprint_identify」のパッチ

ファームウェアの改ざんが可能になると、新たな攻撃ベクトルが生まれ、特定の関数の動作を変更できるようになります。具体的に説明すると、「cv_fingerprint_identify」は、ユーザーが指紋認証でログインしようとする際に Windows Hello によって使用されます。ホストは、CV に保存されている指紋テンプレートのいずれかが、現在リーダーに接触している指紋と一致するかどうかを確認するために、ハンドルのリストを送信します。指紋テンプレートをホスト自体に保存することはプライバシー上の懸念につながりかねないため、センサーにおけるこの疑似的な照合はこれを避けるために行われます。これは興味深い可能性を生み出します。「cv_fingerprint_identify」が常に true を返し、Windows Hello があらゆる指紋を受け入れるようにしたらどうなるでしょうか。

21Windows Hello のバイパスのデモ

SYSTEM サービスのエクスプロイト

デバイス上で実行されているファームウェアを改ざんできるようになったため、悪意のある ControlVault デバイスがホストを侵害できるかどうかについても、調査が可能になりました。

ホストとファームウェア間の通信の概要

CV コマンド、たとえば「cv_get_random」を呼び出すと何が起こるかを考えてみましょう。

図 22:cv_get_random の呼び出し

  1. 「InitParam_List」関数は、2 つの別々のオブジェクト配列「out_param_list_entry」と「in_param_list_entry」にデータを入力するために呼び出されます。前者はファームウェアに渡す引数を指定するために、後者はコマンドから期待される戻り値を格納するために使用されます。
    1. 「InitParam_List」の最初のパラメータはデータのカプセル化のタイプです。

    1. カプセル化のタイプにより、パラメータのカプセル化/カプセル化解除の方法は多少異なります。
      • STRUC は通常のバッファのカプセル化を解除します。
      • LENVAL_STRUC は長さプレフィックス付きのバッファを生成します(つまり、最初の 4 バイトがデータのサイズを示し、その後に実際のデータが続きます)。
      • LENVAL_PAIR は 2 つの別々のパラメータ(サイズとバッファ)としてカプセル化解除されます。
      • INOUT_LENVAL_PAIR はデータなしで初期化されますが、LENVAL_PAIR と同様に 2 つのパラメータとしてカプセル化解除されます。
  1. 「cvhManagerCVAPICall」は、コマンドを実行し、その結果を取得するために呼び出されます。
  1. 俯瞰的に捉えた場合、この関数が呼び出されると、送信するデータが適切な形式でシリアル化され、IOCTL_SUBMIT 呼び出しが実行され、最終的にデータがファームウェアに送信されます。
  2. コマンドの実行が完了すると、データが返されて逆シリアル化され、前のステップで準備された「in_param_list_entry 配列」に入力されます。
  1. 最後に、関数「cvhSaveReturnValues」を使用して「in_param_list_entry 配列」を解析し、呼び出し元が提供するオブジェクトの配列にこれらの値を抽出します。
  1. たとえば上のスクリーンショット(図 22)では、「in_param_list_entry」にパラメータが 1 つあり、そのタイプは CV_ENCAP_INOUT_LENVAL_PAIR です。そのため、「cvhSaveReturnValues」を呼び出すと、2 つのパラメータが生成されます。第 1 のパラメータは「cv_get_random」によって返されるデータのサイズ、第 2 のパラメータは実際のデータです。

ファームウェア側では、CV コマンドを処理するときに戻り値が再定義されますが、これは驚くべきことです。

図 23:cv_get_random コマンドを処理する CvManager(ファームウェア側)

このデータの処理方法が原因で、逆シリアル化が安全に行われないことが判明しました。この問題の根本原因分析については、CVE-2025-24919popup_icon で説明しています。手短に説明すると、ファームウェア側でファームウェアからホストに渡すパラメータが再定義されると、ホスト側でデータが不正にカプセル化解除される可能性があります。たとえば悪意のあるファームウェアイメージが「cv_get_random」の戻り値のタイプを CV_INOUT_LENVAL_PAIR から CV_ENCAP_STRUC に変更した場合、生成されたデータのサイズを受け取るはずの「pLen」引数にデータ自体が格納されてしまいます。図 22 の「pLen」変数は、サイズ値を整数として受け取るためのスタック変数です。そのため、4 バイトを超えるデータを受け取るとこのスタックはオーバーフローし、任意のコードの実行につながる可能性があります。

エクスプロイトの制約

「bcmbipdll.dll」ファイルと一部の ControlVault サービスは ASLR 対応でないため、エクスプロイトがはるかに容易になります。オフセットをハードコードできるため、悪意のある ControlVault デバイスが使用できる情報漏洩を見つける必要がないからです。ただし、データ実行防止(DEP)機能が導入されているため、エクスプロイトをさらに進めるには ROP チェーン攻撃を実行する必要があります。驚くべきことに、もう 1 つの一般的な緩和策であるスタックカナリアは部分的にしか実装されておらず、ControlVault サービスと DLL にごくまれにしか存在しません。たとえば「cv_get_random」の場合、「pLen」はスタック変数ですが、この関数を保護するためのスタック Cookie が含まれていません。このため、エクスプロイトされやすいにもかかわらず高い権限のコンテキストで使用される CV コマンドを特定するという副次的な調査が発生します。

実際、ターゲットとして最適な CV コマンドには次の制約があります。

  • 高い権限を持つサービスによって(直接または呼び出しチェーン内で)使用される必要があります。
  • CV コマンドに渡される変数の 1 つは、CVE-2025-24919 で報告されたバグを使用して破損できるスタック変数である必要があります(「cv_get_random」の「pLen」変数など)。
  • 破損予定のスタック変数と、スタックオーバーフローのターゲットとなる戻りアドレスの間には、スタック Cookie が存在してはなりません。

ターゲットの発見

「cv_get_random」関数は理想的な候補ですが、残念ながらこの関数を確実に使用しているコードを見つけるのは困難です。

CV コマンドの大半を調査した結果、次のことがわかりました。

図 24:WBFUSH_ExistsCVObject による CSS_GetObject の呼び出し

この関数の最初の引数「cvHandle」は、オブジェクトへのハンドルです。これは「CSS_GetObject」に渡され、「CSS_GetObject」はスタック変数「objHeader」に、このハンドルに関連付けられたオブジェクトのヘッダーを格納します。コールスタックの下流では、「cv_get_object」が「cvHandle」変数と「objHeader」変数の両方を使用して呼び出されます。これらの関数のスタックレイアウトが原因で、CVE-2025-24919 を悪用して「objHeader」変数を破損し、親関数でスタックオーバーフローをトリガーすることが可能です。

エクスプロイトの詳細

「WBFUSH_ExistCVObject」関数は、ControlVault ファームウェアに保存されている実際のオブジェクトにオブジェクトハンドルが関連付けられているかどうかを検証するために「BCMStorageAdapter.dll」によって使用されます。一方、「BCMStorageAdapter」は、Windows 生体認証フレームワーク(WBF)との接続に必要なアダプタの Broadcom 実装の一部です。これらのアダプタは、指紋リーダーを WBF に接続して、Windows Hello(指紋認証によるログイン)やその他の生体認証対応シナリオで使用するために必要です。脆弱な関数に到達するコールスタックは次のとおりです。

StorageAdapterControlUnit

-> WBFUSH_ExistsCVObject

-> CSS_GetObject

-> cv_get_object

「StorageAdapterControlUnit」関数に通常のユーザーがアクセスするには、「WinBioOpenSession」を使用して適切なアダプタを開き、「WINBIO_COMPONENT_STORAGE」コンポーネントを使用して「WinBioControlUnit」コマンドを実行します。

図 25:WinBioControlUnit のプロトタイプ

「ControlCode」パラメータは、どのアダプタの関数を呼び出すかを指定します。

図 26:ControlCode=2 を指定した StorageAdapterControlUnit

「BCMStorageAdapter !StorageAdapterControlUnit」をリバースエンジニアリングすることにより、「ControlCode=2」を使用すると、呼び出し元から提供されたハンドルを使用して「WBFUSH_ExistsCVObject」が呼び出されることがわかります。具体的には、「WinBioControlUnit」に渡される「SendBuffer」引数の最初の 4 バイトが、期待されるオブジェクトハンドルにキャストされます。

この点を踏まえると、エクスプロイトプロセスは次のようになります。

  1. ファームウェア上でコードを実行してデバイスキーを漏洩させ、この特定のデバイスで受け入れられるファームウェアファイルを偽造できるようにします。
  2. 改ざんした「cv_get_object」関数を使用して悪意のあるファームウェアアップデートを偽造します。
    1. 「cv_get_object」関数にはバックドアを仕掛けます。オブジェクトハンドルが特定のマジック値(例:0x1337)と一致する場合、「cv_get_object」関数はスタックオーバーフロー ペイロードを返し、カプセル化パラメータを改ざんすることで CVE-2025-24919 をトリガーします。ハンドルが 0x1337 でない場合、「cv_get_object」関数は通常どおり実行され、バックドアによる意図しない副次的影響を回避します。
    2. スタックオーバーフロー ペイロードは、最終的にリバースシェルの実行につながる ROP チェーンになります。
  3. 悪意のあるファームウェアアップデートをインストールします。
  4. 「ControlCode=2」と、「SendBuffer」として「b”\x37\x13\x00\x00”」(0x1337 の DWORD としてのリトルエンディアン表現)を指定して、「WinBioControlUnit」関数を呼び出します。
  5. リバースシェルに接続し、SYSTEM 権限を取得したことを確認します。

27SYSTEM サービスのエクスプロイトのデモ

さらなる調査

インプラント

上記のプロセスは、Windows 上で SYSTEM 権限に昇格する最も複雑な方法の 1 つに見えるかもしれません。しかし、これは状況に応じて考える必要があります。代わりに使用できるサービスや関数は他にも存在します。今回の例では、デモを構築しやすい関数を選択しました。実際には他の関数を利用すれば、ユーザーの操作なしに脆弱性をトリガーすることも可能です。休止状態で時々起動してコールホームできるスタンドアロンのインプラントであれば、これは理にかなっています。もちろん、武器化されたインプラントの開発は、今回の調査の範囲外です。

物理的な攻撃

これまで言及していなかったもう 1 つの有望な切り口は、物理アクセスです。USH ボードは内蔵 USB デバイスです。適切なコネクタがあれば、ボードを搭載したラップトップを開けてボードに直接接続できます。物理アクセスへの対策(例:シャーシ侵入アラート)は存在しますが、これらは一般的にオプトインです。そのため攻撃者は、10 ~ 20 分間物理アクセスができれば、この詳細解説記事に記した攻撃を実行できます。ただし、他の要件を考慮する必要がない(例:ユーザーとしてログインする必要がない、ディスク暗号化で保護されていない)場合です。

次の動画の短いデモでは、USB 経由で USH ボードに直接接続できることを示しています。

図 28:物理的な攻撃のデモ

この動画では、無効化状態の ControlVault デバイスがすでに存在します。これは、使用中のマシンに ControlVault デバイスが組み込まれていたためです。関連するドライバ/DLL もインストール済みです。USB ケーブルを接続すると、新しい ControlVault デバイスが表示され、これが操作対象となります。

影響

攻撃のシナリオ

この記事で解説したリスクをまとめると、次の図のようになります。

図 29:攻撃のシナリオ

USH ボードのいずれかで実行中のファームウェアを改ざんできる脆弱性は、ローカル攻撃者によって、権限の取得、指紋認証ログインのバイパス、Windows の侵害に悪用される可能性があります。侵入後の状況においても、攻撃者がこの脆弱性を悪用する可能性は否定できません。ユーザーのワークステーションが侵害された場合、そのマシン上で動作している ControlVault ファームウェアをインプラントとして機能するように改ざんすると、システムを完全に再インストールした後でもファームウェアが存在し続ける可能性があります。

検出

侵害された ControlVault デバイスを検出するのは簡単ではありません。インプラントは新しいファームウェアアップデートを無視する可能性があります。そのため、正規のファームウェアアップデートが正常にインストールされ、期待されるバージョン文字列が返されることを確認することが、チェックの第 1 歩です。

これは、この記事の前半で示した Python コード(図 5)で実行できます。あるいは、デバイスマネージャで ControlVault デバイスのプロパティを確認する方法もあります。「バージョン管理」パネルには、デバイスから報告された ControlVault ファームウェアのバージョンが表示されます。

ControlVault デバイスのローカルエクスプロイトの兆候は、「bcmbipdll.dll」をロードする予期しないプロセスや、ControlVault デバイス自体へのハンドルを開こうとするプロセスを監視することで検出できます。デバイスのパスは、ラップトップのモデルと内部 USB 接続によって異なる場合があります。フルパスは、InterfaceGuid: {79D2E5E9-8883-4E9D-91CBA14D2B145A41} を指定した「SetupDiGetClassDevsW / SetupDiEnumDeviceInterfaces」を実行すると取得できます。

Cisco Secure Endpoint をご利用のお客様は、「bcmbipdll.dll Loaded by Abnormal Process」というシグネチャ定義によって、潜在的なリスクを把握できます。

最後に、「WinBioSvc」、「bcmHostStorageService」、「bcmHostControlService」、または「bcmUshUpgradeService」で予期しないクラッシュが発生する場合、何らかの問題の兆候である可能性があります。

まとめ

ControlVault は、ハードウェアからファームウェア、そしてソフトウェアに至るまであらゆるものを網羅し、驚くほど複雑な攻撃対象領域です。いくつもの周辺機器、フレームワーク、ドライバも関係しています。2010 年代初頭まで遡るレガシーコードベースを持ち、長年にわたりさまざまなファーストパーティ ソフトウェアが ControlVault と連携してきました。今回の詳細な調査では、ControlVault の複雑さのごく一部に触れたに過ぎませんが、侵害の影響がいかに広範囲に及ぶかを示しました。最も驚くべきことは、このテーマに関する初の公開調査が、どうやら本調査であるらしいという点です。ファームウェアのセキュリティは新しいテーマではありませんが、ControlVault に似たデバイスの中には、まだ発見されておらず、どのような予期せぬリスクをもたらす可能性があるか評価されていないものが数多く存在するのではないかと考えられます。

 

本稿は 2025 年 8 月 9 日にTalos Grouppopup_icon のブログに投稿された「ReVault! When your SoC turns against you… deep dive editionpopup_icon」の抄訳です。

コメントを書く