macOS プロセスの悪用

Reading time: 26 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

プロセスの基本情報

プロセスは実行中の実行可能ファイルのインスタンスですが、プロセスはコードを実行しません。これらはスレッドです。したがって、プロセスは実行中のスレッドのコンテナに過ぎません。メモリ、ディスクリプタ、ポート、権限を提供します...

従来、プロセスは**forkを呼び出すことによって他のプロセス内で開始されました(PID 1を除く)。これにより、現在のプロセスの正確なコピーが作成され、その後子プロセスは一般的にexecveを呼び出して新しい実行可能ファイルをロードして実行します。その後、vforkが導入され、メモリコピーなしでこのプロセスを高速化しました。
次に、
posix_spawnが導入され、vforkexecve**を1回の呼び出しで組み合わせ、フラグを受け入れます:

  • POSIX_SPAWN_RESETIDS: 有効なIDを実際のIDにリセット
  • POSIX_SPAWN_SETPGROUP: プロセスグループの所属を設定
  • POSUX_SPAWN_SETSIGDEF: シグナルのデフォルト動作を設定
  • POSIX_SPAWN_SETSIGMASK: シグナルマスクを設定
  • POSIX_SPAWN_SETEXEC: 同じプロセスで実行(より多くのオプションを持つexecveのように)
  • POSIX_SPAWN_START_SUSPENDED: サスペンド状態で開始
  • _POSIX_SPAWN_DISABLE_ASLR: ASLRなしで開始
  • _POSIX_SPAWN_NANO_ALLOCATOR: libmallocのナノアロケータを使用
  • _POSIX_SPAWN_ALLOW_DATA_EXEC: データセグメントでrwxを許可
  • POSIX_SPAWN_CLOEXEC_DEFAULT: exec(2)でデフォルトで全てのファイル記述子を閉じる
  • _POSIX_SPAWN_HIGH_BITS_ASLR: ASLRスライドの高位ビットをランダム化

さらに、posix_spawnは生成されたプロセスのいくつかの側面を制御する**posix_spawnattrの配列を指定することを許可し、ディスクリプタの状態を変更するためのposix_spawn_file_actions**を提供します。

プロセスが終了すると、親プロセスに戻りコードを送信します(親が終了した場合、新しい親はPID 1)。親はwait4()またはwaitid()を呼び出してこの値を取得する必要があり、その間、子プロセスはリソースを消費しないゾンビ状態に留まります。

PIDs

PID、プロセス識別子は、ユニークなプロセスを識別します。XNUでは、PIDs64ビットで、単調に増加し、決してラップしません(悪用を避けるため)。

プロセスグループ、セッション & コアレーション

プロセスグループに挿入され、管理を容易にします。たとえば、シェルスクリプト内のコマンドは同じプロセスグループにあり、killを使用して一緒にシグナルを送信することが可能です。
プロセスをセッションにグループ化することも可能です。プロセスがセッションを開始すると(setsid(2))、子プロセスはセッション内に設定されますが、独自のセッションを開始しない限りです。

コアレーションは、Darwinでプロセスをグループ化する別の方法です。コアレーションに参加するプロセスは、プールリソースにアクセスでき、台帳を共有したり、Jetsamに直面したりします。コアレーションには異なる役割があります:リーダー、XPCサービス、拡張。

資格情報 & ペルソナ

各プロセスは、システム内の特権を識別するための資格情報を保持します。各プロセスには1つの主要なuidと1つの主要なgidがあります(ただし、複数のグループに属することがあります)。
バイナリがsetuid/setgidビットを持っている場合、ユーザーおよびグループIDを変更することも可能です。
新しいuid/gidを設定するための関数がいくつかあります。

システムコール**personaは、代替資格情報セットを提供します。ペルソナを採用すると、そのuid、gid、およびグループメンバーシップを一度に**引き受けます。ソースコードには、構造体が見つかります:

c
struct kpersona_info { uint32_t persona_info_version;
uid_t    persona_id; /* overlaps with UID */
int      persona_type;
gid_t    persona_gid;
uint32_t persona_ngroups;
gid_t    persona_groups[NGROUPS];
uid_t    persona_gmuid;
char     persona_name[MAXLOGNAME + 1];

/* TODO: MAC policies?! */
}

スレッドの基本情報

  1. POSIXスレッド (pthreads): macOSはPOSIXスレッド(pthreads)をサポートしており、これはC/C++の標準スレッドAPIの一部です。macOSにおけるpthreadsの実装は/usr/lib/system/libsystem_pthread.dylibにあり、これは公開されているlibpthreadプロジェクトから来ています。このライブラリはスレッドを作成し管理するために必要な関数を提供します。
  2. スレッドの作成: pthread_create()関数は新しいスレッドを作成するために使用されます。内部的に、この関数はXNUカーネル(macOSが基づいているカーネル)特有の低レベルのシステムコールであるbsdthread_create()を呼び出します。このシステムコールは、スレッドの動作を指定するpthread_attr(属性)から派生したさまざまなフラグを受け取ります。これにはスケジューリングポリシーやスタックサイズが含まれます。
  • デフォルトスタックサイズ: 新しいスレッドのデフォルトスタックサイズは512 KBで、通常の操作には十分ですが、必要に応じてスレッド属性を介して調整できます。
  1. スレッドの初期化: __pthread_init()関数はスレッドのセットアップ中に重要で、env[]引数を利用してスタックの位置やサイズに関する詳細を含む環境変数を解析します。

macOSにおけるスレッドの終了

  1. スレッドの終了: スレッドは通常、pthread_exit()を呼び出すことで終了します。この関数はスレッドがクリーンに終了し、必要なクリーンアップを行い、スレッドが戻り値を参加者に返すことを可能にします。
  2. スレッドのクリーンアップ: pthread_exit()を呼び出すと、pthread_terminate()関数が呼び出され、すべての関連スレッド構造の削除を処理します。これによりMachスレッドポートが解放され(MachはXNUカーネルの通信サブシステム)、スレッドに関連するカーネルレベルの構造を削除するシステムコールbsdthread_terminateが呼び出されます。

同期メカニズム

共有リソースへのアクセスを管理し、競合状態を避けるために、macOSはいくつかの同期プリミティブを提供します。これらはマルチスレッド環境においてデータの整合性とシステムの安定性を確保するために重要です:

  1. ミューテックス:
  • 通常のミューテックス (署名: 0x4D555458): メモリフットプリントが60バイト(ミューテックス56バイト、署名4バイト)の標準ミューテックス。
  • ファストミューテックス (署名: 0x4d55545A): 通常のミューテックスに似ていますが、より高速な操作のために最適化されており、サイズは60バイトです。
  1. 条件変数:
  • 特定の条件が発生するのを待つために使用され、サイズは44バイト(40バイトプラス4バイトの署名)。
  • 条件変数属性 (署名: 0x434e4441): 条件変数の設定属性で、サイズは12バイトです。
  1. ワンス変数 (署名: 0x4f4e4345):
  • 初期化コードが一度だけ実行されることを保証します。サイズは12バイトです。
  1. 読み書きロック:
  • 複数のリーダーまたは1つのライターを同時に許可し、共有データへの効率的なアクセスを促進します。
  • 読み書きロック (署名: 0x52574c4b): サイズは196バイトです。
  • 読み書きロック属性 (署名: 0x52574c41): 読み書きロックの属性で、サイズは20バイトです。

tip

これらのオブジェクトの最後の4バイトはオーバーフローを検出するために使用されます。

スレッドローカル変数 (TLV)

スレッドローカル変数 (TLV) は、Mach-Oファイル(macOSの実行可能ファイルの形式)の文脈で、マルチスレッドアプリケーション内の各スレッドに特有の変数を宣言するために使用されます。これにより、各スレッドが変数の独自のインスタンスを持ち、ミューテックスのような明示的な同期メカニズムを必要とせずに競合を回避し、データの整合性を維持する方法が提供されます。

Cおよび関連言語では、**__thread**キーワードを使用してスレッドローカル変数を宣言できます。以下は、あなたの例での動作方法です:

c
cCopy code__thread int tlv_var;

void main (int argc, char **argv){
tlv_var = 10;
}

このスニペットは tlv_var をスレッドローカル変数として定義しています。このコードを実行している各スレッドは独自の tlv_var を持ち、あるスレッドが tlv_var に加えた変更は他のスレッドの tlv_var に影響を与えません。

Mach-O バイナリでは、スレッドローカル変数に関連するデータが特定のセクションに整理されています:

  • __DATA.__thread_vars: このセクションには、スレッドローカル変数に関するメタデータが含まれており、変数の型や初期化状態などが記載されています。
  • __DATA.__thread_bss: このセクションは、明示的に初期化されていないスレッドローカル変数に使用されます。ゼロ初期化されたデータのために確保されたメモリの一部です。

Mach-O は、スレッドが終了する際にスレッドローカル変数を管理するための特定の API tlv_atexit も提供しています。この API を使用すると、スレッドが終了する際にスレッドローカルデータをクリーンアップする特別な関数である デストラクタ登録 できます。

スレッドの優先度

スレッドの優先度を理解するには、オペレーティングシステムがどのスレッドをいつ実行するかを決定する方法を見ていく必要があります。この決定は、各スレッドに割り当てられた優先度レベルによって影響を受けます。macOS および Unix 系システムでは、これを nicerenice、および Quality of Service (QoS) クラスのような概念を使用して処理します。

Nice と Renice

  1. Nice:
  • プロセスの nice 値は、その優先度に影響を与える数値です。すべてのプロセスには -20(最高優先度)から 19(最低優先度)までの範囲の nice 値があります。プロセスが作成されるときのデフォルトの nice 値は通常 0 です。
  • より低い nice 値(-20 に近い)は、プロセスをより「自己中心的」にし、他のプロセスよりも多くの CPU 時間を与えます。
  1. Renice:
  • renice は、すでに実行中のプロセスの nice 値を変更するために使用されるコマンドです。これを使用して、プロセスの優先度を動的に調整し、新しい nice 値に基づいて CPU 時間の割り当てを増減できます。
  • たとえば、プロセスが一時的により多くの CPU リソースを必要とする場合、renice を使用してその nice 値を下げることができます。

Quality of Service (QoS) クラス

QoS クラスは、特に Grand Central Dispatch (GCD) をサポートする macOS のようなシステムでスレッドの優先度を処理するためのより現代的なアプローチです。QoS クラスを使用すると、開発者は作業をその重要性や緊急性に基づいて異なるレベルに 分類 できます。macOS はこれらの QoS クラスに基づいてスレッドの優先度を自動的に管理します:

  1. ユーザーインタラクティブ:
  • このクラスは、現在ユーザーと対話しているタスクや、良好なユーザーエクスペリエンスを提供するために即時の結果を必要とするタスクに使用されます。これらのタスクは、インターフェースを応答性のあるものに保つために最高の優先度が与えられます(例:アニメーションやイベント処理)。
  1. ユーザー開始:
  • ユーザーが開始し、即時の結果を期待するタスク(例:ドキュメントを開く、計算を必要とするボタンをクリックするなど)。これらは高優先度ですが、ユーザーインタラクティブよりは低いです。
  1. ユーティリティ:
  • これらのタスクは長時間実行され、通常は進行状況インジケーターを表示します(例:ファイルのダウンロード、データのインポート)。これらはユーザー開始タスクよりも優先度が低く、即座に完了する必要はありません。
  1. バックグラウンド:
  • このクラスは、バックグラウンドで動作し、ユーザーには見えないタスクに使用されます。これには、インデックス作成、同期、バックアップなどのタスクが含まれます。これらは最低の優先度を持ち、システムパフォーマンスに最小限の影響を与えます。

QoS クラスを使用することで、開発者は正確な優先度番号を管理する必要がなく、タスクの性質に焦点を当てることができ、システムはそれに応じて CPU リソースを最適化します。

さらに、スレッドスケジューリングポリシーが異なり、スケジューラが考慮する一連のスケジューリングパラメータを指定することができます。これは thread_policy_[set/get] を使用して行うことができます。これはレースコンディション攻撃に役立つかもしれません。

MacOS プロセスの悪用

MacOS は、他のオペレーティングシステムと同様に、プロセスが相互作用し、通信し、データを共有するためのさまざまな方法とメカニズムを提供します。これらの技術は効率的なシステム機能に不可欠ですが、脅威アクターによって 悪意のある活動を行うために悪用される可能性もあります

ライブラリインジェクション

ライブラリインジェクションは、攻撃者が プロセスに悪意のあるライブラリをロードさせる 技術です。一度注入されると、ライブラリはターゲットプロセスのコンテキストで実行され、攻撃者はプロセスと同じ権限とアクセスを得ることができます。

macOS Library Injection

関数フッキング

関数フッキングは、ソフトウェアコード内の 関数呼び出し やメッセージを 傍受する ことを含みます。関数をフックすることで、攻撃者はプロセスの 動作を変更 したり、機密データを観察したり、実行フローを制御したりすることができます。

macOS Function Hooking

プロセス間通信

プロセス間通信 (IPC) は、別々のプロセスが データを共有し、交換する 方法を指します。IPC は多くの正当なアプリケーションにとって基本的ですが、プロセスの隔離を破ったり、機密情報を漏洩させたり、無許可のアクションを実行するために悪用される可能性もあります。

macOS IPC - Inter Process Communication

Electron アプリケーションのインジェクション

特定の環境変数で実行される Electron アプリケーションは、プロセスインジェクションに対して脆弱である可能性があります:

