macOS Thread Injection via Task port

htARTE(HackTricks AWS Red Team Expert) でAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

コード

1. スレッドハイジャッキング

最初に、リモートタスクからスレッドリストを取得するために**task_threads()**関数がタスクポートで呼び出されます。ハイジャックするスレッドが選択されます。このアプローチは、新しいミティゲーションによってthread_create_running()がブロックされているため、新しいリモートスレッドを作成する従来のコードインジェクション方法とは異なります。

スレッドを制御するために、**thread_suspend()**が呼び出され、その実行が停止されます。

リモートスレッドで許可される唯一の操作は、それを停止および開始し、そのレジスタ値を取得および変更することです。リモート関数呼び出しは、レジスタx0からx7引数に設定し、pcを目的の関数に向け、スレッドをアクティブ化することで開始されます。戻り値後にスレッドがクラッシュしないようにするには、戻り値を取得する必要があります。

1つの戦略は、リモートスレッドのために例外ハンドラを登録することで、thread_set_exception_ports()を使用することです。関数呼び出し前にlrレジスタを無効なアドレスに設定します。これにより、関数の実行後に例外が発生し、例外ポートにメッセージが送信され、スレッドの状態を検査して戻り値を回収できます。代替策として、Ian Beerのtriple_fetch exploitから採用された方法では、lrを無限ループに設定します。その後、スレッドのレジスタが**pcがその命令を指すまで**継続的に監視されます。

2. 通信用のMachポート

次の段階では、Machポートを確立してリモートスレッドとの通信を容易にします。これらのポートは、タスク間で任意の送信および受信権を転送するのに重要です。

双方向通信のために、2つのMach受信権が作成されます:1つはローカルタスクに、もう1つはリモートタスクにあります。その後、各ポートの送信権が対向するタスクに転送され、メッセージの交換が可能になります。

ローカルポートに焦点を当てると、受信権はローカルタスクによって保持されます。ポートはmach_port_allocate()で作成されます。課題は、このポートへの送信権をリモートタスクに転送することです。

1つの戦略は、thread_set_special_port()を活用して、ローカルポートへの送信権をリモートスレッドのTHREAD_KERNEL_PORTに配置することです。その後、リモートスレッドに対してmach_thread_self()を呼び出して送信権を取得するよう指示します。

リモートポートの場合、プロセスは基本的に逆転します。リモートスレッドに、mach_port_allocate()の返り値メカニズムのために適していないため、mach_reply_port()を介してMachポートを生成するよう指示します。ポートの作成後、mach_port_insert_right()がリモートスレッドで呼び出され、送信権が確立されます。この権利は、thread_set_special_port()を使用してカーネルに隠されます。ローカルタスクでは、リモートスレッドに対してthread_get_special_port()を使用して、リモートタスク内の新しく割り当てられたMachポートへの送信権を取得します。

これらの手順の完了により、Machポートが確立され、双方向通信の基盤が整います。

3. 基本的なメモリ読み取り/書き込みプリミティブ

このセクションでは、基本的なメモリ読み取りおよび書き込みプリミティブを確立するために実行プリミティブを利用することに焦点を当てます。これらの初期ステップは、リモートプロセス上でのより多くの制御を得るために重要ですが、この段階ではプリミティブはあまり多くの目的には役立ちません。すぐに、これらはより高度なバージョンにアップグレードされます。

実行プリミティブを使用したメモリ読み取りおよび書き込み

特定の関数を使用してメモリの読み取りおよび書き込みを行うことが目標です。メモリの読み取りには、次の構造に似た関数が使用されます:

uint64_t read_func(uint64_t *address) {
return *address;
}

メモリへの書き込みには、次のような構造に似た関数が使用されます:

void write_func(uint64_t *address, uint64_t value) {
*address = value;
}

これらの関数は、与えられたアセンブリ命令に対応しています:

_read_func:
ldr x0, [x0]
ret
_write_func:
str x1, [x0]
ret

適切な関数の特定

一般的なライブラリのスキャンにより、これらの操作に適した候補が特定されました:

  1. メモリの読み取り: Objective-Cランタイムライブラリからのproperty_getName()関数がメモリの読み取りに適した関数として特定されました。以下に関数が示されています:

const char *property_getName(objc_property_t prop) {
return prop->name;
}

