Android の信頼できる実行環境のセキュリティ調査 (3): 権限昇格

0x00 概要

シリーズの最初の 2 つの記事では、最初に Samsung の TEE OS TEEGRIS を紹介し、次にエクスプロイトを実装し、信頼できるアプリケーション (TA) に対してランタイム制御を取得する方法について説明しました

最後の記事では TEE コアに対するランタイム制御を取得する方法を詳しく見ていきます。

特権 TA が物理メモリを TA メモリ空間にマッピングするために使用できるドライバをカーネルがどのように公開するかを示します

HDCP TA のドライバーを使用してセキュリティ レジスタをマッピングし、TEE メモリの保護メカニズムをキャンセルします

最後に、同じ HDCP TA を使用してハイパーバイザー ページ テーブルを変更し、Android アプリケーションが完全な読み取り/書き込みアクセスで (すでに保護されていない) TEE メモリをマップできるようにします。

0x01 脆弱性を探しています

これまで、物理メモリを TA に直接マッピングする機能は、TEE エクスプロイトで頻繁に使用される脆弱性でした。S7 モデルの場合、カーネルが TA にマップされる物理メモリ領域に対して十分なチェックを実行しないため、EL3 コードなどの機密性の高い場所が TA メモリにマップされることが問題になります。

TEEGRIS の実装方法を詳しく調査し、同様の脆弱性が存在するかどうかを確認することは理にかなっています。メモリ内で実行されているドライバーがすぐに見つかり、phys:// 経由でアクセスできました。

ペイズハンドラーの登録:
phys ドライバーは /dev/memLinux デバイスに非常に似ており、多くのシステム コールを通じてアクセスできますが、その中で最も注目に値するのは open と mmap です。phys:// のオープン システム コール ハンドラーは次のことを行います。

オープン システム コール ハンドラーでの TA 権限チェック:

行 9 ~ 13 で、コードは 5 つの潜在的な領域、つまり内部 RAM、内部ブート ROM、セキュア DRAM、非セキュア DRAM、およびレジスタのうちの少なくとも 1 つをマップする権限を TA が持っているかどうかをチェックします。権限があれば正常に実行されます。

最初の記事では、アクセス許可が各グループに静的に割り当てられることを学びました。幸いなことに、ランタイム制御を実装できる HDCP TA は、物理ドライバーにアクセスできる高い特権を持つ samsung_drv グループに属しています。実際、TA はコマンドの 1 つを実行するために物理ドライバーを必要とするため、HDCT TA は当然 samsung_drv グループに属します。

TA がアプリケーションにアクセスできることがわかったので、mmap ハンドラーを分析して、どのようなチェックが実行されるかを確認できます。

物理 mmap ハンドラー:
16 行目で関数 get_memory_type が呼び出され、指定された範囲がパラメータとして渡されます。続いて、get_memory_type 関数の戻り値に値 1 が左シフトされます。コードの残りの部分では、関数が結果が 8 (図の緑色で示される非セキュア RAM に相当)、16 (図の赤色で示されるレジスタに相当)、1 ( ROMに相当(図では赤で表示)、青で表示)。セキュア RAM と内部 RAM (EL3 コードを実行している) は、両方とも TA が持つことができる権限のリストに含まれているにもかかわらず、TA にマップされることはないようです。

ここでは get_memory_type 関数については詳しく説明しません。samsung_drv グループには ROM をマップする権限がないため、HDCP TA では非セキュア RAM とレジスタのみをマップできるようです。これら 2 つの領域のうち、REE での権限昇格によって悪用される可能性がある非セキュア RAM については、あまり懸念していません。TEE は完全にセキュア RAM に配置されているため、TEE にさらに関心があります。攻撃者の観点からは、レジスタには、TrustZone の構成に使用されるレジスタを含む、ペリフェラルの構成に使用されるすべてのレジスタが含まれるため、レジスタにより多くの注意が払われる可能性があります。

アクセス対象がわかったので、これらのメモリ領域のマッピングとアクセスを可能にするリターン指向プログラミング ペイロードを作成できます。これを行うために、ユニバーサル物理メモリから物理メモリへのコピー ペイロードを作成し、具体的には次の操作を実行することにしました。

  • (1) 物理ドライバーのハンドルを開きます。
  • (2) mmap を 2 回呼び出して、ソース バッファとターゲット バッファをマップします。
  • (3) memcpyにジャンプします。

上記のプリミティブを使用すると、memcpy のソースをレジスタ メモリに設定し、ターゲットを非セキュア メモリに設定してレジスタを読み取ることを選択できます。また、memcpy のソースを非セキュア メモリに設定し、ターゲットをレジスタに設定することもできます。 Androidのそれをアプリ内で変更します。

ROP ペイロードを実装した後、どのコンテンツをどこにコピーするかを検討する必要があります。

