macOS IPC - Inter Process Communication
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Mach koristi zadace kao najmanju jedinicu za deljenje resursa, a svaka zadaca može sadržati više niti. Ove zadace i niti su mapirane 1:1 na POSIX procese i niti.
Komunikacija između zadataka se odvija putem Mach međuprocesne komunikacije (IPC), koristeći jednosmerne komunikacione kanale. Poruke se prenose između portova, koji deluju kao redovi poruka kojima upravlja kernel.
Port je osnovni element Mach IPC-a. Može se koristiti za slanje poruka i njihovo primanje.
Svaki proces ima IPC tabelu, u kojoj je moguće pronaći mach portove procesa. Ime mach porta je zapravo broj (pokazivač na kernel objekat).
Proces takođe može poslati ime porta sa nekim pravima drugoj zadaci i kernel će napraviti ovaj unos u IPC tabeli druge zadatke.
Prava portova, koja definišu koje operacije zadatak može izvesti, ključna su za ovu komunikaciju. Moguća prava portova su (definicije ovde):
Pravo primanja, koje omogućava primanje poruka poslatih na port. Mach portovi su MPSC (više proizvođača, jedan potrošač) redovi, što znači da može postojati samo jedno pravo primanja za svaki port u celom sistemu (za razliku od cevi, gde više procesa može držati deskriptore datoteka za kraj čitanja jedne cevi).
Zadaca sa pravom primanja može primati poruke i kreirati prava slanja, omogućavajući joj da šalje poruke. Prvobitno samo vlastita zadaca ima pravo primanja nad svojim portom.
Ako vlasnik prava primanja umre ili ga ubije, pravo slanja postaje beskorisno (mrtvo ime).
Pravo slanja, koje omogućava slanje poruka na port.
Pravo slanja može biti klonirano tako da zadaca koja poseduje pravo slanja može klonirati pravo i dodeliti ga trećoj zadaci.
Imajte na umu da se prava portova takođe mogu proslediti putem Mach poruka.
Pravo slanja jednom, koje omogućava slanje jedne poruke na port i zatim nestaje.
Ovo pravo ne može biti klonirano, ali može biti premesto.
Pravo skupa portova, koje označava skup portova umesto jednog porta. Uklanjanje poruke iz skupa portova uklanja poruku iz jednog od portova koje sadrži. Skupovi portova mogu se koristiti za slušanje na nekoliko portova istovremeno, slično kao select
/poll
/epoll
/kqueue
u Unix-u.
Mrtvo ime, koje nije stvarno pravo porta, već samo mesto za rezervaciju. Kada se port uništi, sva postojeća prava portova na port postaju mrtva imena.
Zadace mogu prenositi PRAVA SLANJA drugima, omogućavajući im da šalju poruke nazad. PRAVA SLANJA se takođe mogu klonirati, tako da zadaca može duplicirati i dati pravo trećoj zadaci. Ovo, u kombinaciji sa posredničkim procesom poznatim kao bootstrap server, omogućava efikasnu komunikaciju između zadataka.
File portovi omogućavaju enkapsulaciju deskriptora datoteka u Mac portovima (koristeći prava Mach portova). Moguće je kreirati fileport
iz datog FD koristeći fileport_makeport
i kreirati FD iz fileporta koristeći fileport_makefd
.
Kao što je ranije pomenuto, moguće je slati prava koristeći Mach poruke, međutim, ne možete poslati pravo bez već imanja prava da pošaljete Mach poruku. Dakle, kako se uspostavlja prva komunikacija?
Za to je uključen bootstrap server (launchd u mac-u), jer svako može dobiti PRAVO SLANJA na bootstrap server, moguće je zatražiti od njega pravo da pošalje poruku drugom procesu:
Zadaca A kreira novi port, dobijajući PRAVO PRIMANJA nad njim.
Zadaca A, kao nosilac prava primanja, generiše PRAVO SLANJA za port.
Zadaca A uspostavlja vezu sa bootstrap serverom, i šalje mu PRAVO SLANJA za port koji je generisala na početku.
Zapamtite da svako može dobiti PRAVO SLANJA na bootstrap server.
Zadaca A šalje bootstrap_register
poruku bootstrap serveru da poveže dati port sa imenom kao što je com.apple.taska
Zadaca B komunicira sa bootstrap serverom da izvrši bootstrap pretragu za imenom usluge (bootstrap_lookup
). Da bi bootstrap server mogao odgovoriti, zadaca B će mu poslati PRAVO SLANJA na port koji je prethodno kreirala unutar poruke pretrage. Ako je pretraga uspešna, server duplicira PRAVO SLANJA primljeno od Zadace A i prenosi ga Zadaci B.
Zapamtite da svako može dobiti PRAVO SLANJA na bootstrap server.
Sa ovim PRAVOM SLANJA, Zadaca B je sposobna za slanje poruke Zadaci A.
Za dvosmernu komunikaciju obično zadaca B generiše novi port sa PRAVOM PRIMANJA i PRAVOM SLANJA, i daje PRAVO SLANJA Zadaci A kako bi mogla slati poruke Zadaci B (dvosmerna komunikacija).
Bootstrap server ne može autentifikovati ime usluge koje tvrdi zadaca. To znači da bi zadaca mogla potencijalno imitirati bilo koju sistemsku zadacu, kao što je lažno tvrđenje o imenu usluge za autorizaciju i zatim odobravanje svake zahteve.
Zatim, Apple čuva imena usluga koje pruža sistem u sigurnim konfiguracionim datotekama, smeštenim u SIP-zaštićenim direktorijumima: /System/Library/LaunchDaemons
i /System/Library/LaunchAgents
. Pored svakog imena usluge, pripadajuća binarna datoteka se takođe čuva. Bootstrap server će kreirati i zadržati PRAVO PRIMANJA za svako od ovih imena usluga.
Za ove unapred definisane usluge, proces pretrage se malo razlikuje. Kada se ime usluge pretražuje, launchd pokreće uslugu dinamički. Novi radni tok je sledeći:
Zadaca B inicira bootstrap pretragu za imenom usluge.
launchd proverava da li zadaca radi i ako ne, pokreće je.
Zadaca A (usluga) vrši bootstrap prijavu (bootstrap_check_in()
). Ovde, bootstrap server kreira PRAVO SLANJA, zadržava ga i prenosi PRAVO PRIMANJA Zadaci A.
launchd duplicira PRAVO SLANJA i šalje ga Zadaci B.
Zadaca B generiše novi port sa PRAVOM PRIMANJA i PRAVOM SLANJA, i daje PRAVO SLANJA Zadaci A (usluga) kako bi mogla slati poruke Zadaci B (dvosmerna komunikacija).
Međutim, ovaj proces se primenjuje samo na unapred definisane sistemske zadace. Ne-sistemske zadace i dalje funkcionišu kao što je prvobitno opisano, što bi potencijalno moglo omogućiti imitaciju.
Stoga, launchd nikada ne bi trebao da se sruši ili će ceo sistem pasti.
Funkcija mach_msg
, koja je u suštini sistemski poziv, koristi se za slanje i primanje Mach poruka. Funkcija zahteva da poruka koja se šalje bude prvi argument. Ova poruka mora početi sa strukturom mach_msg_header_t
, nakon koje sledi stvarni sadržaj poruke. Struktura je definisana kao:
Procesi koji poseduju receive right mogu primati poruke na Mach portu. Nasuprot tome, pošiljaocima se dodeljuju send ili send-once right. Send-once right je isključivo za slanje jedne poruke, nakon čega postaje nevažeći.
Početno polje msgh_bits
je bitmapa:
Prvi bit (najznačajniji) se koristi za označavanje da je poruka složena (više o tome u nastavku)
i 4. bit koriste kernel
5 najmanje značajnih bitova 2. bajta može se koristiti za voucher: još jedan tip porta za slanje kombinacija ključ/vrednost.
5 najmanje značajnih bitova 3. bajta može se koristiti za local port
5 najmanje značajnih bitova 4. bajta može se koristiti za remote port
Tipovi koji se mogu specificirati u voucheru, lokalnim i udaljenim portovima su (iz mach/message.h):
For example, MACH_MSG_TYPE_MAKE_SEND_ONCE
can be used to indicate that a send-once right should be derived and transferred for this port. It can also be specified MACH_PORT_NULL
to prevent the recipient to be able to reply.
In order to achieve an easy bi-directional communication a process can specify a mach port in the mach message header called the reply port (msgh_local_port
) where the receiver of the message can send a reply to this message.
Napomena da se ova vrsta bi-direkcione komunikacije koristi u XPC porukama koje očekuju odgovor (xpc_connection_send_message_with_reply
i xpc_connection_send_message_with_reply_sync
). Ali obično se kreiraju različite portove kao što je objašnjeno ranije da bi se stvorila bi-direkcionalna komunikacija.
The other fields of the message header are:
msgh_size
: veličina celog paketa.
msgh_remote_port
: port na kojem je ova poruka poslata.
msgh_voucher_port
: mach vaučeri.
msgh_id
: ID ove poruke, koji se tumači od strane primaoca.
Napomena da se mach poruke šalju preko mach port
, koji je jedan primalac, više pošiljalaca komunikacioni kanal ugrađen u mach kernel. Više procesa može slati poruke na mach port, ali u bilo kojem trenutku samo jedan proces može čitati iz njega.
Messages are then formed by the mach_msg_header_t
header followed by the body and by the trailer (if any) and it can grant permission to reply to it. In these cases, the kernel just need to pass the message from one task to the other.
A trailer is information added to the message by the kernel (cannot be set by the user) which can be requested in message reception with the flags MACH_RCV_TRAILER_<trailer_opt>
(there is different information that can be requested).
However, there are other more complex messages, like the ones passing additional port rights or sharing memory, where the kernel also needs to send these objects to the recipient. In this cases the most significant bit of the header msgh_bits
is set.
The possible descriptors to pass are defined in mach/message.h
:
U 32 bita, svi deskriptori su 12B, a tip deskriptora je u 11. deskriptoru. U 64 bita, veličine variraju.
Kernel će kopirati deskriptore iz jednog zadatka u drugi, ali prvo stvarajući kopiju u kernel memoriji. Ova tehnika, poznata kao "Feng Shui", je zloupotrebljena u nekoliko eksploatacija da bi se kernelu naložilo da kopira podatke u svoju memoriju, čineći da proces šalje deskriptore samom sebi. Tada proces može primati poruke (kernel će ih osloboditi).
Takođe je moguće poslati prava na port ranjivom procesu, a prava na port će se jednostavno pojaviti u procesu (čak i ako ih ne obrađuje).
Napomena da su portovi povezani sa imenskim prostorom zadatka, tako da se za kreiranje ili pretraživanje porta takođe upitkuje imenski prostor zadatka (više u mach/mach_port.h
):
mach_port_allocate
| mach_port_construct
: Kreiraj port.
mach_port_allocate
takođe može kreirati port set: pravo prijema nad grupom portova. Kada se poruka primi, označava se port iz kojeg je došla.
mach_port_allocate_name
: Promeni ime porta (po defaultu 32bitni ceo broj)
mach_port_names
: Dobij imena portova iz cilja
mach_port_type
: Dobij prava zadatka nad imenom
mach_port_rename
: Preimenuj port (kao dup2 za FD-ove)
mach_port_allocate
: Alociraj novi RECEIVE, PORT_SET ili DEAD_NAME
mach_port_insert_right
: Kreiraj novo pravo u portu gde imaš RECEIVE
mach_port_...
mach_msg
| mach_msg_overwrite
: Funkcije koje se koriste za slanje i primanje mach poruka. Verzija za prepisivanje omogućava da se odredi drugačiji bafer za prijem poruka (druga verzija će samo ponovo koristiti isti).
Pošto su funkcije mach_msg
i mach_msg_overwrite
one koje se koriste za slanje i primanje poruka, postavljanje tačke prekida na njih bi omogućilo inspekciju poslatih i primljenih poruka.
Na primer, počnite da debagujete bilo koju aplikaciju koju možete debagovati jer će učitati libSystem.B
koja će koristiti ovu funkciju.
Da biste dobili argumente mach_msg
, proverite registre. Ovo su argumenti (iz mach/message.h):
Dobijte vrednosti iz registara:
Ispitajte zaglavlje poruke proveravajući prvi argument:
Ta vrsta mach_msg_bits_t
je veoma uobičajena za omogućavanje odgovora.
ime je podrazumevano ime dato portu (proverite kako se povećava u prva 3 bajta). ipc-object
je obfuskovani jedinstveni identifikator porta.
Takođe obratite pažnju na to kako portovi sa samo send
pravom identifikuju vlasnika (ime porta + pid).
Takođe obratite pažnju na upotrebu +
da označite druge zadatke povezane sa istim portom.
Takođe je moguće koristiti procesxp da se vide i registrovana imena usluga (sa onemogućenim SIP-om zbog potrebe za com.apple.system-task-port
):
Možete instalirati ovaj alat na iOS preuzimanjem sa http://newosxbook.com/tools/binpack64-256.tar.gz
Obratite pažnju kako pošiljalac alokuje port, kreira pravo slanja za ime org.darlinghq.example
i šalje ga bootstrap serveru dok je pošiljalac tražio pravo slanja tog imena i koristio ga za slanje poruke.
Postoje neki posebni portovi koji omogućavaju izvršavanje određenih osetljivih akcija ili pristup određenim osetljivim podacima u slučaju da zadatak ima SEND dozvole nad njima. Ovo čini ove portove veoma zanimljivim iz perspektive napadača, ne samo zbog mogućnosti, već i zato što je moguće deliti SEND dozvole između zadataka.
Ovi portovi su predstavljeni brojem.
SEND prava se mogu dobiti pozivanjem host_get_special_port
i RECEIVE prava pozivanjem host_set_special_port
. Međutim, oba poziva zahtevaju host_priv
port koji može pristupiti samo root. Štaviše, u prošlosti je root mogao da pozove host_set_special_port
i preuzme proizvoljan port, što je omogućilo, na primer, zaobilaženje potpisivanja koda preuzimanjem HOST_KEXTD_PORT
(SIP sada sprečava ovo).
Oni su podeljeni u 2 grupe: prvih 7 portova je u vlasništvu kernela, a to su 1 HOST_PORT
, 2 HOST_PRIV_PORT
, 3 HOST_IO_MASTER_PORT
i 7 je HOST_MAX_SPECIAL_KERNEL_PORT
.
Oni koji počinju od broja 8 su u vlasništvu sistemskih demona i mogu se naći deklarisani u host_special_ports.h
.
Host port: Ako proces ima SEND privilegiju nad ovim portom, može dobiti informacije o sistemu pozivajući njegove rutine kao što su:
host_processor_info
: Dobijanje informacija o procesoru
host_info
: Dobijanje informacija o hostu
host_virtual_physical_table_info
: Virtuelna/Fizička tabela stranica (zahteva MACH_VMDEBUG)
host_statistics
: Dobijanje statistike hosta
mach_memory_info
: Dobijanje rasporeda memorije kernela
Host Priv port: Proces sa SEND pravom nad ovim portom može izvršavati privilegovane akcije kao što su prikazivanje podataka o pokretanju ili pokušaj učitavanja ekstenzije kernela. Proces mora biti root da bi dobio ovu dozvolu.
Štaviše, da bi pozvao kext_request
API, potrebno je imati druge privilegije com.apple.private.kext*
koje se daju samo Apple binarnim datotekama.
Druge rutine koje se mogu pozvati su:
host_get_boot_info
: Dobijanje machine_boot_info()
host_priv_statistics
: Dobijanje privilegovanih statistika
vm_allocate_cpm
: Alokacija Kontinuirane Fizičke Memorije
host_processors
: Slanje prava host procesorima
mach_vm_wire
: Učiniti memoriju rezidentnom
Kako root može pristupiti ovoj dozvoli, može pozvati host_set_[special/exception]_port[s]
da preuzme host posebne ili izuzetne portove.
Moguće je videti sve host posebne portove pokretanjem:
Ovo su portovi rezervisani za dobro poznate usluge. Moguće je dobiti/postaviti ih pozivajući task_[get/set]_special_port
. Mogu se naći u task_special_ports.h
:
From here:
TASK_KERNEL_PORT[task-self send right]: Port koji se koristi za kontrolu ovog zadatka. Koristi se za slanje poruka koje utiču na zadatak. Ovo je port koji vraća mach_task_self (vidi Task Ports ispod).
TASK_BOOTSTRAP_PORT[bootstrap send right]: Bootstrap port zadatka. Koristi se za slanje poruka koje zahtevaju vraćanje drugih portova sistemskih usluga.
TASK_HOST_NAME_PORT[host-self send right]: Port koji se koristi za zahtev informacija o hostu koji sadrži. Ovo je port koji vraća mach_host_self.
TASK_WIRED_LEDGER_PORT[ledger send right]: Port koji imenuje izvor iz kojeg ovaj zadatak crpi svoju fiksnu memoriju jezgra.
TASK_PAGED_LEDGER_PORT[ledger send right]: Port koji imenuje izvor iz kojeg ovaj zadatak crpi svoju podrazumevanu upravljanu memoriju.
Prvobitno, Mach nije imao "procese", imao je "zadate" koje su se smatrale više kao kontejner niti. Kada je Mach spojen sa BSD svaki zadatak je bio povezan sa BSD procesom. Stoga svaki BSD proces ima detalje koji su mu potrebni da bude proces, a svaki Mach zadatak takođe ima svoje unutrašnje funkcionisanje (osim nepostojećeg pid 0 koji je kernel_task
).
Postoje dve veoma zanimljive funkcije povezane sa ovim:
task_for_pid(target_task_port, pid, &task_port_of_pid)
: Dobijanje SEND prava za port zadatka koji je povezan sa onim koji je specificiran pid
i dodeljivanje istog naznačenom target_task_port
(koji je obično zadatak pozivaoca koji je koristio mach_task_self()
, ali može biti SEND port preko drugog zadatka).
pid_for_task(task, &pid)
: Dajući SEND pravo zadatku, pronaći kojem PID-u je ovaj zadatak povezan.
Da bi se izvršavale radnje unutar zadatka, zadatak je trebao SEND
pravo za sebe pozivajući mach_task_self()
(koji koristi task_self_trap
(28)). Sa ovom dozvolom zadatak može izvršiti nekoliko radnji kao što su:
task_threads
: Dobijanje SEND prava nad svim portovima zadatka niti zadatka
task_info
: Dobijanje informacija o zadatku
task_suspend/resume
: Suspendovanje ili nastavak zadatka
task_[get/set]_special_port
thread_create
: Kreiranje niti
task_[get/set]_state
: Kontrola stanja zadatka
i više se može naći u mach/task.h
Obratite pažnju da sa SEND pravom nad portom zadatka drugog zadatka, moguće je izvršiti takve radnje nad drugim zadatkom.
Štaviše, task_port je takođe vm_map
port koji omogućava čitanje i manipulaciju memorijom unutar zadatka sa funkcijama kao što su vm_read()
i vm_write()
. To u osnovi znači da zadatak sa SEND pravima nad task_port-om drugog zadatka može ubaciti kod u taj zadatak.
Zapamtite da zato što je jezgro takođe zadatak, ako neko uspe da dobije SEND dozvole nad kernel_task
, moći će da natera jezgro da izvrši bilo šta (jailbreak).
Pozovite mach_task_self()
da dobijete ime za ovaj port za zadatak pozivaoca. Ovaj port se samo nasleđuje preko exec()
; novi zadatak kreiran sa fork()
dobija novi port zadatka (kao poseban slučaj, zadatak takođe dobija novi port zadatka nakon exec()
u suid binarnom). Jedini način da se pokrene zadatak i dobije njegov port je da se izvrši "port swap dance" dok se radi fork()
.
Ovo su ograničenja za pristup portu (iz macos_task_policy
iz binarnog AppleMobileFileIntegrity
):
Ako aplikacija ima com.apple.security.get-task-allow
pravo procesi istog korisnika mogu pristupiti portu zadatka (obično dodato od strane Xcode-a za debagovanje). Proces notarizacije neće to dozvoliti za produkcijske verzije.
Aplikacije sa com.apple.system-task-ports
pravom mogu dobiti port zadatka za bilo koji proces, osim jezgra. U starijim verzijama to se zvalo task_for_pid-allow
. Ovo se dodeljuje samo Apple aplikacijama.
Root može pristupiti portovima zadataka aplikacija koje nisu kompajlirane sa hardened runtime-om (i ne od Apple-a).
Port imena zadatka: Nepovlašćena verzija port-a zadatka. Referencira zadatak, ali ne dozvoljava kontrolu nad njim. Jedina stvar koja izgleda da je dostupna kroz njega je task_info()
.
Niti takođe imaju povezane portove, koji su vidljivi iz zadatka koji poziva task_threads
i iz procesora sa processor_set_threads
. SEND pravo nad portom niti omogućava korišćenje funkcija iz thread_act
pod sistema, kao što su:
thread_terminate
thread_[get/set]_state
act_[get/set]_state
thread_[suspend/resume]
thread_info
...
Svaka nit može dobiti ovaj port pozivajući mach_thread_sef
.
Možete preuzeti shellcode sa:
Introduction to ARM64v8Kompajlirati prethodni program i dodati ovlašćenja da bi mogli da ubrizgate kod sa istim korisnikom (ako ne, moraćete da koristite sudo).