この関数は、objc_property_tの最初のフィールドを返すことで、read_funcのように効果的に機能します。

  1. メモリの書き込み: メモリの書き込み用の事前に構築された関数を見つけることはより困難です。ただし、libxpcからの_xpc_int64_set_value()関数は、次の逆アセンブリを持つ適切な候補です:

__xpc_int64_set_value:
str x1, [x0, #0x18]
ret

特定のアドレスに64ビットの書き込みを行うには、リモートコールは次のように構造化されます:

_xpc_int64_set_value(address - 0x18, value)

4. 共有メモリのセットアップ

目的は、ローカルとリモートタスク間で共有メモリを確立し、データ転送を簡素化し、複数の引数を持つ関数の呼び出しを容易にすることです。このアプローチには、libxpcとそのOS_xpc_shmemオブジェクトタイプを活用します。これはMachメモリエントリに基づいて構築されています。

プロセスの概要:

  1. メモリの割り当て:

  • 共有のためのメモリをmach_vm_allocate()を使用して割り当てます。

  • 割り当てられたメモリ領域のためのOS_xpc_shmemオブジェクトを作成するためにxpc_shmem_create()を使用します。この関数はMachメモリエントリの作成を管理し、OS_xpc_shmemオブジェクトのオフセット0x18にMach送信権を格納します。

  1. リモートプロセスでの共有メモリの作成:

  • リモートプロセスでmalloc()にリモートコールしてOS_xpc_shmemオブジェクトのためのメモリを割り当てます。

  • ローカルのOS_xpc_shmemオブジェクトの内容をリモートプロセスにコピーします。ただし、この初期コピーでは、オフセット0x18で正しくないMachメモリエントリ名が含まれます。

  1. Machメモリエントリの修正:

  • thread_set_special_port()メソッドを使用して、Machメモリエントリの送信権をリモートタスクに挿入します。

  • リモートメモリエントリの名前でオフセット0x18のMachメモリエントリフィールドを上書きして修正します。

  1. 共有メモリのセットアップの最終化:

  • リモートのOS_xpc_shmemオブジェクトを検証します。

  • リモートでxpc_shmem_remote()にリモートコールして共有メモリマッピングを確立します。

これらの手順に従うことで、ローカルとリモートタスク間で共有メモリが効率的に設定され、簡単なデータ転送や複数の引数を必要とする関数の実行が可能になります。

追加のコードスニペット

メモリの割り当てと共有メモリオブジェクトの作成用:

mach_vm_allocate();
xpc_shmem_create();

リモートプロセス内で共有メモリオブジェクトを作成および修正するために:

malloc(); // for allocating memory remotely
thread_set_special_port(); // for inserting send right

5. 完全な制御の達成

共有メモリを正常に確立し、任意の実行機能を獲得した場合、基本的にはターゲットプロセス上で完全な制御を獲得したことになります。この制御を可能にする主要な機能は次のとおりです:

  1. 任意のメモリ操作

  • 共有領域からデータをコピーするためにmemcpy()を呼び出して任意のメモリ読み取りを実行します。

  • 共有領域にデータを転送するためにmemcpy()を使用して任意のメモリ書き込みを実行します。

  1. 複数の引数を持つ関数呼び出しの処理

  • 8つ以上の引数が必要な関数に対して、呼び出し規約に従ってスタック上に追加の引数を配置します。

  1. Machポートの転送

  • 事前に確立されたポートを介してMachメッセージを介してタスク間でMachポートを転送します。

  1. ファイルディスクリプタの転送

  • triple_fetchでIan Beerによって強調されたファイルポートを使用して、プロセス間でファイルディスクリプタを転送します。

この包括的な制御は、被害者プロセスとのやり取りのための詳細な実装とユーザーフレンドリーなAPIを提供するthreadexecライブラリによってカプセル化されています。

重要な考慮事項:

  • システムの安定性とデータの整合性を維持するために、メモリ読み取り/書き込み操作にmemcpy()を適切に使用してください。

  • Machポートやファイルディスクリプタを転送する際には、適切なプロトコルに従い、リソースを適切に処理して情報漏洩や意図しないアクセスを防止してください。

これらのガイドラインに従い、threadexecライブラリを利用することで、ターゲットプロセス上で完全な制御を効率的に管理し、やり取りすることができます。

参考文献

Last updated