FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (estudio de caso PS5)
Reading time: 8 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Resumen
Esta página documenta una técnica práctica de inyección de procesos/ELF en usermode para Unix/BSD en PlayStation 5 (PS5), que se basa en FreeBSD. El método se generaliza a derivados de FreeBSD cuando ya dispones de primitivas de kernel read/write (R/W). A alto nivel:
- Parchea las credenciales del proceso actual (ucred) para otorgar privilegios de depurador, habilitando ptrace/mdbg en procesos de usuario arbitrarios.
- Encuentra procesos objetivo recorriendo la lista allproc del kernel.
- Evita las restricciones PROT_EXEC ajustando vm_map_entry.protection |= PROT_EXEC en el vm_map del objetivo mediante escrituras de datos en el kernel.
- Usa ptrace para realizar Remote Function Invocation (RFI): suspende un hilo, configura registros para llamar funciones arbitrarias dentro del objetivo, reanuda, recoge valores de retorno y restaura el estado.
- Mapea y ejecuta payloads ELF arbitrarios dentro del objetivo usando un ELF loader en proceso, luego crea un hilo dedicado que ejecuta tu payload y provoca un breakpoint para desacoplarse limpiamente.
Mitigaciones del hypervisor del PS5 a tener en cuenta (contextualizadas para esta técnica):
- XOM (execute-only .text) impide la lectura/escritura de .text del kernel.
- Limpiar CR0.WP o desactivar CR4.SMEP provoca un hypervisor vmexit (crash). Solo las escrituras de kernel que afecten únicamente datos son viables.
- mmap en userland está restringido a PROT_READ|PROT_WRITE por defecto. Conceder PROT_EXEC debe hacerse editando entradas de vm_map en la memoria del kernel.
Esta técnica es post-explotación: asume primitivas kernel R/W obtenidas por una cadena de exploits. Payloads públicos demuestran esto hasta firmware 10.01 en el momento de escribir.
Primitivas del kernel solo-datos
Descubrimiento de procesos vía allproc
FreeBSD mantiene una lista doblemente enlazada de procesos en .data del kernel en allproc. Con una primitiva de lectura de kernel, itera sobre ella para localizar nombres de procesos y PIDs:
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);
}
Notas:
- KERNEL_ADDRESS_ALLPROC depende del firmware.
- p_comm es un nombre de tamaño fijo; considera búsquedas pid->proc si es necesario.
Elevar credenciales para depuración (ucred)
En PS5, struct ucred incluye un campo Authority ID accesible vía proc->p_ucred. Escribir el Authority ID del debugger otorga ptrace/mdbg sobre otros procesos:
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 es específico de la familia de firmware PS5 y debe verificarse para cada versión.
- Después de esta escritura, el injector puede adjuntarse e instrumentar procesos de usuario vía ptrace/mdbg.
Bypassing RW-only user mappings: vm_map PROT_EXEC flip
El mmap en espacio de usuario puede estar limitado a PROT_READ|PROT_WRITE. FreeBSD rastrea el espacio de direcciones de un proceso en un vm_map formado por nodos vm_map_entry (BST más lista). Cada entry lleva los campos protection and 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;
};
Con kernel R/W puedes localizar el vm_map del objetivo y ajustar entry->protection |= PROT_EXEC (y, si es necesario, entry->max_protection). Notas prácticas de implementación:
- Recorre las entries ya sea linealmente vía next o usando el árbol balanceado (left/right) para búsqueda O(log n) por rango de direcciones.
- Elige una región RW conocida que controles (scratch buffer o archivo mapeado) y añade PROT_EXEC para poder situar código o thunks del loader.
- PS5 SDK code proporciona helpers para búsqueda rápida de map-entry y alternar protecciones.
Esto evade la política de mmap del userland editando directamente metadata propiedad del kernel.
Remote Function Invocation (RFI) with ptrace
FreeBSD no dispone de VirtualAllocEx/CreateRemoteThread al estilo Windows. En su lugar, haz que el objetivo se ejecute a sí mismo llamando funciones bajo control de ptrace:
- Adjunta al objetivo y selecciona un thread; PTRACE_ATTACH o flujos mdbg específicos de PS5 pueden aplicarse.
- Guarda el contexto del thread: registros, PC, SP, flags.
- Escribe los registros de argumentos según el ABI (x86_64 SysV or arm64 AAPCS64), ajusta el PC a la función objetivo y, opcionalmente, coloca argumentos adicionales/pila según sea necesario.
- Ejecuta paso a paso o continúa hasta una parada controlada (p.ej., software breakpoint o señal), luego lee los valores de retorno desde regs.
- Restaura el contexto original y continúa.
Casos de uso:
- Llamar a un ELF loader en proceso (p.ej., elfldr_load) con un puntero a tu imagen ELF en la memoria del objetivo.
- Invocar rutinas helper para obtener entrypoints retornados y punteros a payload-args.
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);
El loader mapea segments, resuelve imports, aplica relocations y devuelve la entry (a menudo un CRT bootstrap) además de un puntero opaque payload_args que tu stager pasa al main() del payload.
Stager multihilo y detach limpio
Un stager mínimo dentro del objetivo crea un nuevo pthread que ejecuta el main del ELF y luego dispara int3 para señalar al injector que haga detach:
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;
}
- Los punteros SCEFunctions/payload_args son proporcionados por el loader/SDK glue.
- Después del breakpoint y detach, el payload continúa en su propio hilo.
Flujo de extremo a extremo (implementación de referencia PS5)
Una implementación funcional se entrega como un pequeño servidor injector TCP más un script cliente:
- El servidor NineS escucha en TCP 9033 y recibe un encabezado que contiene el nombre del proceso objetivo seguido por la imagen ELF:
typedef struct __injector_data_t{
char proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
- Uso del cliente Python:
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
Ejemplo de payload Hello-world (registra en klog):
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
int main(){
klog_printf("Hello from PID %d\n", getpid());
return 0;
}
Consideraciones prácticas
- Offsets and constants (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) son específicos del firmware y deben actualizarse por cada versión.
- Las protecciones del hypervisor obligan a escrituras en el kernel solo de datos; no intentes parchear CR0.WP o CR4.SMEP.
- La memoria JIT es una alternativa: algunos procesos exponen PS5 JIT APIs para asignar páginas ejecutables. El cambio de protección de vm_map elimina la necesidad de depender de trucos de JIT/mirroring.
- Mantén robusto el guardado/restauración de registros; en caso de fallo puedes provocar un deadlock o crash del objetivo.
Herramientas públicas
- 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
Referencias
- 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
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.