macOS Thread Injection via Task port
Código
1. Sequestro de Thread
Inicialmente, a função task_threads()
é invocada na porta da tarefa para obter uma lista de threads da tarefa remota. Um thread é selecionado para sequestro. Esta abordagem difere dos métodos convencionais de injeção de código, pois a criação de um novo thread remoto é proibida devido à nova mitigação que bloqueia thread_create_running()
.
Para controlar o thread, é chamado thread_suspend()
, interrompendo sua execução.
As únicas operações permitidas no thread remoto envolvem parar e iniciar ele, recuperar e modificar seus valores de registro. Chamadas de função remotas são iniciadas configurando os registros x0
a x7
para os argumentos, configurando pc
para a função desejada e ativando o thread. Garantir que o 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 o thread remoto usando thread_set_exception_ports()
, configurando o registro lr
para um endereço inválido antes da chamada da função. Isso desencadeia uma exceção pós-execução da função, enviando uma mensagem para a porta de exceção, permitindo a inspeção do estado do thread para recuperar o valor de retorno. Alternativamente, como adotado do exploit triple_fetch de Ian Beer, lr
é configurado para fazer um loop infinito. Os registros do thread são então monitorados continuamente até que o pc
aponte para essa instrução.
2. Portas Mach para comunicação
A fase subsequente envolve o estabelecimento de portas Mach para facilitar a comunicação com o thread remoto. Essas portas são instrumentais na transferência de direitos de envio e recebimento arbitrários entre tarefas.
Para comunicação bidirecional, são criados dois direitos de recebimento Mach: um na tarefa local e outro na tarefa remota. Posteriormente, 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 está em transferir um direito de envio para esta porta para a tarefa remota.
Uma estratégia envolve aproveitar thread_set_special_port()
para colocar um direito de envio para a porta local no THREAD_KERNEL_PORT
do thread remoto. Em seguida, instrui-se o thread remoto a chamar mach_thread_self()
para recuperar o direito de envio.
Para a porta remota, o processo é essencialmente reverso. O thread remoto é direcionado a gerar uma porta Mach via mach_reply_port()
(como mach_port_allocate()
é inadequado devido ao seu mecanismo de retorno). Após a criação da porta, mach_port_insert_right()
é invocado no thread remoto para estabelecer um direito de envio. Este direito é então armazenado no kernel usando thread_set_special_port()
. De volta à tarefa local, thread_get_special_port()
é usado no thread remoto para adquirir um direito de envio para a porta Mach recém-alocada na tarefa remota.
A conclusão dessas etapas resulta no estabelecimento de portas Mach, preparando o terreno para a comunicação bidirecional.
3. Primitivas Básicas de Leitura/Gravação de Memória
Nesta seção, o foco está em utilizar a primitiva de execução para estabelecer primitivas básicas de leitura e gravação de memória. Esses passos iniciais são cruciais para obter mais controle sobre o processo remoto, embora as primitivas nesta fase não sirvam para muitos propósitos. Em breve, elas serão atualizadas para versões mais avançadas.
Leitura e Escrita de Memória Usando a Primitiva de Execução
O objetivo é realizar a leitura e escrita de memória usando funções específicas. Para ler memória, são usadas funções com a seguinte estrutura:
E para escrever na memória, funções semelhantes a esta estrutura são usadas:
Estas funções correspondem às instruções de montagem fornecidas:
Identificação de Funções Adequadas
Uma varredura de bibliotecas comuns revelou candidatos apropriados para essas operações:
Lendo Memória: A função
property_getName()
da biblioteca de tempo de execução Objective-C é identificada como uma função adequada para ler memória. A função é descrita abaixo:
Esta função atua efetivamente como a read_func
retornando 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 é um candidato adequado com o seguinte desmontagem:
Para realizar uma escrita de 64 bits em um endereço específico, a chamada remota é estruturada da seguinte forma:
Com essas primitivas estabelecidas, o palco está montado para criar memória compartilhada, marcando uma progressão significativa no controle do processo remoto.
4. Configuração de Memória Compartilhada
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 alavancar libxpc
e seu tipo de objeto OS_xpc_shmem
, que é construído sobre entradas de memória Mach.
Visão Geral do Processo:
Alocação de Memória:
Aloque a memória para compartilhamento usando
mach_vm_allocate()
.Use
xpc_shmem_create()
para criar um objetoOS_xpc_shmem
para a região de memória alocada. Essa função gerenciará a criação da entrada de memória Mach e armazenará o direito de envio Mach no deslocamento0x18
do objetoOS_xpc_shmem
.
Criando Memória Compartilhada no Processo Remoto:
Aloque memória para o objeto
OS_xpc_shmem
no processo remoto com uma chamada remota paramalloc()
.Copie o conteúdo do objeto
OS_xpc_shmem
local para o processo remoto. No entanto, essa cópia inicial terá nomes de entrada de memória Mach incorretos no deslocamento0x18
.
Corrigindo a Entrada de Memória Mach:
Utilize o método
thread_set_special_port()
para inserir um direito de envio para a entrada de memória Mach na tarefa remota.Corrija o campo de 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:
Valide o objeto
OS_xpc_shmem
remoto.Estabeleça 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.
Trechos de Código Adicionais
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:
5. Alcançando Controle Total
Após estabelecer com sucesso a memória compartilhada e obter capacidades de execução arbitrárias, essencialmente ganhamos controle total sobre o processo alvo. As principais funcionalidades que possibilitam esse controle são:
Operações de Memória Arbitrárias:
Realizar leituras de memória arbitrárias invocando
memcpy()
para copiar dados da região compartilhada.Executar escritas 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 exigem mais de 8 argumentos, organize os argumentos adicionais na pilha em conformidade com a convenção de chamada.
Transferência de Porta Mach:
Transferir portas Mach entre tarefas por meio de mensagens Mach via portas previamente estabelecidas.
Transferência de Descritor 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.
Considerações Importantes:
Garanta o uso adequado do
memcpy()
para operações de leitura/escrita de memória a fim de manter a estabilidade do sistema e a integridade dos dados.Ao transferir portas Mach ou descritores de arquivo, siga protocolos adequados e gerencie recursos de forma responsável para evitar vazamentos ou acessos não intencionais.
Ao aderir a essas diretrizes e utilizar a biblioteca threadexec
, é possível gerenciar e interagir eficientemente com processos em um nível granular, alcançando controle total sobre o processo alvo.
Referências
Last updated