Skip to content

Commit 87510b8

Browse files
authored
Merge pull request #1405 from HackTricks-wiki/update_Usermode_ELF_injection_on_the_PlayStation_5_20250914_183156
Usermode ELF injection on the PlayStation 5
2 parents bacfd40 + 2ec32ef commit 87510b8

3 files changed

Lines changed: 201 additions & 0 deletions

File tree

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@
845845
- [WWW2Exec - \_\_malloc_hook & \_\_free_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook.md)
846846
- [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md)
847847
- [Linux kernel exploitation - toctou](binary-exploitation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md)
848+
- [PS5 compromission](binary-exploitation/freebsd-ptrace-rfi-vm_map-prot_exec-bypass-ps5.md)
848849
- [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
849850
- [iOS Exploiting](binary-exploitation/ios-exploiting/README.md)
850851
- [ios CVE-2020-27950-mach_msg_trailer_t](binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md)
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study)
2+
3+
{{#include ../../../banners/hacktricks-training.md}}
4+
5+
## Overview
6+
7+
This page documents a practical Unix/BSD usermode process/ELF injection technique on PlayStation 5 (PS5), which is based on FreeBSD. The method generalizes to FreeBSD derivatives when you already have kernel read/write (R/W) primitives. High level:
8+
9+
- Patch the current process credentials (ucred) to grant debugger authority, enabling ptrace/mdbg on arbitrary user processes.
10+
- Find target processes by walking the kernel allproc list.
11+
- Bypass PROT_EXEC restrictions by flipping vm_map_entry.protection |= PROT_EXEC in the target’s vm_map via kernel data writes.
12+
- Use ptrace to perform Remote Function Invocation (RFI): suspend a thread, set registers to call arbitrary functions inside the target, resume, collect return values, and restore state.
13+
- Map and run arbitrary ELF payloads inside the target using an in-process ELF loader, then spawn a dedicated thread that runs your payload and triggers a breakpoint to detach cleanly.
14+
15+
PS5 hypervisor mitigations worth noting (contextualized for this technique):
16+
- XOM (execute-only .text) prevents reading/writing kernel .text.
17+
- Clearing CR0.WP or disabling CR4.SMEP causes a hypervisor vmexit (crash). Only data-only kernel writes are viable.
18+
- Userland mmap is restricted to PROT_READ|PROT_WRITE by default. Granting PROT_EXEC must be done by editing vm_map entries in kernel memory.
19+
20+
This technique is post-exploitation: it assumes kernel R/W primitives from an exploit chain. Public payloads demonstrate this up to firmware 10.01 at time of writing.
21+
22+
## Kernel data-only primitives
23+
24+
### Process discovery via allproc
25+
26+
FreeBSD maintains a doubly-linked list of processes in kernel .data at allproc. With a kernel read primitive, iterate it to locate process names and PIDs:
27+
28+
```c
29+
struct proc* find_proc_by_name(const char* proc_name){
30+
uint64_t next = 0;
31+
kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t)); // list head
32+
struct proc* proc = malloc(sizeof(struct proc));
33+
do{
34+
kernel_copyout(next, (void*)proc, sizeof(struct proc)); // read entry
35+
if (!strcmp(proc->p_comm, proc_name)) return proc;
36+
kernel_copyout(next, &next, sizeof(uint64_t)); // advance next
37+
} while (next);
38+
free(proc);
39+
return NULL;
40+
}
41+
42+
void list_all_proc_and_pid(){
43+
uint64_t next = 0;
44+
kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t));
45+
struct proc* proc = malloc(sizeof(struct proc));
46+
do{
47+
kernel_copyout(next, (void*)proc, sizeof(struct proc));
48+
printf("%s - %d\n", proc->p_comm, proc->pid);
49+
kernel_copyout(next, &next, sizeof(uint64_t));
50+
} while (next);
51+
free(proc);
52+
}
53+
```
54+
55+
Notes:
56+
- KERNEL_ADDRESS_ALLPROC is firmware-dependent.
57+
- p_comm is a fixed-size name; consider pid->proc lookups if needed.
58+
59+
### Elevate credentials for debugging (ucred)
60+
61+
On PS5, struct ucred includes an Authority ID field reachable via proc->p_ucred. Writing the debugger authority ID grants ptrace/mdbg over other processes:
62+
63+
```c
64+
void set_ucred_to_debugger(){
65+
struct proc* proc = get_proc_by_pid(getpid());
66+
if (proc){
67+
uintptr_t authid = 0; // read current (optional)
68+
uintptr_t ptrace_authid = 0x4800000000010003ULL; // debugger Authority ID
69+
kernel_copyout((uintptr_t)proc->p_ucred + 0x58, &authid, sizeof(uintptr_t));
70+
kernel_copyin(&ptrace_authid, (uintptr_t)proc->p_ucred + 0x58, sizeof(uintptr_t));
71+
free(proc);
72+
}
73+
}
74+
```
75+
76+
- Offset 0x58 is specific to the PS5 firmware family and must be verified per version.
77+
- After this write, the injector can attach and instrument user processes via ptrace/mdbg.
78+
79+
## Bypassing RW-only user mappings: vm_map PROT_EXEC flip
80+
81+
Userland mmap may be constrained to PROT_READ|PROT_WRITE. FreeBSD tracks a process’s address space in a vm_map of vm_map_entry nodes (BST plus list). Each entry carries protection and max_protection fields:
82+
83+
```c
84+
struct vm_map_entry {
85+
struct vm_map_entry *prev,*next,*left,*right;
86+
vm_offset_t start, end, avail_ssize;
87+
vm_size_t adj_free, max_free;
88+
union vm_map_object object; vm_ooffset_t offset; vm_eflags_t eflags;
89+
vm_prot_t protection; vm_prot_t max_protection; vm_inherit_t inheritance;
90+
int wired_count; vm_pindex_t lastr;
91+
};
92+
```
93+
94+
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:
95+
- Walk entries either linearly via next or using the balanced-tree (left/right) for O(log n) search by address range.
96+
- Pick a known RW region you control (scratch buffer or mapped file) and add PROT_EXEC so you can stage code or loader thunks.
97+
- PS5 SDK code provides helpers for fast map-entry lookup and toggling protections.
98+
99+
This bypasses userland’s mmap policy by editing kernel-owned metadata directly.
100+
101+
## Remote Function Invocation (RFI) with ptrace
102+
103+
FreeBSD lacks Windows-style VirtualAllocEx/CreateRemoteThread. Instead, drive the target to call functions on itself under ptrace control:
104+
105+
1. Attach to the target and select a thread; PTRACE_ATTACH or PS5-specific mdbg flows may apply.
106+
2. Save thread context: registers, PC, SP, flags.
107+
3. 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.
108+
4. Single-step or continue until a controlled stop (e.g., software breakpoint or signal), then read back return values from regs.
109+
5. Restore original context and continue.
110+
111+
Use cases:
112+
- Call into an in-process ELF loader (e.g., elfldr_load) with a pointer to your ELF image in target memory.
113+
- Invoke helper routines to fetch returned entrypoints and payload-args pointers.
114+
115+
Example of driving the ELF loader:
116+
117+
```c
118+
intptr_t entry = elfldr_load(target_pid, (uint8_t*)elf_in_target);
119+
intptr_t args = elfldr_payload_args(target_pid);
120+
printf("[+] ELF entrypoint: %#02lx\n[+] Payload Args: %#02lx\n", entry, args);
121+
```
122+
123+
The loader maps segments, resolves imports, applies relocations and returns the entry (often a CRT bootstrap) plus an opaque payload_args pointer that your stager passes to the payload’s main().
124+
125+
## Threaded stager and clean detach
126+
127+
A minimal stager inside the target creates a new pthread that runs the ELF’s main and then triggers int3 to signal the injector to detach:
128+
129+
```c
130+
int __attribute__((section(".stager_shellcode$1"))) stager(SCEFunctions* functions){
131+
pthread_t thread;
132+
functions->pthread_create_ptr(&thread, 0,
133+
(void*(*)(void*))functions->elf_main, functions->payload_args);
134+
asm("int3");
135+
return 0;
136+
}
137+
```
138+
139+
- The SCEFunctions/payload_args pointers are provided by the loader/SDK glue.
140+
- After the breakpoint and detach, the payload continues in its own thread.
141+
142+
## End-to-end pipeline (PS5 reference implementation)
143+
144+
A working implementation ships as a small TCP injector server plus a client script:
145+
146+
- NineS server listens on TCP 9033 and receives a header containing the target process name followed by the ELF image:
147+
148+
```c
149+
typedef struct __injector_data_t{
150+
char proc_name[MAX_PROC_NAME];
151+
Elf64_Ehdr elf_header;
152+
} injector_data_t;
153+
```
154+
155+
- Python client usage:
156+
157+
```bash
158+
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
159+
```
160+
161+
Hello-world payload example (logs to klog):
162+
163+
```c
164+
#include <stdio.h>
165+
#include <unistd.h>
166+
#include <ps5/klog.h>
167+
int main(){
168+
klog_printf("Hello from PID %d\n", getpid());
169+
return 0;
170+
}
171+
```
172+
173+
## Practical considerations
174+
175+
- Offsets and constants (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) are firmware-specific and must be updated per release.
176+
- Hypervisor protections force data-only kernel writes; do not attempt to patch CR0.WP or CR4.SMEP.
177+
- JIT memory is an alternative: some processes expose PS5 JIT APIs to allocate executable pages. The vm_map protection flip removes the need to rely on JIT/mirroring tricks.
178+
- Keep register save/restore robust; on failure, you can deadlock or crash the target.
179+
180+
## Public tooling
181+
182+
- PS5 SDK (dynamic linking, kernel R/W wrappers, vm_map helpers): https://github.com/ps5-payload-dev/sdk
183+
- ELF loader: https://github.com/ps5-payload-dev/elfldr
184+
- Injector server: https://github.com/buzzer-re/NineS/
185+
- Utilities/vm_map helpers: https://github.com/buzzer-re/playstation_research_utils
186+
- Related projects: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv
187+
188+
## References
189+
190+
- [Usermode ELF injection on the PlayStation 5](https://reversing.codes/posts/PlayStation-5-ELF-Injection/)
191+
- [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk)
192+
- [ps5-payload-dev/elfldr](https://github.com/ps5-payload-dev/elfldr)
193+
- [buzzer-re/NineS](https://github.com/buzzer-re/NineS/)
194+
- [playstation_research_utils](https://github.com/buzzer-re/playstation_research_utils)
195+
- [Mira](https://github.com/OpenOrbis/mira-project)
196+
- [gdbsrv](https://github.com/ps5-payload-dev/gdbsrv)
197+
- [FreeBSD klog reference](https://lists.freebsd.org/pipermail/freebsd-questions/2006-October/134233.html)
198+
199+
{{#include ../../../banners/hacktricks-training.md}}

src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ Note that executables compiled with **`pyinstaller`** won't use these environmen
262262
>
263263
> Even **root** will run this code when running python.
264264
265+
265266
## Detection
266267
267268
### Shield

0 commit comments

Comments
 (0)