macOS Electron Applications Injection

Chromium インジェクション

--load-extension および --use-fake-ui-for-media-stream フラグを使用して ブラウザ内の攻撃 を実行し、キー入力、トラフィック、クッキーを盗んだり、ページにスクリプトを注入したりすることが可能です:

macOS Chromium Injection

ダーティ NIB

NIB ファイルは ユーザーインターフェース (UI) 要素 とその相互作用をアプリケーション内で定義します。しかし、これらは 任意のコマンドを実行する ことができ、Gatekeeper はすでに実行されたアプリケーションが NIB ファイルが変更されても 実行されるのを止めません。したがって、これらは任意のプログラムが任意のコマンドを実行するために使用される可能性があります:

macOS Dirty NIB

Java アプリケーションのインジェクション

特定の Java 機能(_JAVA_OPTS 環境変数など)を悪用して、Java アプリケーションが 任意のコード/コマンドを実行する ことが可能です。

macOS Java Applications Injection

.Net アプリケーションのインジェクション

.Net デバッグ機能 を悪用して、.Net アプリケーションにコードを注入することが可能です(macOS の保護(ランタイムハードニングなど)によって保護されていません)。

macOS .Net Applications Injection

Perl インジェクション

Perl スクリプトが任意のコードを実行するためのさまざまなオプションを確認してください:

macOS Perl Applications Injection

Ruby インジェクション

Ruby 環境変数を悪用して、任意のスクリプトが任意のコードを実行することも可能です:

macOS Ruby Applications Injection

Python インジェクション

環境変数 PYTHONINSPECT が設定されている場合、Python プロセスは終了後に Python CLI にドロップします。また、PYTHONSTARTUP を使用して、インタラクティブセッションの開始時に実行する Python スクリプトを指定することも可能です。
ただし、PYTHONINSPECT がインタラクティブセッションを作成する際には PYTHONSTARTUP スクリプトは実行されません。

他の環境変数(PYTHONPATHPYTHONHOME など)も、Python コマンドが任意のコードを実行するのに役立つ可能性があります。

pyinstaller でコンパイルされた実行可能ファイルは、埋め込まれた Python を使用して実行されていても、これらの環境変数を使用しないことに注意してください。

caution

全体として、環境変数を悪用して Python が任意のコードを実行する方法を見つけることができませんでした。
ただし、ほとんどの人は Hombrew を使用して Python をインストールし、デフォルトの管理者ユーザーのために 書き込み可能な場所 に Python をインストールします。次のようにハイジャックできます:

mv /opt/homebrew/bin/python3 /opt/homebrew/bin/python3.old
cat > /opt/homebrew/bin/python3 <<EOF
#!/bin/bash
# 追加のハイジャックコード
/opt/homebrew/bin/python3.old "$@"
EOF
chmod +x /opt/homebrew/bin/python3

これにより、root も Python を実行する際にこのコードを実行します。

検出

Shield

Shield (Github) は、プロセスインジェクション アクションを 検出し、ブロックする ことができるオープンソースアプリケーションです:

  • 環境変数を使用: 次の環境変数のいずれかの存在を監視します: DYLD_INSERT_LIBRARIESCFNETWORK_LIBRARY_PATHRAWCAMERA_BUNDLE_PATH および ELECTRON_RUN_AS_NODE
  • task_for_pid 呼び出しを使用: あるプロセスが他のプロセスの タスクポートを取得しようとする 時を見つけるために使用され、これによりプロセスにコードを注入できます。
  • Electron アプリのパラメータ: 誰かが --inspect--inspect-brk および --remote-debugging-port コマンドライン引数を使用して Electron アプリをデバッグモードで起動し、コードを注入することができます。
  • シンボリックリンク または ハードリンク を使用: 一般的な悪用は、ユーザー権限でリンクを配置し、より高い権限の場所を指す ことです。リンクを作成するプロセスがターゲットファイルとは 異なる権限レベル を持っている場合、アラート を作成します。残念ながら、シンボリックリンクの場合、作成前にリンクの宛先に関する情報がないため、ブロックは不可能です。これは Apple の EndpointSecuriy フレームワークの制限です。

他のプロセスによって行われた呼び出し

このブログ記事 では、関数 task_name_for_pid を使用して、他の プロセスがプロセスにコードを注入している 情報を取得し、その他のプロセスに関する情報を取得する方法を見つけることができます。

この関数を呼び出すには、プロセスを実行しているのと 同じ uid であるか、root である必要があります(この関数はプロセスに関する情報を返し、コードを注入する方法を返すわけではありません)。

参考文献

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする