mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
183 lines
9.0 KiB
Markdown
183 lines
9.0 KiB
Markdown
# 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 <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}}
|