macOS IPC - Inter Process Communication
Last updated
Last updated
Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Mach gebruik take as die kleinste eenheid vir die deel van hulpbronne, en elke taak kan meervoudige drade bevat. Hierdie take en drade is 1:1 gekarteer na POSIX prosesse en drade.
Kommunikasie tussen take vind plaas via Mach Inter-Process Communication (IPC), wat eenrigting kommunikasiekanale benut. Boodskappe word tussen porte oorgedra, wat optree as 'n soort boodskap rye wat deur die kernel bestuur word.
'n Port is die basiese element van Mach IPC. Dit kan gebruik word om boodskappe te stuur en om hulle te ontvang.
Elke proses het 'n IPC tabel, waar dit moontlik is om die mach porte van die proses te vind. Die naam van 'n mach port is eintlik 'n nommer (naanwyser na die kernel objek).
'n Proses kan ook 'n portnaam met sekere regte na 'n ander taak stuur en die kernel sal hierdie inskrywing in die IPC tabel van die ander taak laat verskyn.
Port regte, wat definieer watter operasies 'n taak kan uitvoer, is sleutel tot hierdie kommunikasie. Die moontlike port regte is (definisies hier vandaan):
Ontvang reg, wat toelaat om boodskappe wat na die port gestuur word, te ontvang. Mach porte is MPSC (meervoudige-produsent, enkele-verbruiker) rye, wat beteken dat daar slegs een ontvang reg vir elke port in die hele stelsel mag wees (in teenstelling met pype, waar meervoudige prosesse almal file descriptors na die leeskant van een pyp kan hou).
'n taak met die Ontvang reg kan boodskappe ontvang en Stuur regte skep, wat dit toelaat om boodskappe te stuur. Oorspronklik het slegs die eie taak die Ontvang reg oor sy port.
As die eienaar van die Ontvang reg sterf of dit doodmaak, het die stuur reg nutteloos geword (dode naam).
Stuur reg, wat toelaat om boodskappe na die port te stuur.
Die Stuur reg kan gekloneer word sodat 'n taak wat 'n Stuur reg besit, die reg kan kloneer en aan 'n derde taak kan toeken.
Let daarop dat port regte ook oorgegee kan word deur Mac boodskappe.
Stuur-een keer reg, wat toelaat om een boodskap na die port te stuur en dan verdwyn.
Hierdie reg kan nie gekloneer word nie, maar dit kan verplaas word.
Port stel reg, wat 'n port stel aandui eerder as 'n enkele port. Om 'n boodskap van 'n port stel te verwyder, verwyder 'n boodskap van een van die porte wat dit bevat. Port stelle kan gebruik word om op verskeie porte gelyktydig te luister, baie soos select
/poll
/epoll
/kqueue
in Unix.
Dode naam, wat nie 'n werklike port reg is nie, maar bloot 'n plekhouer. Wanneer 'n port vernietig word, draai al bestaande port regte na die port in dode name.
Take kan STUUR regte aan ander oordra, wat hulle in staat stel om boodskappe terug te stuur. STUUR regte kan ook geklonen word, sodat 'n taak die reg kan dupliceer en aan 'n derde taak kan gee. Dit, saam met 'n intermediêre proses bekend as die bootstrap server, stel effektiewe kommunikasie tussen take in staat.
Lêer porte laat toe om lêer descriptors in Mac porte te enkapsuleer (met behulp van Mach port regte). Dit is moontlik om 'n fileport
van 'n gegewe FD te skep met fileport_makeport
en 'n FD van 'n fileport te skep met fileport_makefd
.
Soos vroeër genoem, is dit moontlik om regte te stuur met behulp van Mach boodskappe, egter, jy kan nie 'n reg stuur sonder om reeds 'n reg te hê om 'n Mach boodskap te stuur nie. So, hoe word die eerste kommunikasie gevestig?
Vir hierdie, is die bootstrap server (launchd in mac) betrokke, aangesien enigiemand 'n STUUR reg na die bootstrap server kan kry, is dit moontlik om dit te vra vir 'n reg om 'n boodskap na 'n ander proses te stuur:
Taak A skep 'n nuwe port, en verkry die ONTVANG reg oor dit.
Taak A, as die houer van die ONTVANG reg, genereer 'n STUUR reg vir die port.
Taak A vestig 'n verbinding met die bootstrap server, en stuur dit die STUUR reg vir die port wat dit aan die begin gegenereer het.
Onthou dat enigiemand 'n STUUR reg na die bootstrap server kan kry.
Taak A stuur 'n bootstrap_register
boodskap na die bootstrap server om die gegewe port met 'n naam te assosieer soos com.apple.taska
Taak B interaksie met die bootstrap server om 'n bootstrap lookup vir die diens naam (bootstrap_lookup
) uit te voer. So die bootstrap server kan antwoordgee, taak B sal 'n STUUR reg na 'n port wat dit voorheen geskep het binne die lookup boodskap stuur. As die lookup suksesvol is, sal die server die STUUR reg wat van Taak A ontvang is, dupliceer en aan Taak B oordra.
Onthou dat enigiemand 'n STUUR reg na die bootstrap server kan kry.
Met hierdie STUUR reg, is Taak B in staat om 'n boodskap na Taak A te stuur.
Vir 'n bi-rigting kommunikasie genereer taak B gewoonlik 'n nuwe port met 'n ONTVANG reg en 'n STUUR reg, en gee die STUUR reg aan Taak A sodat dit boodskappe na TAak B kan stuur (bi-rigting kommunikasie).
Die bootstrap server kan nie die diens naam wat deur 'n taak geclaim word, verifieer nie. Dit beteken 'n taak kan potensieel enige stelseltaak naboots, soos valslik 'n magtiging diens naam te claim en dan elke versoek goedkeur.
Dan, stoor Apple die name van stelsel-gelewerde dienste in veilige konfigurasie lêers, geleë in SIP-beskermde directories: /System/Library/LaunchDaemons
en /System/Library/LaunchAgents
. Saam met elke diens naam, word die geassosieerde binêre ook gestoor. Die bootstrap server sal 'n ONTVANG reg vir elkeen van hierdie diens name skep en hou.
Vir hierdie vooraf gedefinieerde dienste, verskil die lookup proses effens. Wanneer 'n diens naam opgevraag word, begin launchd die diens dinamies. Die nuwe werksvloei is soos volg:
Taak B begin 'n bootstrap lookup vir 'n diens naam.
launchd kyk of die taak aan die gang is en as dit nie is nie, begin dit.
Taak A (die diens) voer 'n bootstrap check-in (bootstrap_check_in()
) uit. Hier, die bootstrap server skep 'n STUUR reg, hou dit, en oordra die ONTVANG reg aan Taak A.
launchd dupliceer die STUUR reg en stuur dit aan Taak B.
Taak B genereer 'n nuwe port met 'n ONTVANG reg en 'n STUUR reg, en gee die STUUR reg aan Taak A (die svc) sodat dit boodskappe na TAak B kan stuur (bi-rigting kommunikasie).
Echter, hierdie proses geld slegs vir vooraf gedefinieerde stelseltaake. Nie-stelseltaake werk steeds soos oorspronklik beskryf, wat potensieel nabootsing kan toelaat.
Daarom, launchd moet nooit crash nie of die hele stelsel sal crash.
Die mach_msg
funksie, wat essensieel 'n stelselaanroep is, word gebruik om Mach boodskappe te stuur en te ontvang. Die funksie vereis dat die boodskap wat gestuur moet word, as die aanvanklike argument. Hierdie boodskap moet begin met 'n mach_msg_header_t
struktuur, gevolg deur die werklike boodskapinhoud. Die struktuur is soos volg gedefinieer:
Processes wat 'n ontvangsreg besit, kan boodskappe op 'n Mach-poort ontvang. Omgekeerd, die stuurders word 'n stuur of 'n stuur-eens reg toegeken. Die stuur-eens reg is uitsluitlik vir die stuur van 'n enkele boodskap, waarna dit ongeldig word.
Die aanvanklike veld msgh_bits
is 'n bitmap:
Eerste bit (mees betekenisvolle) word gebruik om aan te dui dat 'n boodskap kompleks is (meer hieroor hieronder)
Die 3de en 4de word deur die kern gebruik
Die 5 minste betekenisvolle bits van die 2de byte kan gebruik word vir voucher: 'n ander tipe poort om sleutel/waarde kombinasies te stuur.
Die 5 minste betekenisvolle bits van die 3de byte kan gebruik word vir lokale poort
Die 5 minste betekenisvolle bits van die 4de byte kan gebruik word vir afgeleë poort
Die tipes wat in die voucher, lokale en afgeleë poorte gespesifiseer kan word, is (van mach/message.h):
For example, MACH_MSG_TYPE_MAKE_SEND_ONCE
kan gebruik word om te aandui dat 'n send-once regte afgelei en oorgedra moet word vir hierdie poort. Dit kan ook gespesifiseer word as MACH_PORT_NULL
om te voorkom dat die ontvanger kan antwoordgee.
Om 'n maklike bi-rigting kommunikasie te bereik, kan 'n proses 'n mach poort in die mach boodskap kop spesifiseer wat die antwoord poort (msgh_local_port
) genoem word, waar die ontvanger van die boodskap 'n antwoord op hierdie boodskap kan stuur.
Let daarop dat hierdie soort bi-rigting kommunikasie gebruik word in XPC boodskappe wat 'n herhaling verwag (xpc_connection_send_message_with_reply
en xpc_connection_send_message_with_reply_sync
). Maar gewoonlik word verskillende poorte geskep soos voorheen verduidelik om die bi-rigting kommunikasie te skep.
Die ander velde van die boodskap kop is:
msgh_size
: die grootte van die hele pakket.
msgh_remote_port
: die poort waarop hierdie boodskap gestuur word.
msgh_voucher_port
: mach vouchers.
msgh_id
: die ID van hierdie boodskap, wat deur die ontvanger geïnterpreteer word.
Let daarop dat mach boodskappe oor 'n mach poort
gestuur word, wat 'n enkele ontvanger, meervoudige sender kommunikasiekanaal is wat in die mach-kern ingebou is. Meervoudige prosesse kan boodskappe na 'n mach poort stuur, maar op enige tydstip kan slegs 'n enkele proses lees daarvan.
Boodskappe word dan gevorm deur die mach_msg_header_t
kop gevolg deur die liggaam en deur die trailer (indien enige) en dit kan toestemming gee om daarop te antwoord. In hierdie gevalle hoef die kern net die boodskap van een taak na die ander oor te dra.
'n Trailer is inligting wat deur die kern aan die boodskap bygevoeg word (kan nie deur die gebruiker gestel word nie) wat aangevra kan word in boodskapontvangs met die vlae MACH_RCV_TRAILER_<trailer_opt>
(daar is verskillende inligting wat aangevra kan word).
Daar is egter ander meer kompleks boodskappe, soos dié wat addisionele poortregte of gedeelde geheue oordra, waar die kern ook hierdie voorwerpe na die ontvanger moet stuur. In hierdie gevalle word die mees betekenisvolle bit van die kop msgh_bits
gestel.
Die moontlike beskrywings om oor te dra word gedefinieer in mach/message.h
:
In 32-bits is al die beskrywings 12B en die beskrywing tipe is in die 11de een. In 64-bits varieer die groottes.
Die kernel sal die beskrywings van een taak na die ander kopieer, maar eers 'n kopie in kernelgeheue skep. Hierdie tegniek, bekend as "Feng Shui", is in verskeie eksploitte misbruik om die kernel data in sy geheue te kopieer, wat 'n proses in staat stel om beskrywings na homself te stuur. Dan kan die proses die boodskappe ontvang (die kernel sal hulle vrylaat).
Dit is ook moontlik om poortregte na 'n kwesbare proses te stuur, en die poortregte sal net in die proses verskyn (selfs al hanteer hy hulle nie).
Let daarop dat poorte aan die taaknaamruimte geassosieer is, so om 'n poort te skep of te soek, word die taaknaamruimte ook gevra (meer in mach/mach_port.h
):
mach_port_allocate
| mach_port_construct
: Skep 'n poort.
mach_port_allocate
kan ook 'n poortstel skep: ontvangreg oor 'n groep poorte. Wanneer 'n boodskap ontvang word, word die poort aangedui waarvandaan dit gekom het.
mach_port_allocate_name
: Verander die naam van die poort (per standaard 32-bis heelgetal)
mach_port_names
: Kry poortname van 'n teiken
mach_port_type
: Kry regte van 'n taak oor 'n naam
mach_port_rename
: Hernoem 'n poort (soos dup2 vir FD's)
mach_port_allocate
: Toekenning van 'n nuwe ONTVANG, POORT_STEL of DOOD_NAAM
mach_port_insert_right
: Skep 'n nuwe reg in 'n poort waar jy ONTVANG het
mach_port_...
mach_msg
| mach_msg_overwrite
: Funksies wat gebruik word om mach boodskappe te stuur en te ontvang. Die oorskrywing weergawe laat jou toe om 'n ander buffer vir boodskapontvangs aan te dui (die ander weergawe sal dit net hergebruik).
Aangesien die funksies mach_msg
en mach_msg_overwrite
diegene is wat gebruik word om ontvang boodskappe te stuur, sal die instelling van 'n breekpunt op hulle jou in staat stel om die gestuurde en ontvangde boodskappe te inspekteer.
Byvoorbeeld, begin om enige toepassing te debug wat jy kan debug, aangesien dit libSystem.B
sal laai wat hierdie funksie sal gebruik.
Om die argumente van mach_msg
te kry, kyk na die registers. Dit is die argumente (van mach/message.h):
Kry die waardes van die registers:
Inspecteer die boodskapkop wat die eerste argument nagaan:
Die tipe van mach_msg_bits_t
is baie algemeen om 'n antwoord toe te laat.
Die naam is die standaardnaam wat aan die poort gegee word (kyk hoe dit toeneem in die eerste 3 bytes). Die ipc-object
is die obfuscated unieke identifiseerder van die poort.
Let ook op hoe die poorte met slegs send
regte die eienaar daarvan identifiseer (poortnaam + pid).
Let ook op die gebruik van +
om ander take wat aan dieselfde poort gekoppel is aan te dui.
Dit is ook moontlik om procesxp te gebruik om ook die ** geregistreerde diensname** te sien (met SIP gedeaktiveer weens die behoefte aan com.apple.system-task-port
):
U kan hierdie hulpmiddel op iOS installeer deur dit van http://newosxbook.com/tools/binpack64-256.tar.gz af te laai.
Let op hoe die sender 'n poort toewys, 'n send right vir die naam org.darlinghq.example
skep en dit na die bootstrap server stuur terwyl die sender om die send right van daardie naam gevra het en dit gebruik het om 'n boodskap te stuur.
Daar is 'n paar spesiale poorte wat toelaat om sekere sensitiewe aksies uit te voer of sekere sensitiewe data te bekom in die geval dat 'n taak die SEND regte oor hulle het. Dit maak hierdie poorte baie interessant vanuit 'n aanvaller se perspektief, nie net vanweë die vermoëns nie, maar omdat dit moontlik is om SEND regte oor take te deel.
Hierdie poorte word deur 'n nommer verteenwoordig.
SEND regte kan verkry word deur host_get_special_port
aan te roep en RECEIVE regte deur host_set_special_port
aan te roep. Beide oproepe vereis egter die host_priv
poort waartoe slegs root toegang het. Boonop was root in die verlede in staat om host_set_special_port
aan te roep en arbitrêre te kapen wat byvoorbeeld toegelaat het om kodehandtekeninge te omseil deur HOST_KEXTD_PORT
te kapen (SIP voorkom dit nou).
Hierdie is in 2 groepe verdeel: Die eerste 7 poorte behoort aan die kern wat die 1 HOST_PORT
, die 2 HOST_PRIV_PORT
, die 3 HOST_IO_MASTER_PORT
en die 7 is HOST_MAX_SPECIAL_KERNEL_PORT
.
Diegene wat begin met die nommer 8 is besit deur stelseldemons en hulle kan in host_special_ports.h
verklaar word.
Gasheerpoort: As 'n proses SEND voorreg oor hierdie poort het, kan hy inligting oor die stelsel verkry deur sy roetines aan te roep soos:
host_processor_info
: Kry prosessorinligting
host_info
: Kry gasheerinligting
host_virtual_physical_table_info
: Virtuele/Fisiese bladsy tabel (vereis MACH_VMDEBUG)
host_statistics
: Kry gasheerstatistieke
mach_memory_info
: Kry kerngeheue uitleg
Gasheer Priv poort: 'n Proses met SEND reg oor hierdie poort kan bevoorregte aksies uitvoer soos om opstartdata te wys of te probeer om 'n kernuitbreiding te laai. Die proses moet root wees om hierdie toestemming te verkry.
Boonop, om die kext_request
API aan te roep, is dit nodig om ander regte com.apple.private.kext*
te hê wat slegs aan Apple-binaries gegee word.
Ander roetines wat aangeroep kan word, is:
host_get_boot_info
: Kry machine_boot_info()
host_priv_statistics
: Kry bevoorregte statistieke
vm_allocate_cpm
: Toewys Aaneengeskakelde Fisiese Geheue
host_processors
: Stuur reg na gasheerprosessoren
mach_vm_wire
: Maak geheue resident
Aangesien root toegang tot hierdie toestemming kan verkry, kan dit host_set_[special/exception]_port[s]
aanroep om gasheer spesiale of uitsondering poorte te kapen.
Dit is moontlik om alle gasheer spesiale poorte te sien deur te loop:
Hierdie is poorte wat gereserveer is vir bekende dienste. Dit is moontlik om hulle te kry/instel deur task_[get/set]_special_port
aan te roep. Hulle kan gevind word in task_special_ports.h
:
From here:
TASK_KERNEL_PORT[task-self send right]: Die poort wat gebruik word om hierdie taak te beheer. Gebruik om boodskappe te stuur wat die taak beïnvloed. Dit is die poort wat teruggegee word deur mach_task_self (sien Taak Poorte hieronder).
TASK_BOOTSTRAP_PORT[bootstrap send right]: Die taak se bootstrap poort. Gebruik om boodskappe te stuur wat die terugkeer van ander stelseldienspoorte versoek.
TASK_HOST_NAME_PORT[host-self send right]: Die poort wat gebruik word om inligting van die bevatende gasheer aan te vra. Dit is die poort wat teruggegee word deur mach_host_self.
TASK_WIRED_LEDGER_PORT[ledger send right]: Die poort wat die bron benoem waaruit hierdie taak sy bedrade kerngeheue trek.
TASK_PAGED_LEDGER_PORT[ledger send right]: Die poort wat die bron benoem waaruit hierdie taak sy standaard geheue bestuurde geheue trek.
Oorspronklik het Mach nie "prosesse" gehad nie, dit het "take" gehad wat meer soos 'n houer van drade beskou is. Toe Mach met BSD saamgevoeg is, was elke taak gekorreleerd met 'n BSD-proses. Daarom het elke BSD-proses die besonderhede wat dit nodig het om 'n proses te wees en elke Mach-taak het ook sy interne werking (behalwe vir die nie-bestaande pid 0 wat die kernel_task
is).
Daar is twee baie interessante funksies wat hiermee verband hou:
task_for_pid(target_task_port, pid, &task_port_of_pid)
: Kry 'n SEND-regte vir die taakpoort van die taak wat verband hou met die spesifieke pid
en gee dit aan die aangeduide target_task_port
(wat gewoonlik die oproepende taak is wat mach_task_self()
gebruik het, maar kan 'n SEND-poort oor 'n ander taak wees).
pid_for_task(task, &pid)
: Gegee 'n SEND-regte aan 'n taak, vind uit watter PID hierdie taak verband hou.
Om aksies binne die taak uit te voer, het die taak 'n SEND
-regte na homself nodig gehad deur mach_task_self()
aan te roep (wat die task_self_trap
(28) gebruik). Met hierdie toestemming kan 'n taak verskeie aksies uitvoer soos:
task_threads
: Kry SEND-regte oor alle taakpoorte van die drade van die taak
task_info
: Kry inligting oor 'n taak
task_suspend/resume
: Suspend of hervat 'n taak
task_[get/set]_special_port
thread_create
: Skep 'n draad
task_[get/set]_state
: Beheer taaktoestand
en meer kan gevind word in mach/task.h
Let daarop dat met 'n SEND-regte oor 'n taakpoort van 'n ander taak, dit moontlik is om sulke aksies oor 'n ander taak uit te voer.
Boonop is die task_port ook die vm_map
poort wat toelaat om geheue te lees en te manipuleer binne 'n taak met funksies soos vm_read()
en vm_write()
. Dit beteken basies dat 'n taak met SEND-regte oor die task_port van 'n ander taak in staat gaan wees om kode in daardie taak in te spuit.
Onthou dat omdat die kern ook 'n taak is, as iemand daarin slaag om 'n SEND-toestemmings oor die kernel_task
te verkry, sal dit in staat wees om die kern enigiets te laat uitvoer (jailbreaks).
Roep mach_task_self()
aan om die naam vir hierdie poort vir die oproepende taak te kry. Hierdie poort word slegs geërf oor exec()
; 'n nuwe taak wat met fork()
geskep word, kry 'n nuwe taakpoort (as 'n spesiale geval, kry 'n taak ook 'n nuwe taakpoort na exec()
in 'n suid-binary). Die enigste manier om 'n taak te spawn en sy poort te kry, is om die "poortruil dans" uit te voer terwyl jy fork()
doen.
Dit is die beperkings om toegang tot die poort te verkry (van macos_task_policy
van die binêre AppleMobileFileIntegrity
):
As die app com.apple.security.get-task-allow
regte het, kan prosesse van die dieselfde gebruiker toegang tot die taakpoort verkry (gewoonlik deur Xcode vir debugging bygevoeg). Die notarization proses sal dit nie toelaat vir produksievrystellings nie.
Apps met die com.apple.system-task-ports
regte kan die taakpoort vir enige proses kry, behalwe die kern. In ouer weergawes is dit task_for_pid-allow
genoem. Dit word slegs aan Apple-toepassings toegestaan.
Root kan toegang tot taakpoorte van toepassings nie saamgecompileer met 'n versterkte runtime (en nie van Apple nie).
Die taaknaampoort: 'n Onbevoegde weergawe van die taakpoort. Dit verwys na die taak, maar laat nie toe om dit te beheer nie. Die enigste ding wat blykbaar deur dit beskikbaar is, is task_info()
.
Drade het ook geassosieerde poorte, wat sigbaar is vanaf die taak wat task_threads
aanroep en vanaf die verwerker met processor_set_threads
. 'n SEND-regte na die draadpoort laat toe om die funksie van die thread_act
subsysteem te gebruik, soos:
thread_terminate
thread_[get/set]_state
act_[get/set]_state
thread_[suspend/resume]
thread_info
...
Enige draad kan hierdie poort kry deur mach_thread_sef
aan te roep.
Jy kan 'n shellcode gryp van:
Introduction to ARM64v8Compileer die vorige program en voeg die regte by om kode met dieselfde gebruiker in te spuit (as nie, sal jy sudo moet gebruik).