0x02 イオンバッファ

最初のステップとして、Android 上で実行されている CA と TEE 上で実行されている脆弱な HDCP TA の間で物理メモリを共有する方法を見つける必要がありました。通常、通常の Android アプリケーションはバッファーがマッピングされている物理アドレスを知らないため、このプロセスは簡単ではありません。

この目的のために、Android が提供する ION メカニズムを使用します。これは、2 つのエンティティ間でデータが共有されるときにメモリ内のコピーを削除する共有メモリ マネージャーとして理解できます。Android アプリケーションでこれらのバッファにアクセスできるため、クライアント アプリケーションで使用できます。アロケータは、メモリが既知の物理アドレスにマップされていることを保証します。このサブシステムには、/dev/ion と対話することでアクセスできます。これにより、オープン呼び出しを行ってファイル記述子を取得し、ioctl を介して ION バッファを要求できます。

Samsung デバイスのファームウェア バージョンに一致する Android ソース コードをダウンロードし、デバイス ツリー ソースを表示して要件を満たす ION バッファを見つけました。exynos9820-beyond0lte_common.dtsi という名前のファイルの次の断片には、開始アドレス (0x99000000) やサイズ (0xE400000) など、このバッファーに関する詳細が含まれています。

camera {
compatible = "exynos9820-ion";
ion,heapname = "camera_heap";
reg = <0x0 0x99000000 0xE400000>;
ion,recyclable;
};

つまり、camera_heap ION バッファ全体を CA にマップし、物理アドレス 0x99000000 を HDCP TA にマップすると、同じメモリの 2 つのビューが存在するはずです。

0x03 TZASC の変更

この問題を解決したので、残った唯一の問題は、マップするレジスタとそのマップ先の場所を見つけることです。最初の記事では、TrustZone の構成に一般的に使用される 2 つのレジスタ、TZASC と TZPC について説明しました。TA がそれらのいずれかにアクセスすると、レジスタの内容を読み出すと同時に書き込むことができるため、メモリ領域または周辺機器の保護が解除されます。原則として、この 2 つのいずれかを変更すると、REE が TEE にアクセスできるようになり、最終的には TEE の元のセキュリティに影響を与える可能性があります。ただし、私たちの経験では、TZPC の構造はデバイスに依存する傾向があるため (現在、S10 Exynos チップに関する公開されたドキュメントはありません)、TZASC に対してそれを行う方が簡単ですが、TZASC はすでにある程度標準化されています。

TEEGRIS カーネル コードをリバースすることで、TZASC 処理に関連する関数を発見し、この SoC には実際には 4 つの TZASC ペリフェラル (おそらく各 DRAM コントローラに 1 つずつ) があることがわかりました。

まず文字列 tzasc を検索し、次に TEE カーネル バイナリ内に一致するものがあるかどうかを確認します。下の図は、TZASC ハンドラーのアドレスを与える初期化関数に到達する方法を示しています。

TZASC ドライバーの初期化:

dev://tzasc デバイスには、ioctl システム コールを使用してアクセスできます。最終的には tzasc_get_settings_for_range を呼び出します。

逆コンパイルされた tzasc_get_settings_for_range:

この関数は私たちが達成しようとしている攻撃にはあまり関連していないようですが、この関数が tzasc_physical_addresses と呼ばれる構造体を参照していることに気付きました。この構造体には、4 つの異なる TZASC ペリフェラルであると思われるものの物理アドレスが含まれています。

4 つの TZASC ペリフェラルの物理アドレス:
仮説を検証するために、この情報を、/proc/iomem を読み取ることによって Android から収集された各物理デバイスの物理メモリ マップと関連付けました。この検証の背後にある考え方は、TEE の物理範囲が REE の物理範囲と重複すべきではないということです。次のリストは、REE mcinfo レジスタの位置間にギャップがあり、TZASC レジスタ用の余地が残っていることを示しています。TZASC 文字列は出力を読み取るために手動で追加されており、通常の iomem 出力には含まれていないことに注意してください。

beyond0:/data/local/tmp # cat /proc/iomem
…
1bc3004c-1bc3004f : /mcinfo@1BC300000
-----TZASC1:0x1BC40000-----
1bd3004c-1bd3004f : /mcinfo@1BC300000
-----TZASC2:0x1BD40000-----
1be3004c-1be3004f : /mcinfo@1BC300000
-----TZASC3:0x1BE40000-----
1bf3004c-1bf3004f : /mcinfo@1BC300000
-----TZASC4:0x1BF40000-----

必要な情報をすべて収集したら、いよいよエクスプロイトが機能するかどうかを確認してみます。まず TZASC の内容を読み取ろうとします。次の手順に従ってください。

  • (1) Camera_heap バッファーをマップし、既知の固定値に初期化できる Android アプリケーションを見つけます。
  • (2) 同時に、HDCP TA の脆弱性を悪用し、0x1BC40000 (最初の TZASC のアドレス) と 0x99000000 (camera_heap のアドレス) の間の 0x1000 バイト (TZASC レジスタ サイズ) の物理メモリ コピーに物理メモリを転送します。 。
  • (3) Android アプリケーションで、camera_heap を継続的に監視します。その内容が変化した場合、TA エクスプロイトが成功したことが証明されます。
  • (4) さらに分析できるよう、バッファの内容をファイルに保存します。

実際には、数秒後に実行が完了し、TZASC のダンプがファイルに保存されます。次に、16 進エディタを使用してダンプの内容を分析しました。

TZASC 範囲でダンプします。
システム内の 4 つの TZASC コントローラーの構成は非常に似ていますが、ここでは例として 1 つだけを分析します。上の図の緑色でマークされた領域はエリア 0 を表します。これは、最初の記事で説明したデフォルトのベース エリア (0x0000000000000000-0x000000fffffff000) です。紫は、特定の時点でシステム上に構成されたすべてのセキュリティ スコープを表します。各セキュリティ範囲は 2 つの 8 バイト (リトル エンディアン) で構成され、その後に属性用の別の 16 バイトが続きます。最後に、赤でマークされた領域は、セキュリティ範囲の開始アドレスと終了アドレスです。これは、TEE OS の範囲 (0x00000000bab00000-0x00000000bffff000) です。これは、/proc/iomem によって提供される情報とも一致します。

80000000-baafffff : System RAM
80090000-8197ffff : Kernel code
82380000-831b0fff : Kernel data
c0000000-dfffffff : System RAM

REE はこのメモリにアクセスできないため、TEE 領域にギャップがあることがわかります。

次に、単に読み取るのではなく、4 つの TZASC をカバーするようにエクスプロイト コードを微調整します。同じ物理 ROP ペイロードを使用できますが、送信元アドレスと宛先アドレスを交換します。TEE OS セキュリティ範囲の開始アドレスをターゲットにし、終了アドレスよりも大きい値に設定します。その結果、スコープは無効となり無視され、TEE メモリ全体が非セキュアとして設定されます。

0x04 TEE メモリにアクセス

この時点で、すべての TEE メモリが非セキュアで、REE からアクセスできるシステムが実行されています。これで、最後のハードルが残ります。この記憶にどうやってアクセスするかということです。

前述したように、物理ドライバーがセキュア メモリへのアクセスを拒否するため、HDCP TA はアクセスできません (スコープがセキュアとして再構成されている場合でも)。そのため、Androidアプリからアクセスする必要があります。ただし、通常の状況では、この範囲は TEE 用に予約されているため、Linux カーネルによってマッピングが許可されます。したがって、エクスプロイト中、TA は依然として非セキュア メモリ全体にアクセスできます。このようにして、問題を解決する方法を見つけました。

いくつかの異なるアイデアが考えられますが、最終的には Samsung の管理プログラム RKP を使用して実装することにしました。ARMv8-A 特権レベルの概要については、最初の記事を参照してください。Samsung は、実装しているセキュリティ機能を説明するために次の図を公式に提供しました。

RKP 読み取り専用ページ保護:
ARMv8-A システムにハイパーバイザーがある場合、システム内で複数のゲスト オペレーティング システムを許可することを本来の目的とする変換テーブルの別の段階が存在します。したがって、攻撃プロセスは 2 つの段階に分かれています。

  • 最初の段階は、Linux カーネルが仮想アドレスからいわゆる中間物理アドレス (IPA) への変換を実行するときです。
  • 第 2 段階は、ハイパーバイザーが IPA を最終物理アドレス (PA) に変換する段階です。

次の図は、2 段階のメモリ変換プロセスを示しています。

ハイパーバイザーはメモリ権限を制限できます。つまり、ページが第 2 フェーズで読み取り専用としてマップされている場合、ページが書き込み権限でマップされている場合でも、Linux カーネルはその内容を変更できません。RKP を使用すると、Linux カーネル メモリ内の特定のセキュリティ関連構造を強制的に読み取り専用にし、攻撃者がカーネル コンテキストで任意のコードを実行した場合でも変更できなくすることができます。

次に、私たちの攻撃のアイデアは、camera_heap ION バッファに対応するハイパーバイザーの変換テーブルを見つけて、TEE カーネルの物理アドレスを指すように変更することです。Linux カーネル自体内の構造を変更することで、ハイパーバイザーを無視して TEE メモリにアクセスできることに注意してください。ただし、ハイパーバイザーは Linux カーネルよりもはるかに複雑ではないため、ハイパーバイザーを対象とします。第 2 段階のページ テーブルは固定の物理アドレスにマップされ、決して変更されないと想定しています。そのため、変更が必要な正確なメモリ位置を見つけるために複雑な結果を解析する必要はありません。

