FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study)
Reading time: 10 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
Overview
本页记录了在基于 FreeBSD 的 PlayStation 5 (PS5) 上实现的一种实用 Unix/BSD usermode process/ELF injection 技术。该方法在已有 kernel read/write (R/W) primitives 的情况下可推广到 FreeBSD 衍生版本。高层次步骤:
- 修补当前进程凭据 (ucred) 以授予调试器权限,从而对任意用户进程启用 ptrace/mdbg。
- 通过遍历内核的 allproc 链表来查找目标进程。
- 通过在目标的 vm_map 中对 vm_map_entry.protection 进行位操作(设置 |= PROT_EXEC)来绕过 PROT_EXEC 限制,方法是写入内核数据。
- 使用 ptrace 执行 Remote Function Invocation (RFI):挂起线程、设置寄存器以在目标内调用任意函数、恢复执行、收集返回值并恢复状态。
- 在目标进程内使用 in-process ELF loader 映射并运行任意 ELF payload,然后创建一个专用线程运行该 payload 并触发断点以干净地 detach。
需要注意的 PS5 hypervisor 缓解措施(针对本技术的上下文):
- XOM (execute-only .text) 阻止读取/写入内核 .text。
- 清除 CR0.WP 或禁用 CR4.SMEP 会导致 hypervisor vmexit(崩溃)。只有纯数据的内核写入是可行的。
- Userland mmap 默认限制为 PROT_READ|PROT_WRITE。授予 PROT_EXEC 必须通过修改内核内的 vm_map 条目来完成。
此技术属于 post-exploitation:它假设已有来自 exploit chain 的 kernel R/W primitives。公开的 payload 在撰写时已证明可用于到固件 10.01。
Kernel data-only primitives
Process discovery via allproc
FreeBSD 在内核 .data 的 allproc 处维护了一个双向链表。借助 kernel read primitive,遍历该链表以定位进程名和 PID:
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 查找。
提升用于调试的凭证 (ucred)
在 PS5 上,struct ucred 包含一个可以通过 proc->p_ucred 访问的 Authority ID 字段。写入 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 的用户映射: vm_map PROT_EXEC flip
用户态的 mmap 可能被限制为 PROT_READ|PROT_WRITE。FreeBSD 使用由 vm_map_entry 节点组成的 vm_map(BST 加链表)来跟踪进程的地址空间。每个条目包含 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;
};
在获得内核 R/W 权限后,你可以定位目标的 vm_map 并设置 entry->protection |= PROT_EXEC(如果需要,也可以设置 entry->max_protection)。实现要点:
- 通过 next 线性遍历条目,或使用平衡树(left/right)按地址范围进行 O(log n) 查找。
- 选择一个你可控的已知 RW 区域(scratch buffer 或 mapped file),并添加 PROT_EXEC,以便你能放置代码或 loader thunks。
- PS5 SDK 代码提供用于快速 map-entry 查找和切换保护的辅助函数。
这通过直接编辑内核拥有的元数据来绕过 userland 的 mmap 策略。
Remote Function Invocation (RFI) with ptrace
- 附加到目标并选择一个线程;可使用 PTRACE_ATTACH 或 PS5 特定的 mdbg 流程。
- 保存线程上下文:registers、PC、SP、flags。
- 按照 ABI(x86_64 SysV 或 arm64 AAPCS64)写入 argument registers,将 PC 设置为目标函数,并根据需要在堆栈中放置额外的 args/stack。
- 单步执行或继续运行直到受控停止(例如软件断点或 signal),然后从 regs 中读取返回值。
- 恢复原始上下文并继续执行。
使用场景:
- 在进程内调用 ELF loader(例如 elfldr_load),传入指向目标进程内 ELF 镜像的指针。
- 调用辅助例程以获取返回的 entrypoints 和 payload-args 指针。
驱动 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);
loader 会映射 segments、解析 imports、应用 relocations,并返回 entry(通常是 CRT bootstrap)以及一个不透明的 payload_args 指针,该指针由你的 stager 传给 payload 的 main()。
线程化 stager 与 干净的 detach
目标内的最小 stager 会创建一个新的 pthread 来运行 ELF 的 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 pointers 由 loader/SDK glue 提供。
- 在 breakpoint 和 detach 之后,payload 在其自己的 thread 中继续执行。
端到端流程(PS5 reference implementation)
一个可工作的实现以一个小型 TCP injector server 加上一个 client script 的形式提供:
- NineS server 在 TCP 9033 上监听,并接收包含目标进程名的 header,后面跟着 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;
}
实践注意事项
- Offsets and constants (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) 是固件特定的,必须随每个版本更新。
- Hypervisor 保护强制只做数据型内核写入;不要尝试修补 CR0.WP 或 CR4.SMEP。
- JIT memory 是一种替代方案:一些进程暴露 PS5 JIT APIs 来分配可执行页面。vm_map protection flip 消除了对 JIT/mirroring 技巧的依赖。
- 保持寄存器保存/恢复逻辑的健壮性;若失败,可能导致目标死锁或崩溃。
公共工具
- 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
- 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 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。