FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 Fallstudie)
Reading time: 8 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Übersicht
Diese Seite dokumentiert eine praktische Unix/BSD usermode process/ELF injection technique auf PlayStation 5 (PS5), die auf FreeBSD basiert. Die Methode generalisiert auf FreeBSD-Derivate, sobald kernel read/write (R/W) primitives vorhanden sind. Auf hoher Ebene:
- Patchen der aktuellen Prozess-Credentials (ucred), um Debugger-Berechtigungen zu gewähren und ptrace/mdbg auf beliebigen Benutzerprozessen zu ermöglichen.
- Zielprozesse finden durch Durchlaufen der kernel allproc-Liste.
- Umgehen von PROT_EXEC-Einschränkungen durch Setzen von vm_map_entry.protection |= PROT_EXEC im Ziel-vm_map via Kernel-Daten-Schreibzugriffe.
- Verwendung von ptrace zur Remote Function Invocation (RFI): einen Thread stoppen, Register so setzen, dass beliebige Funktionen im Ziel aufgerufen werden, fortsetzen, Rückgabewerte erfassen und Zustand wiederherstellen.
- Mapping und Ausführen beliebiger ELF-Payloads im Ziel mit einem in-prozess ELF-Loader, dann einen dedizierten Thread starten, der die Payload ausführt und einen Breakpoint auslöst, um sauber zu detach-en.
PS5-Hypervisor-Mitigations, die für diese Technik relevant sind:
- XOM (execute-only .text) verhindert das Lesen/Schreiben von Kernel-.text.
- Das Löschen von CR0.WP oder Deaktivieren von CR4.SMEP führt zu einem Hypervisor-vmexit (Crash). Nur daten-only Kernel-Schreibzugriffe sind praktikabel.
- Userland mmap ist standardmäßig auf PROT_READ|PROT_WRITE beschränkt. PROT_EXEC muss durch Editieren von vm_map entries im Kernel-Speicher gewährt werden.
Diese Technik ist Post-Exploitation: sie setzt kernel R/W primitives aus einer Exploit-Chain voraus. Öffentliche Payloads demonstrieren dies bis Firmware 10.01 zum Zeitpunkt der Erstellung.
Kernel data-only primitives
Prozess-Erkennung via allproc
FreeBSD unterhält eine doppelt verkettete Liste von Prozessen in der Kernel-.data an allproc. Mit einem kernel read primitive iteriert man diese Liste, um Prozessnamen und PIDs zu ermitteln:
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);
}
Hinweise:
- KERNEL_ADDRESS_ALLPROC ist firmwareabhängig.
- p_comm ist ein Name fester Größe; ziehe pid->proc-Suchen in Betracht, falls nötig.
Berechtigungen zum Debugging erhöhen (ucred)
Auf der PS5 enthält struct ucred ein Authority ID-Feld, das über proc->p_ucred erreichbar ist. Das Schreiben der debugger authority ID gewährt ptrace/mdbg-Zugriff auf andere Prozesse:
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 ist spezifisch für die PS5-Firmware-Familie und muss für jede Version verifiziert werden.
- Nach diesem Schreibvorgang kann der Injector Benutzerprozesse mittels ptrace/mdbg attachen und instrumentieren.
Umgehen von RW-only user mappings: vm_map PROT_EXEC flip
Userland mmap kann auf PROT_READ|PROT_WRITE beschränkt sein. FreeBSD verwaltet den Adressraum eines Prozesses in einer vm_map aus vm_map_entry-Knoten (BST plus Liste). Jeder Eintrag enthält die Felder protection und max_protection:
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;
};
Mit Kernel R/W können Sie die vm_map des Ziels lokalisieren und entry->protection |= PROT_EXEC setzen (und, falls nötig, entry->max_protection). Praktische Implementierungs-Hinweise:
- Walk entries entweder linear via next oder unter Verwendung des balanced-tree (left/right) für O(log n)-Suche nach Adressbereichen.
- Wählen Sie eine bekannte RW-Region, die Sie kontrollieren (scratch buffer oder mapped file) und fügen Sie PROT_EXEC hinzu, damit Sie Code oder Loader-Thunks bereitstellen können.
- PS5 SDK code stellt Hilfsfunktionen für schnelles map-entry lookup und das Umschalten von Protections bereit.
Dies umgeht userland’s mmap policy, indem kernel-eigene Metadaten direkt bearbeitet werden.
Remote Function Invocation (RFI) with ptrace
FreeBSD lacks Windows-style VirtualAllocEx/CreateRemoteThread. Stattdessen bringen Sie das Ziel dazu, unter ptrace-Kontrolle Funktionen in sich selbst aufzurufen:
- Attach to the target and select a thread; PTRACE_ATTACH or PS5-specific mdbg flows may apply.
- Save thread context: registers, PC, SP, flags.
- Write argument registers per the ABI (x86_64 SysV or arm64 AAPCS64), set PC to the target function, and optionally place additional args/stack as needed.
- Single-step or continue until a controlled stop (e.g., software breakpoint or signal), then read back return values from regs.
- Restore original context and continue.
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:
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);
Der loader mappt Segmente, löst Imports auf, wendet Relocations an und gibt den entry zurück (häufig ein CRT bootstrap) sowie einen opaken payload_args pointer, den dein stager an das payload’s main() übergibt.
Threaded stager und sauberes Detach
Ein minimaler stager innerhalb des Ziels erzeugt einen neuen pthread, der das ELF’s main ausführt, und löst dann int3 aus, um den injector zum Detach zu signalisieren:
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;
}
- Die SCEFunctions/payload_args pointers werden vom loader/SDK glue bereitgestellt.
- Nach dem breakpoint und detach läuft der payload in seinem eigenen thread weiter.
End-to-End-Pipeline (PS5-Referenzimplementierung)
Eine funktionierende Implementierung wird als kleiner TCP injector server sowie ein client script ausgeliefert:
- Der NineS server hört auf TCP 9033 und empfängt einen Header, der den Zielprozessnamen gefolgt vom ELF image enthält:
typedef struct __injector_data_t{
char proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
- Verwendung des Python-Clients:
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
Beispiel für Hello-world payload (loggt in klog):
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
int main(){
klog_printf("Hello from PID %d\n", getpid());
return 0;
}
Praktische Überlegungen
- Offsets und Konstanten (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) sind firmware-spezifisch und müssen pro Release aktualisiert werden.
- Hypervisor-Schutzmaßnahmen erzwingen ausschließlich datenbasierte Kernel-Schreibzugriffe; versuche nicht, CR0.WP oder CR4.SMEP zu patchen.
- JIT-Speicher ist eine Alternative: Einige Prozesse stellen PS5 JIT APIs bereit, um ausführbare Seiten zuzuweisen. Das Umschalten der vm_map-Protektion entfernt die Notwendigkeit, auf JIT-/Mirroring-Tricks angewiesen zu sein.
- Mache das Speichern/Wiederherstellen von Registern robust; bei Fehlern kannst du einen Deadlock verursachen oder das Ziel zum Absturz bringen.
Öffentliche Tools
- PS5 SDK (dynamisches 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
- Verwandte Projekte: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv
Referenzen
- Usermode ELF injection on the PlayStation 5
- ps5-payload-dev/sdk
- ps5-payload-dev/elfldr
- buzzer-re/NineS
- playstation_research_utils
- Mira
- gdbsrv
- FreeBSD klog reference
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.