FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study)

Reading time: 8 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Επισκόπηση

Αυτή η σελίδα τεκμηριώνει μια πρακτική τεχνική έγχυσης διεργασίας/ELF σε usermode σε PlayStation 5 (PS5), το οποίο βασίζεται σε FreeBSD. Η μέθοδος γενικεύεται σε παράγωγα του FreeBSD όταν έχετε ήδη kernel read/write (R/W) primitives. Σε υψηλό επίπεδο:

  • Τροποποιήστε τα credentials της τρέχουσας διεργασίας (ucred) για να χορηγήσετε δικαιώματα debugger, επιτρέποντας ptrace/mdbg σε αυθαίρετες διεργασίες χρήστη.
  • Εντοπίστε διεργασίες-στόχους περπατώντας τη λίστα allproc του kernel.
  • Παρακάμψτε τους περιορισμούς PROT_EXEC θέτοντας vm_map_entry.protection |= PROT_EXEC στο vm_map του στόχου μέσω εγγραφών στη μνήμη του kernel.
  • Χρησιμοποιήστε ptrace για να εκτελέσετε Remote Function Invocation (RFI): σταματήστε ένα thread, ορίστε registers για να καλέσετε αυθαίρετες συναρτήσεις εντός του στόχου, επαναλάβετε την εκτέλεση, συλλέξτε τιμές επιστροφής και επαναφέρετε την κατάσταση.
  • Map και εκτελέστε αυθαίρετα ELF payloads μέσα στον στόχο χρησιμοποιώντας έναν in-process ELF loader, στη συνέχεια spawn ένα αφιερωμένο thread που τρέχει το payload σας και προκαλεί ένα breakpoint για καθαρό detach.

PS5 hypervisor mitigations worth noting (contextualized for this technique):

  • XOM (execute-only .text) αποτρέπει την ανάγνωση/εγγραφή του kernel .text.
  • Το καθάρισμα του CR0.WP ή η απενεργοποίηση του CR4.SMEP προκαλεί hypervisor vmexit (crash). Μόνο εγγραφές δεδομένων στον kernel είναι βιώσιμες.
  • Το mmap σε userland περιορίζεται σε PROT_READ|PROT_WRITE από προεπιλογή. Η παροχή PROT_EXEC πρέπει να γίνει επεξεργάζοντας τις εγγραφές vm_map στη μνήμη του kernel.

Αυτή η τεχνική είναι post-exploitation: προϋποθέτει kernel R/W primitives από μια αλυσίδα exploit. Δημόσια payloads επιδεικνύουν αυτό έως το firmware 10.01 κατά το χρόνο γραφής.

Kernel data-only primitives

Process discovery via allproc

FreeBSD διατηρεί μια διπλά-συνδεδεμένη λίστα διεργασιών στο kernel .data στο allproc. Με ένα kernel read primitive, επαναλάβετε αυτή τη λίστα για να εντοπίσετε ονόματα διεργασιών και PIDs:

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 εξαρτάται από το firmware.
  • p_comm είναι ένα όνομα σταθερού μεγέθους· σκεφτείτε αναζητήσεις pid->proc αν χρειαστεί.

Ανύψωση δικαιωμάτων για debugging (ucred)

Στο PS5, το struct ucred περιλαμβάνει ένα πεδίο Authority ID προσβάσιμο μέσω proc->p_ucred. Η εγγραφή του debugger Authority ID χορηγεί ptrace/mdbg πάνω σε άλλες διεργασίες:

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 είναι συγκεκριμένο για την οικογένεια firmware του PS5 και πρέπει να επαληθευτεί ανά έκδοση.
  • Μετά από αυτή τη write, ο injector μπορεί να επισυνάψει και να instrument διαδικασίες χρήστη μέσω ptrace/mdbg.

Παράκαμψη RW-only user mappings: vm_map PROT_EXEC flip

