macOS Thread Injection via Task port
Last updated
Last updated
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Inicialmente, a função task_threads()
é invocada na porta da tarefa para obter uma lista de threads da tarefa remota. Uma thread é selecionada para sequestro. Essa abordagem diverge dos métodos convencionais de injeção de código, pois a criação de uma nova thread remota é proibida devido à nova mitigação que bloqueia thread_create_running()
.
Para controlar a thread, thread_suspend()
é chamada, interrompendo sua execução.
As únicas operações permitidas na thread remota envolvem parar e iniciar a thread, recuperar e modificar seus valores de registradores. Chamadas de função remotas são iniciadas configurando os registradores x0
a x7
para os argumentos, configurando pc
para direcionar à função desejada e ativando a thread. Garantir que a thread não falhe após o retorno requer a detecção do retorno.
Uma estratégia envolve registrar um manipulador de exceção para a thread remota usando thread_set_exception_ports()
, definindo o registrador lr
para um endereço inválido antes da chamada da função. Isso aciona uma exceção após a execução da função, enviando uma mensagem para a porta de exceção, permitindo a inspeção do estado da thread para recuperar o valor de retorno. Alternativamente, como adotado do exploit triple_fetch de Ian Beer, lr
é configurado para loop infinito. Os registradores da thread são então monitorados continuamente até que pc
aponte para essa instrução.
A fase subsequente envolve estabelecer portas Mach para facilitar a comunicação com a thread remota. Essas portas são instrumentais na transferência de direitos de envio e recebimento arbitrários entre tarefas.
Para comunicação bidirecional, dois direitos de recebimento Mach são criados: um na tarefa local e o outro na tarefa remota. Subsequentemente, um direito de envio para cada porta é transferido para a tarefa correspondente, permitindo a troca de mensagens.
Focando na porta local, o direito de recebimento é mantido pela tarefa local. A porta é criada com mach_port_allocate()
. O desafio reside em transferir um direito de envio para esta porta na tarefa remota.
Uma estratégia envolve aproveitar thread_set_special_port()
para colocar um direito de envio na porta local na THREAD_KERNEL_PORT
da thread remota. Em seguida, a thread remota é instruída a chamar mach_thread_self()
para recuperar o direito de envio.
Para a porta remota, o processo é essencialmente invertido. A thread remota é direcionada a gerar uma porta Mach via mach_reply_port()
(já que mach_port_allocate()
não é adequada devido ao seu mecanismo de retorno). Após a criação da porta, mach_port_insert_right()
é invocado na thread remota para estabelecer um direito de envio. Esse direito é então armazenado no kernel usando thread_set_special_port()
. De volta à tarefa local, thread_get_special_port()
é usado na thread remota para adquirir um direito de envio para a nova porta Mach alocada na tarefa remota.
A conclusão desses passos resulta no estabelecimento de portas Mach, preparando o terreno para comunicação bidirecional.
Nesta seção, o foco está em utilizar o primitivo de execução para estabelecer primitivas básicas de leitura e escrita de memória. Esses passos iniciais são cruciais para obter mais controle sobre o processo remoto, embora os primitivos nesta fase não sirvam para muitos propósitos. Em breve, eles serão atualizados para versões mais avançadas.
O objetivo é realizar leitura e escrita de memória usando funções específicas. Para ler memória, funções que se assemelham à seguinte estrutura são usadas:
E para escrever na memória, funções semelhantes a esta estrutura são usadas:
Essas funções correspondem às instruções de assembly dadas:
Uma varredura em bibliotecas comuns revelou candidatos apropriados para essas operações:
Lendo Memória: A função property_getName()
da biblioteca de tempo de execução do Objective-C é identificada como uma função adequada para ler memória. A função é descrita abaixo:
Esta função atua efetivamente como o read_func
ao retornar o primeiro campo de objc_property_t
.
Escrevendo na Memória: Encontrar uma função pré-construída para escrever na memória é mais desafiador. No entanto, a função _xpc_int64_set_value()
da libxpc é uma candidata adequada com a seguinte desassemblagem:
Para realizar uma gravação de 64 bits em um endereço específico, a chamada remota é estruturada da seguinte forma:
Com essas primitivas estabelecidas, o palco está preparado para criar memória compartilhada, marcando um progresso significativo no controle do processo remoto.
O objetivo é estabelecer memória compartilhada entre tarefas locais e remotas, simplificando a transferência de dados e facilitando a chamada de funções com múltiplos argumentos. A abordagem envolve aproveitar libxpc
e seu tipo de objeto OS_xpc_shmem
, que é construído sobre entradas de memória Mach.
Alocação de Memória:
Alocar a memória para compartilhamento usando mach_vm_allocate()
.
Usar xpc_shmem_create()
para criar um objeto OS_xpc_shmem
para a região de memória alocada. Esta função gerenciará a criação da entrada de memória Mach e armazenará o direito de envio Mach no deslocamento 0x18
do objeto OS_xpc_shmem
.
Criando Memória Compartilhada no Processo Remoto:
Alocar memória para o objeto OS_xpc_shmem
no processo remoto com uma chamada remota para malloc()
.
Copiar o conteúdo do objeto local OS_xpc_shmem
para o processo remoto. No entanto, essa cópia inicial terá nomes de entradas de memória Mach incorretos no deslocamento 0x18
.
Corrigindo a Entrada de Memória Mach:
Utilizar o método thread_set_special_port()
para inserir um direito de envio para a entrada de memória Mach na tarefa remota.
Corrigir o campo da entrada de memória Mach no deslocamento 0x18
sobrescrevendo-o com o nome da entrada de memória remota.
Finalizando a Configuração de Memória Compartilhada:
Validar o objeto remoto OS_xpc_shmem
.
Estabelecer o mapeamento de memória compartilhada com uma chamada remota para xpc_shmem_remote()
.
Seguindo esses passos, a memória compartilhada entre as tarefas locais e remotas será configurada de forma eficiente, permitindo transferências de dados diretas e a execução de funções que requerem múltiplos argumentos.
Para alocação de memória e criação de objeto de memória compartilhada:
Para criar e corrigir o objeto de memória compartilhada no processo remoto:
Lembre-se de lidar corretamente com os detalhes dos ports Mach e nomes de entradas de memória para garantir que a configuração de memória compartilhada funcione corretamente.
Após estabelecer com sucesso a memória compartilhada e ganhar capacidades de execução arbitrária, essencialmente ganhamos controle total sobre o processo alvo. As principais funcionalidades que possibilitam esse controle são:
Operações de Memória Arbitrária:
Realizar leituras de memória arbitrárias invocando memcpy()
para copiar dados da região compartilhada.
Executar gravações de memória arbitrárias usando memcpy()
para transferir dados para a região compartilhada.
Manipulação de Chamadas de Função com Múltiplos Argumentos:
Para funções que requerem mais de 8 argumentos, organize os argumentos adicionais na pilha em conformidade com a convenção de chamada.
Transferência de Mach Port:
Transferir Mach ports entre tarefas através de mensagens Mach via ports previamente estabelecidos.
Transferência de Descritores de Arquivo:
Transferir descritores de arquivo entre processos usando fileports, uma técnica destacada por Ian Beer em triple_fetch
.
Esse controle abrangente está encapsulado na biblioteca threadexec, fornecendo uma implementação detalhada e uma API amigável para interação com o processo vítima.
Assegure o uso adequado de memcpy()
para operações de leitura/gravação de memória para manter a estabilidade do sistema e a integridade dos dados.
Ao transferir Mach ports ou descritores de arquivo, siga os protocolos adequados e gerencie os recursos de forma responsável para evitar leaks ou acesso não intencional.
Ao aderir a essas diretrizes e utilizar a biblioteca threadexec
, é possível gerenciar e interagir com processos de forma eficiente em um nível granular, alcançando controle total sobre o processo alvo.
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)