macOS Process Abuse
プロセスの基本情報
プロセスは実行中の実行可能ファイルのインスタンスですが、プロセスはコードを実行しません。これらはスレッドです。したがって、プロセスは実行中のスレッドを提供するためのメモリ、ディスクリプタ、ポート、権限を提供するだけのコンテナです。
従来、プロセスは(PID 1を除く)他のプロセス内で開始され、fork
を呼び出すことで現在のプロセスの正確なコピーを作成し、その後子プロセスは通常、新しい実行可能ファイルをロードして実行するために**execve
を呼び出しました。その後、vfork
** が導入され、このプロセスをメモリコピーなしで高速化するようにしました。
その後、posix_spawn
が導入され、vfork
と execve
を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のNanoアロケータを使用_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()
を呼び出してこの値を取得する必要があり、それが起こるまで子プロセスはゾンビ状態になり、まだリストされていますがリソースを消費しません。
PID
PID(プロセス識別子)は一意のプロセスを識別します。XNUでは、PID は 64ビットで単調に増加し、巻き戻しは決して行われません(乱用を避けるため)。
プロセスグループ、セッション、Coalation
プロセスはそれらを扱いやすくするために グループ に挿入できます。たとえば、シェルスクリプト内のコマンドは同じプロセスグループになるため、例えば kill を使用して 一緒にシグナルを送信することができます。
また、プロセスをセッションにグループ化することもできます。プロセスがセッションを開始すると(setsid(2)
)、子プロセスはセッション内に設定されますが、独自のセッションを開始しない限り。
Coalation は、Darwinでプロセスをグループ化する別の方法です。Coalationに参加するプロセスは、プールリソースにアクセスしたり、レジャーを共有したり、Jetsamに直面したりすることができます。Coalationには異なる役割があります: リーダー、XPCサービス、エクステンション。
資格情報とペルソナ
各プロセスには、システムでの特権を識別する 資格情報 があります。各プロセスには1つの主要な uid
と1つの主要な gid
(ただし、複数のグループに属する場合があります)があります。
バイナリに setuid/setgid
ビットがある場合、ユーザーIDとグループIDを変更することも可能です。
新しい uid/gid を設定するためのいくつかの関数があります。
シスコール persona
は、別のセットの 資格情報 を提供します。ペルソナを採用すると、そのuid、gid、およびグループメンバーシップを 一度に 割り当てます。ソースコード で、構造体を見つけることができます。
スレッドの基本情報
POSIXスレッド(pthreads): macOSはC/C++向けの標準スレッドAPIであるPOSIXスレッド(
pthreads
)をサポートしています。macOSにおけるpthreadの実装は/usr/lib/system/libsystem_pthread.dylib
にあり、これは一般に利用可能なlibpthread
プロジェクトから来ています。このライブラリはスレッドの作成や管理に必要な関数を提供します。スレッドの作成: 新しいスレッドを作成するには
pthread_create()
関数が使用されます。内部的には、この関数はXNUカーネル(macOSが基づいているカーネル)に固有の低レベルシステムコールであるbsdthread_create()
を呼び出します。このシステムコールは、スケジューリングポリシーやスタックサイズなどのスレッドの動作を指定するpthread_attr
(属性)から派生したさまざまなフラグを取ります。
デフォルトのスタックサイズ: 新しいスレッドのデフォルトのスタックサイズは512 KBであり、通常の操作には十分ですが、必要に応じてスレッド属性を介してスペースを増減させることができます。
スレッドの初期化:
__pthread_init()
関数はスレッドのセットアップ中に重要であり、env[]
引数を利用してスタックの場所やサイズなどの環境変数を解析します。
macOSにおけるスレッドの終了
スレッドの終了: 通常、スレッドは
pthread_exit()
を呼び出すことで終了します。この関数により、スレッドはきれいに終了し、必要なクリーンアップが行われ、スレッドは参加者に戻り値を送信することができます。スレッドのクリーンアップ:
pthread_exit()
を呼び出すと、関数pthread_terminate()
が呼び出され、関連するすべてのスレッド構造の削除が処理されます。これにより、Machスレッドポート(MachはXNUカーネルの通信サブシステム)が解放され、スレッドに関連するカーネルレベルの構造が削除されるbsdthread_terminate
というシステムコールが呼び出されます。
同期メカニズム
共有リソースへのアクセスを管理し、競合状態を回避するために、macOSはいくつかの同期プリミティブを提供しています。これらは、データの整合性とシステムの安定性を確保するためにマルチスレッド環境で重要です。
ミューテックス:
通常のミューテックス(シグネチャ: 0x4D555458): メモリフットプリントが60バイト(ミューテックス56バイトとシグネチャ4バイト)の標準ミューテックス。
高速ミューテックス(シグネチャ: 0x4d55545A): 通常のミューテックスに類似していますが、より高速な操作に最適化されており、サイズも60バイトです。
条件変数:
特定の条件が発生するのを待つために使用され、サイズは44バイト(40バイトと4バイトのシグネチャ)です。
条件変数属性(シグネチャ: 0x434e4441): 条件変数の構成属性で、サイズは12バイトです。
一度だけ変数(シグネチャ: 0x4f4e4345):
初期化コードが一度だけ実行されることを保証します。サイズは12バイトです。
読み書きロック:
一度に複数のリーダーまたは1つのライターを許可し、共有データへの効率的なアクセスを可能にします。
読み書きロック(シグネチャ: 0x52574c4b): サイズは196バイトです。
読み書きロック属性(シグネチャ: 0x52574c41): 読み書きロックの属性で、サイズは20バイトです。
これらのオブジェクトの最後の4バイトはオーバーフローを検出するために使用されます。
スレッドローカル変数(TLV)
スレッドローカル変数(TLV)は、Mach-Oファイル(macOSの実行可能ファイルの形式)のコンテキストで、マルチスレッドアプリケーションにおいて各スレッドに固有の変数を宣言するために使用されます。これにより、各スレッドが変数の独自のインスタンスを持ち、ミューテックスなどの明示的な同期メカニズムを必要とせずに競合を回避し、データの整合性を維持できます。
C言語および関連言語では、**__thread
**キーワードを使用してスレッドローカル変数を宣言できます。以下は、例での使用方法です:
このスニペットでは、tlv_var
をスレッドローカル変数として定義しています。このコードを実行する各スレッドは独自のtlv_var
を持ち、1つのスレッドがtlv_var
を変更しても、他のスレッドのtlv_var
に影響を与えません。
Mach-Oバイナリでは、スレッドローカル変数に関連するデータは特定のセクションに整理されています:
__DATA.__thread_vars
: このセクションには、スレッドローカル変数に関するメタデータが含まれており、そのタイプや初期化状態などが含まれています。__DATA.__thread_bss
: このセクションは、明示的に初期化されていないスレッドローカル変数に使用されます。ゼロで初期化されたデータのために確保されたメモリの一部です。
Mach-Oは、スレッドが終了する際にスレッドローカル変数を管理するための特定のAPIである**tlv_atexit
も提供しています。このAPIを使用すると、スレッドが終了するときにスレッドローカルデータをクリーンアップするための特別な関数であるデストラクタを登録**することができます。
スレッドの優先度
スレッドの優先度を理解するには、オペレーティングシステムがどのスレッドを実行するか、いつ実行するかを見る必要があります。この決定は、各スレッドに割り当てられた優先度レベルに影響を受けます。macOSやUnix系システムでは、これはnice
、renice
、Quality of Service (QoS) クラスなどの概念を使用して処理されます。
NiceとRenice
Nice:
プロセスの
nice
値は、その優先度に影響を与える数値です。すべてのプロセスには、-20(最高の優先度)から19(最低の優先度)までのnice
値があります。プロセスが作成されたときのデフォルトのnice
値は通常0です。より低い
nice
値(-20に近い)はプロセスをより「利己的」にし、他のnice
値が高いプロセスよりもCPU時間を多く与えます。
Renice:
renice
は、既に実行中のプロセスのnice
値を変更するために使用されるコマンドです。これは、新しいnice
値に基づいてプロセスの優先度を動的に調整するために使用できます。たとえば、プロセスが一時的により多くのCPUリソースが必要な場合、
renice
を使用してそのnice
値を下げることができます。
Quality of Service (QoS) クラス
QoSクラスは、特に**Grand Central Dispatch (GCD)**をサポートするmacOSなどのシステムでスレッドの優先度を処理するためのより現代的なアプローチです。QoSクラスを使用すると、開発者は重要度や緊急度に基づいて作業を異なるレベルに分類できます。macOSはこれらのQoSクラスに基づいてスレッドの優先順位付けを自動的に管理します:
User Interactive:
このクラスは、ユーザーと対話しているタスクや即座の結果が必要なタスクに使用されます。これらのタスクは、インターフェースが応答性を維持するために最高の優先度が与えられます(たとえば、アニメーションやイベント処理)。
User Initiated:
ユーザーが開始し、即座の結果が期待されるタスク、例えばドキュメントを開くかボタンをクリックして計算が必要な場合などに使用されます。これらは高い優先度ですが、ユーザーインタラクティブより下です。
Utility:
これらのタスクは長時間実行され、通常は進行状況インジケーターが表示されます(たとえば、ファイルのダウンロード、データのインポート)。ユーザーが開始したタスクよりも優先度が低く、即座に完了する必要はありません。
Background:
このクラスは、バックグラウンドで動作し、ユーザーには見えないタスクに使用されます。これらはインデックス作成、同期、バックアップなどのタスクであり、最低の優先度でシステムパフォーマンスにほとんど影響を与えません。
QoSクラスを使用すると、開発者は正確な優先度の数値を管理する必要はなく、代わりにタスクの性質に焦点を当て、システムがCPUリソースを適切に最適化します。
さらに、スケジューリングポリシーには異なるスレッドスケジューリングポリシーがあり、スケジューラが考慮するスケジューリングパラメータのセットを指定するために使用できます。これはthread_policy_[set/get]
を使用して行うことができ、競合状態攻撃で役立つかもしれません。
Python Injection
環境変数**PYTHONINSPECT
が設定されている場合、pythonプロセスは終了時にpython cliに移行します。また、PYTHONSTARTUP
を使用して、対話セッションの開始時に実行するpythonスクリプトを指定することも可能です。
ただし、PYTHONINSPECT
が対話セッションを作成するときには、PYTHONSTARTUP
**スクリプトは実行されません。
**PYTHONPATH
やPYTHONHOME
**などの他の環境変数も、pythonコマンドが任意のコードを実行するのに役立つかもしれません。
**pyinstaller
**でコンパイルされた実行可能ファイルは、埋め込まれたpythonを使用していても、これらの環境変数を使用しません。
全体的に、環境変数を悪用してpythonが任意のコードを実行する方法を見つけることはできませんでした。\ ただし、ほとんどの人は**Hombrew**を使用してpythonをインストールするため、デフォルトの管理者ユーザーの**書き込み可能な場所**にpythonがインストールされます。次のような方法でそれを乗っ取ることができます: ```bash mv /opt/homebrew/bin/python3 /opt/homebrew/bin/python3.old cat > /opt/homebrew/bin/python3 <
Shield
Shield (Github) は、次のようなプロセスインジェクションアクションを検出およびブロックするオープンソースアプリケーションです:
環境変数の使用:次の環境変数の存在を監視します:
DYLD_INSERT_LIBRARIES
、CFNETWORK_LIBRARY_PATH
、RAWCAMERA_BUNDLE_PATH
、ELECTRON_RUN_AS_NODE
task_for_pid
の呼び出し:1つのプロセスが別のプロセスのタスクポートを取得しようとする場合を見つけ、プロセスにコードをインジェクトすることを可能にします。Electronアプリのパラメータ:誰かが**
--inspect
、--inspect-brk
、--remote-debugging-port
** のコマンドライン引数を使用してElectronアプリをデバッグモードで起動し、それにコードをインジェクトすることができます。シンボリックリンクまたはハードリンクの使用:一般的に最も一般的な悪用は、ユーザ権限でリンクを配置し、それをより高い権限の場所に向けることです。ハードリンクとシンボリックリンクの検出は非常に簡単です。リンクを作成するプロセスがターゲットファイルよりも異なる権限レベルを持っている場合、アラートを作成します。残念ながら、シンボリックリンクの場合、ブロックは不可能です。リンクの宛先に関する情報が作成前にはわからないためです。これはAppleのEndpointSecuriyフレームワークの制限です。
他のプロセスによる呼び出し
このブログ投稿 では、task_name_for_pid
関数を使用して、プロセスにコードをインジェクトする他のプロセスに関する情報を取得し、その他のプロセスに関する情報を取得する方法について説明しています。
その関数を呼び出すには、そのプロセスを実行しているユーザと同じuidである必要があります。またはrootである必要があります(情報を返すだけであり、コードをインジェクトする方法ではありません)。
参考文献
Last updated