
一部の旧型Qualcommチップセットのハイパーバイザーファームウェアにおけるセキュリティ問題
CVE-2022-22063 は、一部の古い Qualcomm チップセットのハイパーバイザーファームウェアにおけるセキュリティ上の問題です。保護されていないハードウェアコンポーネント(「ブートリマッパー」)を悪用すると、改変されたオペレーティングシステムからハイパーバイザーへの完全な読み取り/書き込みアクセスを取得できます(権限昇格)。影響を受けるプラットフォームでは、特定のファームウェアバージョンに関する知識(アドレスや変数など)が不要なため、この問題の悪用は簡単です。
注記: Qualcomm は顧客に修正を提供しましたが(アップデートをリリースする十分な時間がありました)、影響を受けるデバイスの多くはかなり古く、ベンダーから修正を受け取れない場合があります。この問題は、改変された、または侵害されたオペレーティングシステムから(別のセキュリティ問題を利用して)のみ悪用可能です。ファームウェアに脆弱性があっても、オペレーティングシステムを最新かつ安全な状態に保つことで十分な場合があります。
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hこの問題は [Qualcomm's December 2022 Security Bulletin] にも掲載されました。
この問題は、影響を受ける対象で実行されているハードウェアとソフトウェアの組み合わせに依存します:
hyp パーティション内の ELF イメージ)。APCS_BOOT_START_ADDR_NSEC と呼ばれるハードウェアレジスタを使用して設定可能)。これはハイパーバイザーによって保護されておらず、したがって特権レベルの低いオペレーティングシステムカーネル(例:Linux)からアクセス可能です。影響を受けるハードウェアを搭載している可能性が高いチップセットは他にもいくつかあります(例:MSM8909 や MSM8953)が、それらには侵害される可能性のある別個のハイパーバイザーファームウェアがありません。
権限昇格: すでに侵害されたオペレーティングシステムカーネル(例:Linux)があれば、この問題によりハイパーバイザーレベル(ARM では EL1 -> EL2)への権限昇格が簡単に可能になります。ハイパーバイザーが管理するすべてのメモリを読み取りまたは書き込みできます。これにより、ハイパーバイザーが管理する異なるセキュリティドメインや仮想マシン(存在する場合、設定に依存)の分離が破られます。
(関連情報:[An introduction to Access Control on Qualcomm Snapdragon Platforms])
セキュアブート: 市場に出回っているほとんどの Qualcomm デバイスは、ファームウェアの不正な変更を防ぐためにセキュアブートを利用しています。ファームウェアは暗号署名され、ブートチェーンによって検証されます。この問題により、改変されたオペレーティングシステムから(公式にサポートされている「ブートローダーアンロック」または別のエクスプロイトを介して)、ロードされたハイパーバイザーファームウェアを実行時に変更したり、完全に置き換えたりすることが可能です。
(関連情報:[Qualcomm Secure Boot and Image Authentication Technical Overview (v1.0)] および (v2.0))
注記: この問題は元々、Qualcomm Snapdragon 410 (MSM8916) プラットフォームで発見されました。以下の説明の一部は MSM8916 に固有の場合があります。例えば:
ただし、一般的な概念は影響を受けるすべてのプラットフォームに同様に適用されます。
ARMv8-A 64 ビットアーキテクチャは、4 つの特権レベル(「例外レベル」、EL)を定義しています。アプリケーション、オペレーティングシステムカーネル、ハイパーバイザーに通常使用される別々のレベルがあります:
CPU は例外の発生中にレベル間を切り替えます。例えば、割り込みが到来した場合などです。また、ハイパーバイザーコール (hvc) などの特別な命令を使用して、一部のレベル間を切り替えることもできます。
(関連情報:[AArch64 Exception model])
ハイパーバイザーは、それぞれ独立したオペレーティングシステムカーネルを持つ 1 つまたは複数の仮想マシンをホストできます。各仮想マシンには、ステージ2変換 を使用して独自のメモリビューを提供できます。仮想マシンからのすべてのメモリアクセスは、2 つの変換ステージを通過します:最初のステージは(仮想)オペレーティングシステムが管理し、2 番目のステージはハイパーバイザーが管理します。ハイパーバイザーや他の仮想マシンが使用するメモリは、変換テーブルから省略することで隠すことができます。
(関連情報:[AArch64 virtualization], [AArch64 memory management])
Qualcomm のハイパーバイザーファームウェアは EL2 で実行され、ステージ2変換を使用して、EL1 で実行中のメインのオペレーティングシステムカーネル(通常は Linux)からハイパーバイザーメモリへのアクセスを禁止しています。この構成では、ステージ2変換は主にアドレス変換なしのメモリ保護に使用されることに注意してください。メインのオペレーティングシステムは、メモリマップド入出力 (MMIO) 空間内のほとんどのハードウェアコンポーネント(例:SD コントローラーやカメラサブシステム)に直接アクセスできます。ハイパーバイザー/EL2 (hyp) とセキュアモニター/EL3 (tz の一部) に属するメモリへのアクセスは制限されています:
ブートリマッパー は仮想化とは関係ありません:CPU コアの起動初期に必要です。このハードウェアプラットフォームでは、CPU コアは常にアドレス 0x0 から実行を開始します。ブートリマッパーは、CPU の周囲に組み込まれた追加のハードウェアコンポーネントであり、最初の 64 または 128 KiB (0x00000 - 0x20000) を設定可能なメモリ領域にリマップします。
デフォルトでは、ブートリマッパーはブート ROM(デバイス起動時に最初に実行されるコード)を指しています。その後、マッピングが変更され、他の CPU コアが RAM にロードされた EL3 ファームウェア(tz の一部)で直ちに実行を開始するようになります:
CPU がアクセスするアドレス(tz 内)は、2 つの異なる物理アドレスを使用してアクセス可能になることに注目してください:RAM 内の実際のアドレス (0x8650xxxx) と、ブートリマッパーを使用したリマップ後のアドレス (0x0000xxxx) です。
実際には、ブートリマッパーには 2 つの別々のインスタンスがあります:
APCS_BOOT_START_ADDR_SEC (= 0x0b010004) を使用して設定できますが、セキュア状態でのみ設定可能です。APCS_BOOT_START_ADDR_NSEC (= 0x0b010008) を使用して設定可能です。両方のブートリマッパーインスタンスは、リマップ領域のベースアドレスと 2 つの設定ビットを含むメモリレジスタを使用して設定できます:REMAP_EN はリマップを有効にし、BOOT_128KB_EN は 64 KiB だけでなく最初の 128 KiB をリマップします。
(関連情報:[Qualcomm Snapdragon 410E Technical Reference Manual rev. D]、85 ページと 116 ページ)
前の 2 つのセクションの知識を使えば、基本的なアイデアは単純です:ブートリマッパーを使用して、ハイパーバイザーのメモリ保護(ステージ2変換)をバイパスします。
ブートリマッパーは CPU 起動時にのみ機能するわけではありません。いつでも使用でき、リマップ領域への完全な読み取り/書き込み/実行アクセスを許可します。また、Qualcomm のハイパーバイザーは、影響を受けるデバイス上でオペレーティングシステムがブートリマッパーの非セキュアインスタンスを設定およびアクセスすることを防いでいないようです(ステージ2変換によって保護されていません)。したがって、この問題は次のように簡単に悪用できます:
hyp)を指すように設定し、リマップ領域は動的に(ブロック単位で)シフトでき、ブートリマッパーで利用可能な 64/128 KiB よりも大きなメモリ領域にアクセスできます。また、これを使用してハイパーバイザーのメモリ保護を完全に無効にすることも可能です(概念実証 を参照)。
注記: 同じエクスプロイトはセキュアワールドファームウェア (tz) には機能しません。ブートリマッパーはステージ2変換をバイパスできますが、DRAM 内の tz メモリ領域は、ブートリマッパーを通過した後のアクセスを遮断する追加のハードウェアコンポーネント(CPU の外部)によって保護されているようです:
tz メモリ領域はおそらくセキュア状態でのみアクセス可能です。このエクスプロイトでバイパスできるのはハイパーバイザーのメモリ保護のみであり、ハードウェアの他のセキュリティメカニズムは引き続き有効です。
ブートリマッパーを使用すると、ファームウェアバージョンに関する知識なしに、元のハイパーバイザーファームウェアを実行時に完全に無効化して置き換えることができます。特に、変更される可能性のある変数や関数のメモリアドレスを取得するためにリバースエンジニアリングを使用する必要はありません。ハイパーバイザーファームウェアのおおよそのメモリ領域を知るだけで十分です。例えば、オープンソースの Linux コード内のメモリ予約から、またはハイパーバイザーファームウェアバイナリ(内部ストレージの hyp パーティションにあります)の ELF ヘッダーを読むことによって得られます。
一般的なアイデアは次のとおりです:
hvc) を実行して、オペレーティングシステムからハイパーバイザーへ(EL1 から EL2 へ)切り替えます。これを実装するコードは長くはありませんが、低レベルの AArch64 アセンブリと CPU キャッシュの慎重な操作が必要です。しかし、主な疑問はまだ未解決です:コードを特定のハイパーバイザーファームウェアバージョンに依存させずに、シェルコードを正確にどこに書き込むべきでしょうか?
ハイパーバイザーコール中(一般に任意の例外中)は、CPU の実行は特別なメモリアドレス、つまり 例外ベクター に強制的に移されます。例外ベクターは、現在の例外レベルまたは下位の例外レベルから発生するさまざまな種類の例外を処理するコードを含む、より大きな ベクターテーブル の一部です:
各ボックスは、32 個のアセンブリ命令を格納できる例外ベクターを表しています。この領域では不十分なため、通常は追加コードを置くためのより広い領域へジャンプする分岐命令が含まれます。
オフセットは、各例外レベルのベクターテーブルのベースアドレスを定義する ベクターベースアドレスレジスタ (VBAR) を基準にしています。ハイパーバイザーはベースアドレスを VBAR_EL2 CPU レジスタに書き込みます。
ハイパーバイザーコールは、下位の例外レベル(EL1 で実行中のオペレーティングシステムカーネルから EL2 のハイパーバイザーへ)から行われる同期例外です。したがって、オペレーティングシステムカーネルが 32 ビットモードで実行されている場合、CPU は VBAR_EL2+0x600 にジャンプし、64 ビットモードでは VBAR_EL2+0x400 にジャンプします。ブートリマッパーを使用してこのアドレスにカスタムコードを書き込み、ハイパーバイザーコールを行うと、CPU はシェルコードの実行を開始します。