私たちの仮説が正しいかどうかを確認するには、システム RAM 内のハイパーバイザー メモリを見つけて抽出する必要があります。システムのメモリ レイアウトに関する情報を取得するために、/proc/iomem の情報ダンプを取得しました。そこには 3 つの主要な RAM 範囲が示されていました。このエクスプロイトを使用して各範囲の物理メモリをダンプし、RKP などの文字列を検索します。検索結果は、ハイパーバイザーが物理アドレス 0x87000000 の最初の RAM 範囲から開始することを示しています。

2 番目のタスクは、ハイパーバイザーのメモリ内で、camera_heap ION バッファに対応するページ テーブル エントリを見つけることです。

原理的には、ハイパーバイザー バイナリをリバース エンジニアリングして、TTBR レジスタがどのように初期化および解析されるかを調べることができます。ただし、実行時にハイパーバイザーのメモリをダンプしたため、それを調べてページ テーブルを直接見つけられるかどうかを確認することにしました。ページ テーブルを見つけるための前提条件は、その構造をよく知っていることです。ARM には、ブロック エントリとテーブル エントリの変換テーブル記述子の形式を説明する ARMv8-A アドレス変換に関するドキュメントがあります。下の画像はブロックエントリのフォーマットです。

ブロック ページ テーブル エントリの形式は次のとおりです。

この情報を使用して、以下の図に示す ION バッファに対応するブロック エントリをメモリ ダンプ内で見つけました。

Camera_heap バッファーのブロック エントリ:
上の図でマークされているエントリは、0x990004FD (リトル エンディアン) に設定されています。アドレスを変更するだけなので属性は無視できるため、出力ブロック アドレスは 0x99000000 となり、camera_heap のアドレスに対応します。

最後のステップは、camera_heap のページ テーブル エントリが TEE カーネルからのエントリに置き換えられるようにペイロードを変更することです。このように、REE から Camera_heap にアクセスするとき、CA は実際に TEE メモリにアクセスします。これを行うために、安全範囲を 2MB (各ブロック ページ テーブル エントリのサイズ) 増やし、カメラ ヒープ (つまり FD 04) によって設定された属性を保持するために下位バイトを予約しました。

これにより、セキュリティ スコープのダンプを成功させることができます。その中には、共有ライブラリ、ロードされた TA (エクスプロイトに使用された HDCP TA を含む)、TEE カーネル自体など、いくつかのバイナリが見つかりました。現在、私たちの制御下で、ページ テーブルを使用してメモリを REE にマッピングできるため、ページのアクセス許可に制限はありません。つまり、すべてのメモリ (コードを含む) が書き込み可能です。すべての変更は、同じメモリを使用して TEE のビューに反映されます。

要約すると、TEE メモリに完全にアクセスするために、次のような脆弱性の組み合わせを悪用したことがわかります。

  • (1) HDCP TA には GP パラメータ チェックがないため、TA で任意の読み取り/書き込みを実行できます。
  • (2) スタックベースのバッファ オーバーフローを悪用して、TA コンテキストで任意のコードを実行する可能性があります。
  • (3) アンチロールバックは TA には適用されません。つまり、Samsung が TA の脆弱性を正しく修正したとしても、脆弱性を備えた古いバージョンの TA をロードできる可能性があります。
  • (4) グループベースの権限割り当てメカニズムの粒度が大きすぎるため、実際、HDCP TA は非セキュア メモリにアクセスするだけで済みますが、権限が設定された後もレジスタをマッピングできます。
  • (5) レジスタへのアクセスを許可する TA の場合、カーネルは TZASC や TZPC などのレジスタのマッピングを許可します。これらのレジスタは、TrustZone テクノロジーによって提供されるセキュリティを完全に無効にする可能性があるため、TA はこれらのレジスタにアクセスすべきではありません。

0x05 概要

この時点で、完全な脆弱性悪用チェーンが形成されました。この段階で、すべての TEE メモリを Android アプリケーションにマッピングすることに成功しました。メモリは完全に読み取りおよび書き込み可能です。つまり、攻撃者は次のことを行うことができます。

(1) Android アプリケーションには権限制御が適用されないため、TA および TEE カーネルのコードを変更します。
(2) カーネルに KASLR、PAN、PXN のバイパスを実装します。

攻撃者が TEE を完全に制御できるようになると、TEE に実装されているデバイスの指紋や顔認識によるロック解除機能を変更して画面ロックをバイパスするなど、さまざまな攻撃がさらに実行される可能性があります。

おすすめ

転載: blog.csdn.net/weixin_45264425/article/details/132669557