mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/macos-hardening/macos-security-and-privilege-escalation
This commit is contained in:
parent
877afea666
commit
93d728e898
@ -9,47 +9,47 @@
|
||||
|
||||
## 1. Thread Hijacking
|
||||
|
||||
最初に、**`task_threads()`** 関数がタスクポートで呼び出され、リモートタスクからスレッドリストを取得します。ハイジャックするためのスレッドが選択されます。このアプローチは、`thread_create_running()`によって新しいリモートスレッドの作成が禁止されているため、従来のコードインジェクション手法とは異なります。
|
||||
最初に、`task_threads()`関数がタスクポートで呼び出され、リモートタスクからスレッドリストが取得されます。ハイジャックするためのスレッドが選択されます。このアプローチは、`thread_create_running()`をブロックする緩和策により、新しいリモートスレッドを作成することが禁止されているため、従来のコードインジェクション手法とは異なります。
|
||||
|
||||
スレッドを制御するために、**`thread_suspend()`** が呼び出され、その実行が停止します。
|
||||
スレッドを制御するために、`thread_suspend()`が呼び出され、その実行が停止されます。
|
||||
|
||||
リモートスレッドで許可される唯一の操作は、**停止**と**開始**、**レジスタ値の取得**と**変更**です。リモート関数呼び出しは、レジスタ `x0` から `x7` に**引数**を設定し、**`pc`** をターゲット関数に設定し、スレッドをアクティブにすることで開始されます。戻り値の後にスレッドがクラッシュしないようにするためには、戻りを検出する必要があります。
|
||||
リモートスレッドで許可される唯一の操作は、**停止**と**開始**、および**レジスタ値の取得**/**変更**です。リモート関数呼び出しは、レジスタ`x0`から`x7`を**引数**に設定し、`pc`をターゲット関数に設定してスレッドを再開することによって開始されます。戻り後にスレッドがクラッシュしないようにするためには、戻りを検出する必要があります。
|
||||
|
||||
1つの戦略は、`thread_set_exception_ports()`を使用してリモートスレッドのために**例外ハンドラを登録する**ことです。関数呼び出しの前に `lr` レジスタを無効なアドレスに設定します。これにより、関数実行後に例外がトリガーされ、例外ポートにメッセージが送信され、スレッドの状態を検査して戻り値を回復できるようになります。あるいは、Ian Beerのトリプルフェッチエクスプロイトから採用された方法として、`lr` を無限ループに設定します。スレッドのレジスタは、**`pc` がその命令を指すまで**継続的に監視されます。
|
||||
1つの戦略は、`thread_set_exception_ports()`を使用してリモートスレッドの**例外ハンドラ**を登録し、関数呼び出しの前に`lr`レジスタを無効なアドレスに設定することです。これにより、関数実行後に例外がトリガーされ、例外ポートにメッセージが送信され、スレッドの状態を検査して戻り値を回収できるようになります。あるいは、Ian Beerの*triple_fetch*エクスプロイトから採用された方法として、`lr`を無限ループに設定し、`pc`がその命令を指すまでスレッドのレジスタを継続的に監視します。
|
||||
|
||||
## 2. Mach ports for communication
|
||||
|
||||
次の段階では、リモートスレッドとの通信を促進するためにMachポートを確立します。これらのポートは、タスク間で任意の送信および受信権を転送するのに重要です。
|
||||
次の段階では、リモートスレッドとの通信を促進するためにMachポートを確立します。これらのポートは、タスク間で任意の送信/受信権を転送するのに重要です。
|
||||
|
||||
双方向通信のために、ローカルタスクとリモートタスクの2つのMach受信権が作成されます。その後、各ポートの送信権が対となるタスクに転送され、メッセージの交換が可能になります。
|
||||
双方向通信のために、ローカルタスクとリモートタスクのそれぞれに1つずつ、2つのMach受信権が作成されます。その後、各ポートの送信権が対となるタスクに転送され、メッセージの交換が可能になります。
|
||||
|
||||
ローカルポートに焦点を当てると、受信権はローカルタスクによって保持されます。ポートは `mach_port_allocate()` で作成されます。このポートに送信権をリモートタスクに転送することが課題となります。
|
||||
ローカルポートに焦点を当てると、受信権はローカルタスクによって保持されます。ポートは`mach_port_allocate()`で作成されます。このポートに送信権をリモートタスクに転送することが課題となります。
|
||||
|
||||
戦略の1つは、`thread_set_special_port()`を利用して、リモートスレッドの `THREAD_KERNEL_PORT` にローカルポートへの送信権を配置することです。その後、リモートスレッドに `mach_thread_self()` を呼び出して送信権を取得させます。
|
||||
戦略の1つは、`thread_set_special_port()`を利用して、リモートスレッドの`THREAD_KERNEL_PORT`にローカルポートへの送信権を配置することです。その後、リモートスレッドに`mach_thread_self()`を呼び出して送信権を取得させます。
|
||||
|
||||
リモートポートについては、プロセスが基本的に逆になります。リモートスレッドに `mach_reply_port()` を介してMachポートを生成させます(`mach_port_allocate()`はその返却メカニズムのため不適切です)。ポートが作成されると、リモートスレッド内で `mach_port_insert_right()` が呼び出され、送信権が確立されます。この権利はその後、`thread_set_special_port()`を使用してカーネルに保存されます。ローカルタスクに戻ると、`thread_get_special_port()`をリモートスレッドで使用して、リモートタスク内の新しく割り当てられたMachポートへの送信権を取得します。
|
||||
リモートポートについては、プロセスが基本的に逆になります。リモートスレッドに`mach_reply_port()`を介してMachポートを生成させます(`mach_port_allocate()`はその戻りメカニズムのため不適切です)。ポートが作成されると、リモートスレッド内で`mach_port_insert_right()`が呼び出され、送信権が確立されます。この権利はその後、`thread_set_special_port()`を使用してカーネルに保存されます。ローカルタスクに戻ると、`thread_get_special_port()`をリモートスレッドに対して使用して、リモートタスク内の新しく割り当てられたMachポートへの送信権を取得します。
|
||||
|
||||
これらのステップを完了すると、Machポートが確立され、双方向通信の基盤が整います。
|
||||
|
||||
## 3. Basic Memory Read/Write Primitives
|
||||
|
||||
このセクションでは、基本的なメモリの読み書きプリミティブを確立するために実行プリミティブを利用することに焦点を当てます。これらの初期ステップは、リモートプロセスに対するより多くの制御を得るために重要ですが、この段階でのプリミティブはあまり役に立ちません。すぐに、より高度なバージョンにアップグレードされます。
|
||||
このセクションでは、基本的なメモリの読み書きプリミティブを確立するためにexecuteプリミティブを利用することに焦点を当てます。これらの初期ステップは、リモートプロセスに対するより多くの制御を得るために重要ですが、この段階のプリミティブはあまり多くの目的には役立ちません。すぐに、より高度なバージョンにアップグレードされます。
|
||||
|
||||
### Memory Reading and Writing Using Execute Primitive
|
||||
### Memory reading and writing using the execute primitive
|
||||
|
||||
メモリの読み書きを特定の関数を使用して行うことが目標です。メモリを読み取るためには、以下の構造に似た関数が使用されます:
|
||||
メモリの読み書きを特定の関数を使用して行うことが目標です。**メモリの読み取り**:
|
||||
```c
|
||||
uint64_t read_func(uint64_t *address) {
|
||||
return *address;
|
||||
}
|
||||
```
|
||||
メモリへの書き込みには、この構造に似た関数が使用されます:
|
||||
**メモリの書き込み**:
|
||||
```c
|
||||
void write_func(uint64_t *address, uint64_t value) {
|
||||
*address = value;
|
||||
}
|
||||
```
|
||||
これらの関数は、指定されたアセンブリ命令に対応しています:
|
||||
これらの関数は次のアセンブリに対応しています:
|
||||
```
|
||||
_read_func:
|
||||
ldr x0, [x0]
|
||||
@ -62,100 +62,112 @@ ret
|
||||
|
||||
一般的なライブラリのスキャンにより、これらの操作に適した候補が明らかになりました:
|
||||
|
||||
1. **メモリの読み取り:**
|
||||
`property_getName()` 関数は、[Objective-C ランタイムライブラリ](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html)からメモリを読み取るための適切な関数として特定されました。関数の概要は以下の通りです:
|
||||
1. **メモリの読み取り — `property_getName()`** (libobjc):
|
||||
```c
|
||||
const char *property_getName(objc_property_t prop) {
|
||||
return prop->name;
|
||||
}
|
||||
```
|
||||
この関数は、`objc_property_t`の最初のフィールドを返すことによって、実質的に`read_func`のように機能します。
|
||||
|
||||
2. **メモリの書き込み:**
|
||||
メモリを書き込むための事前構築された関数を見つけることは、より困難です。しかし、libxpcの`_xpc_int64_set_value()`関数は、以下の逆アセンブルを持つ適切な候補です:
|
||||
2. **メモリの書き込み — `_xpc_int64_set_value()`** (libxpc):
|
||||
```c
|
||||
__xpc_int64_set_value:
|
||||
str x1, [x0, #0x18]
|
||||
ret
|
||||
```
|
||||
特定のアドレスに64ビットの書き込みを行うには、リモートコールは次のように構成されます:
|
||||
任意のアドレスに64ビットの書き込みを行うには:
|
||||
```c
|
||||
_xpc_int64_set_value(address - 0x18, value)
|
||||
_xpc_int64_set_value(address - 0x18, value);
|
||||
```
|
||||
これらのプリミティブが確立されると、共有メモリを作成するためのステージが整い、リモートプロセスの制御において重要な進展が見られます。
|
||||
|
||||
## 4. 共有メモリのセットアップ
|
||||
## 4. 共有メモリの設定
|
||||
|
||||
目的は、ローカルタスクとリモートタスク間で共有メモリを確立し、データ転送を簡素化し、複数の引数を持つ関数の呼び出しを容易にすることです。このアプローチは、`libxpc`とその`OS_xpc_shmem`オブジェクトタイプを活用し、Machメモリエントリに基づいています。
|
||||
目的は、ローカルタスクとリモートタスク間で共有メモリを確立し、データ転送を簡素化し、複数の引数を持つ関数の呼び出しを容易にすることです。このアプローチは、Machメモリエントリに基づいて構築された`libxpc`とその`OS_xpc_shmem`オブジェクトタイプを活用します。
|
||||
|
||||
### プロセスの概要:
|
||||
### プロセスの概要
|
||||
|
||||
1. **メモリの割り当て**:
|
||||
|
||||
- `mach_vm_allocate()`を使用して共有用のメモリを割り当てます。
|
||||
- `xpc_shmem_create()`を使用して、割り当てたメモリ領域のための`OS_xpc_shmem`オブジェクトを作成します。この関数は、Machメモリエントリの作成を管理し、`OS_xpc_shmem`オブジェクトのオフセット`0x18`にMach送信権を格納します。
|
||||
|
||||
2. **リモートプロセスでの共有メモリの作成**:
|
||||
|
||||
- リモートプロセスで`malloc()`へのリモート呼び出しを使用して`OS_xpc_shmem`オブジェクトのためのメモリを割り当てます。
|
||||
- ローカルの`OS_xpc_shmem`オブジェクトの内容をリモートプロセスにコピーします。ただし、この初期コピーはオフセット`0x18`で不正なMachメモリエントリ名を持っています。
|
||||
|
||||
3. **Machメモリエントリの修正**:
|
||||
|
||||
- `thread_set_special_port()`メソッドを利用して、リモートタスクにMachメモリエントリの送信権を挿入します。
|
||||
- リモートメモリエントリの名前でオフセット`0x18`のMachメモリエントリフィールドを上書きして修正します。
|
||||
|
||||
4. **共有メモリセットアップの最終化**:
|
||||
- リモートの`OS_xpc_shmem`オブジェクトを検証します。
|
||||
- `xpc_shmem_remote()`へのリモート呼び出しで共有メモリマッピングを確立します。
|
||||
|
||||
これらの手順に従うことで、ローカルタスクとリモートタスク間の共有メモリが効率的にセットアップされ、データ転送が簡単になり、複数の引数を必要とする関数の実行が可能になります。
|
||||
|
||||
## 追加のコードスニペット
|
||||
|
||||
メモリの割り当てと共有メモリオブジェクトの作成について:
|
||||
```c
|
||||
mach_vm_allocate();
|
||||
xpc_shmem_create();
|
||||
```
|
||||
リモートプロセスで共有メモリオブジェクトを作成および修正するには:
|
||||
```c
|
||||
malloc(); // for allocating memory remotely
|
||||
thread_set_special_port(); // for inserting send right
|
||||
```
|
||||
Machポートとメモリエントリ名の詳細を正しく処理して、共有メモリのセットアップが適切に機能するようにしてください。
|
||||
1. **メモリの割り当て**
|
||||
* `mach_vm_allocate()`を使用して共有用のメモリを割り当てます。
|
||||
* 割り当てた領域のために`xpc_shmem_create()`を使用して`OS_xpc_shmem`オブジェクトを作成します。
|
||||
2. **リモートプロセスでの共有メモリの作成**
|
||||
* リモートプロセス内で`OS_xpc_shmem`オブジェクトのためにメモリを割り当てます(`remote_malloc`)。
|
||||
* ローカルテンプレートオブジェクトをコピーします。埋め込まれたMach送信権のオフセット`0x18`の修正がまだ必要です。
|
||||
3. **Machメモリエントリの修正**
|
||||
* `thread_set_special_port()`を使用して送信権を挿入し、リモートエントリの名前で`0x18`フィールドを上書きします。
|
||||
4. **最終化**
|
||||
* リモートオブジェクトを検証し、`xpc_shmem_remote()`へのリモート呼び出しでマッピングします。
|
||||
|
||||
## 5. 完全な制御の達成
|
||||
|
||||
共有メモリを確立し、任意の実行能力を獲得することに成功すると、実質的にターゲットプロセスに対する完全な制御を得たことになります。この制御を可能にする主要な機能は次のとおりです。
|
||||
任意の実行と共有メモリのバックチャネルが利用可能になると、ターゲットプロセスを効果的に所有します:
|
||||
|
||||
1. **任意のメモリ操作**:
|
||||
* **任意のメモリR/W** — ローカルと共有領域間で`memcpy()`を使用します。
|
||||
* **8引数以上の関数呼び出し** — arm64呼び出し規約に従って、スタックに追加の引数を配置します。
|
||||
* **Machポートの転送** — 確立されたポートを介してMachメッセージ内で権利を渡します。
|
||||
* **ファイルディスクリプタの転送** — ファイルポートを活用します(*triple_fetch*を参照)。
|
||||
|
||||
- `memcpy()`を呼び出して共有領域からデータをコピーすることで、任意のメモリ読み取りを実行します。
|
||||
- `memcpy()`を使用して共有領域にデータを転送することで、任意のメモリ書き込みを実行します。
|
||||
これらすべては、再利用を容易にするために[`threadexec`](https://github.com/bazad/threadexec)ライブラリにラップされています。
|
||||
|
||||
2. **複数の引数を持つ関数呼び出しの処理**:
|
||||
---
|
||||
|
||||
- 8つ以上の引数を必要とする関数の場合、呼び出し規約に従って追加の引数をスタックに配置します。
|
||||
## 6. Apple Silicon (arm64e) のニュアンス
|
||||
|
||||
3. **Machポートの転送**:
|
||||
Apple Siliconデバイス(arm64e)では、**ポインタ認証コード(PAC)**がすべての戻りアドレスと多くの関数ポインタを保護します。*既存のコードを再利用する*スレッドハイジャック技術は、`lr`/`pc`内の元の値がすでに有効なPAC署名を持っているため、引き続き機能します。攻撃者が制御するメモリにジャンプしようとすると問題が発生します:
|
||||
|
||||
- 以前に確立されたポートを介してMachメッセージを通じてタスク間でMachポートを転送します。
|
||||
1. ターゲット内に実行可能なメモリを割り当てます(リモート`mach_vm_allocate` + `mprotect(PROT_EXEC)`)。
|
||||
2. ペイロードをコピーします。
|
||||
3. *リモート*プロセス内でポインタに署名します:
|
||||
```c
|
||||
uint64_t ptr = (uint64_t)payload;
|
||||
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
|
||||
```
|
||||
4. ハイジャックされたスレッド状態で `pc = ptr` を設定します。
|
||||
|
||||
4. **ファイルディスクリプタの転送**:
|
||||
- Ian Beerが`triple_fetch`で強調した技術を使用して、ファイルポートを介してプロセス間でファイルディスクリプタを転送します。
|
||||
または、既存のガジェット/関数をチェーンしてPAC準拠を維持します(従来のROP)。
|
||||
|
||||
この包括的な制御は、[threadexec](https://github.com/bazad/threadexec)ライブラリにカプセル化されており、被害者プロセスとのインタラクションのための詳細な実装とユーザーフレンドリーなAPIを提供します。
|
||||
## 7. 検出とエンドポイントセキュリティによる強化
|
||||
|
||||
## 重要な考慮事項:
|
||||
**EndpointSecurity (ES)** フレームワークは、ディフェンダーがスレッドインジェクションの試行を観察またはブロックできるカーネルイベントを公開します:
|
||||
|
||||
- システムの安定性とデータの整合性を維持するために、メモリの読み取り/書き込み操作に`memcpy()`を適切に使用してください。
|
||||
- Machポートやファイルディスクリプタを転送する際は、適切なプロトコルに従い、リソースを責任を持って処理して、リークや意図しないアクセスを防いでください。
|
||||
* `ES_EVENT_TYPE_AUTH_GET_TASK` – プロセスが別のタスクのポートを要求したときに発火します(例:`task_for_pid()`)。
|
||||
* `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` – 異なるタスクでスレッドが作成されるたびに発生します。
|
||||
* `ES_EVENT_TYPE_NOTIFY_THREAD_SET_STATE`(macOS 14 Sonomaで追加) – 既存のスレッドのレジスタ操作を示します。
|
||||
|
||||
これらのガイドラインに従い、`threadexec`ライブラリを利用することで、プロセスを効率的に管理し、細かいレベルでインタラクションを行い、ターゲットプロセスに対する完全な制御を達成できます。
|
||||
リモートスレッドイベントを印刷する最小限のSwiftクライアント:
|
||||
```swift
|
||||
import EndpointSecurity
|
||||
|
||||
## 参考文献
|
||||
let client = try! ESClient(subscriptions: [.notifyRemoteThreadCreate]) {
|
||||
(_, msg) in
|
||||
if let evt = msg.remoteThreadCreate {
|
||||
print("[ALERT] remote thread in pid \(evt.target.pid) by pid \(evt.thread.pid)")
|
||||
}
|
||||
}
|
||||
RunLoop.main.run()
|
||||
```
|
||||
**osquery** ≥ 5.8を使用したクエリ:
|
||||
```sql
|
||||
SELECT target_pid, source_pid, target_path
|
||||
FROM es_process_events
|
||||
WHERE event_type = 'REMOTE_THREAD_CREATE';
|
||||
```
|
||||
### Hardened-runtime considerations
|
||||
|
||||
アプリケーションを `com.apple.security.get-task-allow` 権限なしで配布することで、非ルート攻撃者がそのタスクポートを取得するのを防ぎます。システム整合性保護(SIP)は多くのAppleバイナリへのアクセスをブロックしますが、サードパーティ製ソフトウェアは明示的にオプトアウトする必要があります。
|
||||
|
||||
## 8. Recent Public Tooling (2023-2025)
|
||||
|
||||
| Tool | Year | Remarks |
|
||||
|------|------|---------|
|
||||
| [`task_vaccine`](https://github.com/rodionovd/task_vaccine) | 2023 | Ventura/SonomaでのPAC対応スレッドハイジャックを示すコンパクトなPoC |
|
||||
| `remote_thread_es` | 2024 | `REMOTE_THREAD_CREATE`イベントを表示するためにいくつかのEDRベンダーによって使用されるEndpointSecurityヘルパー |
|
||||
|
||||
> これらのプロジェクトのソースコードを読むことは、macOS 13/14で導入されたAPIの変更を理解し、Intel ↔ Apple Silicon間での互換性を保つのに役立ちます。
|
||||
|
||||
## References
|
||||
|
||||
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
|
||||
- [https://github.com/rodionovd/task_vaccine](https://github.com/rodionovd/task_vaccine)
|
||||
- [https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create](https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user