Η Userland mmap μπορεί να περιορίζεται σε PROT_READ|PROT_WRITE. Το FreeBSD παρακολουθεί το address space μιας διεργασίας σε ένα vm_map από κόμβους vm_map_entry (BST plus list). Κάθε entry φέρει τα πεδία protection και max_protection:

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 you can locate the target’s vm_map and set entry->protection |= PROT_EXEC (and, if needed, entry->max_protection). Practical implementation notes:

  • Walk entries either linearly via next or using the balanced-tree (left/right) for O(log n) search by address range.
  • Pick a known RW region you control (scratch buffer or mapped file) and add PROT_EXEC so you can stage code or loader thunks.
  • PS5 SDK code provides helpers for fast map-entry lookup and toggling protections.

This bypasses userland’s mmap policy by editing kernel-owned metadata directly.

Remote Function Invocation (RFI) with ptrace

Το FreeBSD δεν έχει Windows-style VirtualAllocEx/CreateRemoteThread. Αντίθετα, ωθήστε τον στόχο να καλέσει συναρτήσεις στον ίδιο υπό έλεγχο ptrace:

  1. Συνδεθείτε στον στόχο και επιλέξτε ένα thread· PTRACE_ATTACH ή PS5-specific mdbg flows μπορεί να ισχύουν.
  2. Αποθηκεύστε το context του thread: registers, PC, SP, flags.
  3. Γράψτε τα argument registers σύμφωνα με το ABI (x86_64 SysV ή arm64 AAPCS64), ορίστε το PC στη στοχευόμενη συνάρτηση, και προαιρετικά τοποθετήστε επιπλέον args/stack όπως χρειάζεται.
  4. Εκτελέστε single-step ή continue μέχρι ένα ελεγχόμενο stop (π.χ. software breakpoint ή signal), έπειτα διαβάστε τις τιμές επιστροφής από regs.
  5. Επαναφέρετε το αρχικό context και συνεχίστε.

Use cases:

  • Call into an in-process ELF loader (e.g., elfldr_load) with a pointer to your ELF image in target memory.
  • Invoke helper routines to fetch returned entrypoints and payload-args pointers.

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 χαρτογραφεί segments, επιλύει imports, εφαρμόζει relocations και επιστρέφει το entry (συχνά ένα CRT bootstrap) καθώς και έναν αδιαφανή pointer payload_args που ο stager σας περνάει στο main() του payload.

Πολυνηματικός stager και καθαρό detach

Ένας ελάχιστος stager μέσα στον στόχο δημιουργεί ένα νέο pthread που εκτελεί το main του ELF και στη συνέχεια προκαλεί int3 για να σηματοδοτήσει στον injector να αποσυνδεθεί:

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;
}
  • Οι δείκτες SCEFunctions/payload_args παρέχονται από το loader/SDK glue.
  • Μετά το breakpoint και το detach, το payload συνεχίζει σε δικό του thread.

Ολοκληρωμένη ροή από άκρο σε άκρο (PS5 reference implementation)

Μια λειτουργική υλοποίηση διατίθεται ως ένας μικρός TCP injector server συν ένα client script:

  • Ο NineS server ακούει στο TCP 9033 και λαμβάνει ένα header που περιέχει το target process name, ακολουθούμενο από το ELF image:
c
typedef struct __injector_data_t{
char       proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
  • Χρήση Python client:
bash
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>

Παράδειγμα Hello-world payload (logs to klog):

c
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
int main(){
klog_printf("Hello from PID %d\n", getpid());
return 0;
}

Πρακτικές παρατηρήσεις

  • Offsets and constants (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) είναι ειδικά ανά firmware και πρέπει να ενημερώνονται σε κάθε έκδοση.
  • Οι προστασίες του hypervisor επιβάλλουν data-only kernel writes· μην επιχειρήσετε να κάνετε patch το CR0.WP ή το CR4.SMEP.
  • JIT memory είναι εναλλακτική: μερικές διεργασίες εκθέτουν PS5 JIT APIs για να allocate executable pages. Το vm_map protection flip αφαιρεί την ανάγκη να βασιστείτε σε JIT/mirroring tricks.
  • Κρατήστε το register save/restore ανθεκτικό· σε περίπτωση αποτυχίας μπορεί να προκαλέσετε deadlock ή crash στο target.

Public tooling

  • 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

References

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks