macOS Thread Injection via Task port
Kod
1. Hakovanje niti
Prvo se poziva funkcija task_threads()
na Task portu da bi se dobio spisak niti iz udaljenog taska. Nit se bira za hakovanje. Ovaj pristup se razlikuje od konvencionalnih metoda ubacivanja koda jer je stvaranje nove udaljene niti zabranjeno zbog nove zaštite koja blokira thread_create_running()
.
Da bi se kontrolisala nit, poziva se funkcija thread_suspend()
, koja zaustavlja njeno izvršavanje.
Jedine dozvoljene operacije na udaljenoj niti uključuju zaustavljanje i pokretanje niti, dobijanje i menjanje vrednosti registara. Udaljeni pozivi funkcija se pokreću postavljanjem registara x0
do x7
na argumente, konfigurisanjem pc
da cilja željenu funkciju i aktiviranjem niti. Da bi se osiguralo da nit ne padne nakon povratka, neophodno je otkriti povratnu vrednost.
Jedna strategija uključuje registrovanje rukovaoca izuzecima za udaljenu nit korišćenjem thread_set_exception_ports()
, postavljanje registra lr
na nevažeću adresu pre poziva funkcije. Ovo pokreće izuzetak nakon izvršavanja funkcije, šalje poruku na port izuzetka i omogućava inspekciju stanja niti radi povraćaja povratne vrednosti. Alternativno, kao što je preuzeto iz Ian Beer-ovog triple_fetch exploit-a, lr
je postavljen da beskonačno petlja. Registri niti se zatim neprekidno prate dok pc
ne pokazuje na tu instrukciju.
2. Mach portovi za komunikaciju
Sledeća faza uključuje uspostavljanje Mach portova radi olakšane komunikacije sa udaljenom niti. Ovi portovi su od suštinskog značaja za prenos proizvoljnih prava slanja i primanja između zadataka.
Za dvosmernu komunikaciju, kreiraju se dva Mach primanja prava: jedno u lokalnom, a drugo u udaljenom zadatku. Zatim se za svaki port prenosi pravo slanja na odgovarajući zadatak, omogućavajući razmenu poruka.
Fokus je na lokalnom portu, gde lokalni zadatak drži primanje prava. Port se kreira pomoću mach_port_allocate()
. Izazov leži u prenosu prava slanja na ovaj port u udaljeni zadatak.
Jedna strategija uključuje iskorišćavanje thread_set_special_port()
da bi se postavilo pravo slanja na lokalni port u THREAD_KERNEL_PORT
udaljene niti. Zatim se udaljenoj niti nalaže da pozove mach_thread_self()
da bi dobila pravo slanja.
Za udaljeni port, proces je suštinski obrnut. Udaljenoj niti se nalaže da generiše Mach port putem mach_reply_port()
(jer mach_port_allocate()
nije pogodan zbog mehanizma povratka). Nakon kreiranja porta, u udaljenoj niti se poziva mach_port_insert_right()
da bi se uspostavilo pravo slanja. Ovo pravo se zatim smešta u kernel pomoću thread_set_special_port()
. U lokalnom zadatku se zatim koristi thread_get_special_port()
na udaljenoj niti da bi se dobilo pravo slanja na novo dodeljeni Mach port u udaljenom zadatku.
Završetak ovih koraka rezultira uspostavljanjem Mach portova, postavljajući osnovu za dvosmernu komunikaciju.
3. Osnovni primitivi za čitanje/pisanje memorije
U ovoj sekciji fokus je na korišćenju izvršnog primitiva za uspostavljanje osnovnih primitiva za čitanje i pisanje memorije. Ovi početni koraci su ključni za dobijanje veće kontrole nad udaljenim procesom, iako primitivi u ovoj fazi neće služiti mnogo svrsi. Uskoro će biti nadograđeni na naprednije verzije.
Čitanje i pisanje memorije korišćenjem izvršnog primitiva
Cilj je izvršiti čitanje i pisanje memorije koristeći određene funkcije. Za čitanje memorije koriste se funkcije slične sledećoj strukturi:
I za pisanje u memoriju, koriste se funkcije slične ovoj strukturi:
Ove funkcije odgovaraju datim sklopovskim instrukcijama:
Identifikacija odgovarajućih funkcija
Skeniranje uobičajenih biblioteka otkrilo je odgovarajuće kandidate za ove operacije:
Čitanje memorije: Funkcija
property_getName()
iz Objective-C runtime biblioteke identifikovana je kao odgovarajuća funkcija za čitanje memorije. Funkcija je prikazana ispod:
Ova funkcija efektivno deluje kao read_func
vraćajući prvo polje objc_property_t
.
Pisanje u memoriju: Pronalaženje prethodno izgrađene funkcije za pisanje u memoriju je izazovnije. Međutim, funkcija
_xpc_int64_set_value()
iz libxpc je odgovarajući kandidat sa sledećim rastavljanjem:
Da biste izvršili 64-bitni upis na određenoj adresi, udaljeni poziv je strukturiran na sledeći način:
Sa ovim osnovama postavljenim, stvorena je osnova za stvaranje deljene memorije, što predstavlja značajan napredak u kontroli udaljenog procesa.
4. Postavljanje deljene memorije
Cilj je uspostaviti deljenu memoriju između lokalnih i udaljenih zadataka, olakšavajući prenos podataka i omogućavajući pozivanje funkcija sa više argumenata. Pristup uključuje korišćenje libxpc
i njenog objekta tipa OS_xpc_shmem
, koji se zasniva na unosima memorije Mach.
Pregled procesa:
Alokacija memorije:
Alokacija memorije za deljenje korišćenjem
mach_vm_allocate()
.Korišćenje
xpc_shmem_create()
za kreiranje objektaOS_xpc_shmem
za alocirani region memorije. Ova funkcija će upravljati kreiranjem unosa memorije Mach i čuvati Mach send pravo na offsetu0x18
objektaOS_xpc_shmem
.
Kreiranje deljene memorije u udaljenom procesu:
Alokacija memorije za objekat
OS_xpc_shmem
u udaljenom procesu pomoću udaljenog pozivamalloc()
.Kopiranje sadržaja lokalnog objekta
OS_xpc_shmem
u udaljeni proces. Međutim, ova početna kopija će imati netačna imena unosa memorije Mach na offsetu0x18
.
Ispravljanje unosa memorije Mach:
Koristite metodu
thread_set_special_port()
za umetanje send prava za unos memorije Mach u udaljeni zadatak.Ispravite polje unosa memorije Mach na offsetu
0x18
tako što ćete ga prepisati imenom unosa memorije udaljenog zadatka.
Završno postavljanje deljene memorije:
Validirajte udaljeni objekat
OS_xpc_shmem
.Ustanovite mapiranje deljene memorije pomoću udaljenog poziva
xpc_shmem_remote()
.
Prateći ove korake, deljena memorija između lokalnih i udaljenih zadataka će biti efikasno postavljena, omogućavajući jednostavan prenos podataka i izvršavanje funkcija koje zahtevaju više argumenata.
Dodatni isečci koda
Za alokaciju memorije i kreiranje objekta deljene memorije:
Za kreiranje i ispravljanje objekta deljene memorije u udaljenom procesu:
Zapamtite da pravilno rukujete detaljima Mach portova i imenima memorijskih unosa kako biste osigurali ispravno funkcionisanje podele deljene memorije.
5. Postizanje potpune kontrole
Nakon uspešnog uspostavljanja deljene memorije i sticanja proizvoljnih izvršnih mogućnosti, suštinski smo stekli potpunu kontrolu nad ciljnim procesom. Ključne funkcionalnosti koje omogućavaju ovu kontrolu su:
Proizvoljne operacije nad memorijom:
Izvršite proizvoljno čitanje memorije pozivanjem
memcpy()
funkcije za kopiranje podataka iz deljenog regiona.Izvršite proizvoljno pisanje memorije koristeći
memcpy()
funkciju za prenos podataka u deljeni region.
Rukovanje pozivima funkcija sa više argumenata:
Za funkcije koje zahtevaju više od 8 argumenata, rasporedite dodatne argumente na steku u skladu sa konvencijom pozivanja.
Prenos Mach portova:
Prenosite Mach portove između zadataka putem Mach poruka putem prethodno uspostavljenih portova.
Prenos deskriptora fajlova:
Prenosite deskriptore fajlova između procesa koristeći fileportove, tehniku istaknutu od strane Iana Beera u
triple_fetch
.
Ova sveobuhvatna kontrola je obuhvaćena bibliotekom threadexec, koja pruža detaljnu implementaciju i korisnički prijateljski API za interakciju sa ciljnim procesom.
Važne razmatranja:
Obezbedite pravilnu upotrebu
memcpy()
funkcije za operacije čitanja/pisanja memorije kako biste održali stabilnost sistema i integritet podataka.Prilikom prenosa Mach portova ili deskriptora fajlova, pratite odgovarajuće protokole i odgovorno rukujte resursima kako biste sprečili curenje ili neželjeni pristup.
Prateći ove smernice i koristeći biblioteku threadexec
, možete efikasno upravljati i interagovati sa procesima na detaljnom nivou, postižući potpunu kontrolu nad ciljnim procesom.
Reference
Last updated