mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/macos-hardening/macos-security-and-privilege-escalation
This commit is contained in:
parent
5458331d3c
commit
708cf9c321
@ -9,17 +9,17 @@
|
||||
|
||||
## 1. Sequestro de Thread
|
||||
|
||||
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 criar uma nova thread remota é proibido devido à nova mitigação que bloqueia `thread_create_running()`.
|
||||
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 criar uma nova thread remota é proibido devido à mitigação que bloqueia `thread_create_running()`.
|
||||
|
||||
Para controlar a thread, **`thread_suspend()`** é chamada, interrompendo sua execução.
|
||||
Para controlar a thread, `thread_suspend()` é chamada, interrompendo sua execução.
|
||||
|
||||
As únicas operações permitidas na thread remota envolvem **parar** e **iniciar** ela, **recuperar** e **modificar** seus valores de registradores. Chamadas de função remotas são iniciadas configurando os registradores `x0` a `x7` com 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.
|
||||
As únicas operações permitidas na thread remota envolvem **parar** e **iniciar** e **recuperar**/**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 retomando 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()`, configurando 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**.
|
||||
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.
|
||||
|
||||
## 2. Portas Mach para comunicaçã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.
|
||||
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/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.
|
||||
|
||||
@ -33,23 +33,23 @@ A conclusão desses passos resulta no estabelecimento de portas Mach, preparando
|
||||
|
||||
## 3. Primitivas Básicas de Leitura/Escrita de Memória
|
||||
|
||||
Nesta seção, o foco está em utilizar a primitiva 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 as primitivas nesta fase não sirvam para muitos propósitos. Em breve, elas serão atualizadas para versões mais avançadas.
|
||||
Nesta seção, o foco está em utilizar a primitiva de execução para estabelecer primitivas básicas de leitura/escrita de memória. Esses passos iniciais são cruciais para obter mais controle sobre o processo remoto, embora as primitivas neste estágio 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
|
||||
### Leitura e escrita de memória usando a primitiva de execução
|
||||
|
||||
O objetivo é realizar leitura e escrita de memória usando funções específicas. Para leitura de memória, funções que se assemelham à seguinte estrutura são usadas:
|
||||
O objetivo é realizar leitura e escrita de memória usando funções específicas. Para **ler memória**:
|
||||
```c
|
||||
uint64_t read_func(uint64_t *address) {
|
||||
return *address;
|
||||
}
|
||||
```
|
||||
E para escrever na memória, funções semelhantes a esta estrutura são usadas:
|
||||
Para **escrever na memória**:
|
||||
```c
|
||||
void write_func(uint64_t *address, uint64_t value) {
|
||||
*address = value;
|
||||
}
|
||||
```
|
||||
Essas funções correspondem às instruções de assembly dadas:
|
||||
Essas funções correspondem à seguinte montagem:
|
||||
```
|
||||
_read_func:
|
||||
ldr x0, [x0]
|
||||
@ -58,104 +58,116 @@ _write_func:
|
||||
str x1, [x0]
|
||||
ret
|
||||
```
|
||||
### Identificando Funções Adequadas
|
||||
### Identificando funções adequadas
|
||||
|
||||
Uma varredura em bibliotecas comuns revelou candidatos apropriados para essas operações:
|
||||
|
||||
1. **Lendo Memória:**
|
||||
A função `property_getName()` da [biblioteca de tempo de execução do Objective-C](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html) é identificada como uma função adequada para ler memória. A função é descrita abaixo:
|
||||
1. **Lendo memória — `property_getName()`** (libobjc):
|
||||
```c
|
||||
const char *property_getName(objc_property_t prop) {
|
||||
return prop->name;
|
||||
}
|
||||
```
|
||||
Esta função atua efetivamente como o `read_func`, retornando o primeiro campo de `objc_property_t`.
|
||||
|
||||
2. **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 a seguinte desassemblagem:
|
||||
2. **Escrevendo na memória — `_xpc_int64_set_value()`** (libxpc):
|
||||
```c
|
||||
__xpc_int64_set_value:
|
||||
str x1, [x0, #0x18]
|
||||
ret
|
||||
```
|
||||
Para realizar uma gravação de 64 bits em um endereço específico, a chamada remota é estruturada da seguinte forma:
|
||||
Para realizar uma gravação de 64 bits em um endereço arbitrário:
|
||||
```c
|
||||
_xpc_int64_set_value(address - 0x18, value)
|
||||
_xpc_int64_set_value(address - 0x18, value);
|
||||
```
|
||||
Com essas primitivas estabelecidas, o palco está preparado para criar memória compartilhada, marcando um progresso significativo 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 aproveitar `libxpc` e seu tipo de objeto `OS_xpc_shmem`, que é construído sobre entradas de memória Mach.
|
||||
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 utiliza `libxpc` e seu tipo de objeto `OS_xpc_shmem`, que é construído sobre entradas de memória Mach.
|
||||
|
||||
### Visão Geral do Processo:
|
||||
### Visão geral do processo
|
||||
|
||||
1. **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`.
|
||||
|
||||
2. **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`.
|
||||
|
||||
3. **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.
|
||||
|
||||
4. **Finalizando a Configuração da 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.
|
||||
|
||||
## Trechos de Código Adicionais
|
||||
|
||||
Para alocação de memória e criação de objeto de memória compartilhada:
|
||||
```c
|
||||
mach_vm_allocate();
|
||||
xpc_shmem_create();
|
||||
```
|
||||
Para criar e corrigir o objeto de memória compartilhada no processo remoto:
|
||||
```c
|
||||
malloc(); // for allocating memory remotely
|
||||
thread_set_special_port(); // for inserting send right
|
||||
```
|
||||
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.
|
||||
1. **Alocação de memória**
|
||||
* Alocar memória para compartilhamento usando `mach_vm_allocate()`.
|
||||
* Usar `xpc_shmem_create()` para criar um objeto `OS_xpc_shmem` para a região alocada.
|
||||
2. **Criando memória compartilhada no processo remoto**
|
||||
* Alocar memória para o objeto `OS_xpc_shmem` no processo remoto (`remote_malloc`).
|
||||
* Copiar o objeto de template local; a correção do direito de envio Mach embutido no deslocamento `0x18` ainda é necessária.
|
||||
3. **Corrigindo a entrada de memória Mach**
|
||||
* Inserir um direito de envio com `thread_set_special_port()` e sobrescrever o campo `0x18` com o nome da entrada remota.
|
||||
4. **Finalizando**
|
||||
* Validar o objeto remoto e mapeá-lo com uma chamada remota para `xpc_shmem_remote()`.
|
||||
|
||||
## 5. Obtendo Controle Total
|
||||
|
||||
Ao 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:
|
||||
Uma vez que a execução arbitrária e um canal de retorno de memória compartilhada estão disponíveis, você efetivamente possui o processo alvo:
|
||||
|
||||
1. **Operações de Memória Arbitrária**:
|
||||
* **R/W de memória arbitrária** — use `memcpy()` entre regiões locais e compartilhadas.
|
||||
* **Chamadas de função com > 8 args** — coloque os argumentos extras na pilha seguindo a convenção de chamada arm64.
|
||||
* **Transferência de porta Mach** — passe direitos em mensagens Mach através das portas estabelecidas.
|
||||
* **Transferência de descritor de arquivo** — aproveite fileports (veja *triple_fetch*).
|
||||
|
||||
- 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.
|
||||
Tudo isso está encapsulado na biblioteca [`threadexec`](https://github.com/bazad/threadexec) para fácil reutilização.
|
||||
|
||||
2. **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.
|
||||
## 6. Nuances do Apple Silicon (arm64e)
|
||||
|
||||
3. **Transferência de Portas Mach**:
|
||||
Em dispositivos Apple Silicon (arm64e), **Códigos de Autenticação de Ponteiros (PAC)** protegem todos os endereços de retorno e muitos ponteiros de função. Técnicas de sequestro de thread que *reutilizam código existente* continuam a funcionar porque os valores originais em `lr`/`pc` já possuem assinaturas PAC válidas. Problemas surgem quando você tenta pular para a memória controlada pelo atacante:
|
||||
|
||||
- Transferir portas Mach entre tarefas através de mensagens Mach via portas previamente estabelecidas.
|
||||
1. Alocar memória executável dentro do alvo (remote `mach_vm_allocate` + `mprotect(PROT_EXEC)`).
|
||||
2. Copiar sua carga útil.
|
||||
3. Dentro do processo *remoto*, assinar o ponteiro:
|
||||
```c
|
||||
uint64_t ptr = (uint64_t)payload;
|
||||
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
|
||||
```
|
||||
4. Defina `pc = ptr` no estado da thread sequestrada.
|
||||
|
||||
4. **Transferência de Descritores de Arquivo**:
|
||||
- Transferir descritores de arquivo entre processos usando fileports, uma técnica destacada por Ian Beer em `triple_fetch`.
|
||||
Alternativamente, mantenha a conformidade com PAC encadeando gadgets/funções existentes (ROP tradicional).
|
||||
|
||||
Esse controle abrangente está encapsulado na biblioteca [threadexec](https://github.com/bazad/threadexec), fornecendo uma implementação detalhada e uma API amigável para interação com o processo da vítima.
|
||||
## 7. Detecção e Fortalecimento com EndpointSecurity
|
||||
|
||||
## Considerações Importantes:
|
||||
O **EndpointSecurity (ES)** expõe eventos do kernel que permitem que defensores observem ou bloqueiem tentativas de injeção de thread:
|
||||
|
||||
- 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 portas Mach ou descritores de arquivo, siga os protocolos adequados e gerencie os recursos de forma responsável para evitar leaks ou acesso não intencional.
|
||||
* `ES_EVENT_TYPE_AUTH_GET_TASK` – acionado quando um processo solicita a porta de outra tarefa (por exemplo, `task_for_pid()`).
|
||||
* `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` – emitido sempre que uma thread é criada em uma tarefa *diferente*.
|
||||
* `ES_EVENT_TYPE_NOTIFY_THREAD_SET_STATE` (adicionado no macOS 14 Sonoma) – indica manipulação de registradores de uma thread existente.
|
||||
|
||||
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.
|
||||
Cliente Swift mínimo que imprime eventos de thread remota:
|
||||
```swift
|
||||
import EndpointSecurity
|
||||
|
||||
let client = try! ESClient(subscriptions: [.notifyRemoteThreadCreate]) {
|
||||
(_, msg) in
|
||||
if let evt = msg.remoteThreadCreate {
|
||||
print("[ALERT] remote thread in pid \(evt.target.pid) by pid \(evt.thread.pid)")
|
||||
}
|
||||
}
|
||||
RunLoop.main.run()
|
||||
```
|
||||
Consultando com **osquery** ≥ 5.8:
|
||||
```sql
|
||||
SELECT target_pid, source_pid, target_path
|
||||
FROM es_process_events
|
||||
WHERE event_type = 'REMOTE_THREAD_CREATE';
|
||||
```
|
||||
### Considerações sobre runtime endurecido
|
||||
|
||||
Distribuir seu aplicativo **sem** a concessão `com.apple.security.get-task-allow` impede que atacantes não-root obtenham seu task-port. A Proteção de Integridade do Sistema (SIP) ainda bloqueia o acesso a muitos binários da Apple, mas o software de terceiros deve optar explicitamente por sair.
|
||||
|
||||
## 8. Ferramentas Públicas Recentes (2023-2025)
|
||||
|
||||
| Ferramenta | Ano | Observações |
|
||||
|------------|-----|-------------|
|
||||
| [`task_vaccine`](https://github.com/rodionovd/task_vaccine) | 2023 | PoC compacta que demonstra sequestro de thread ciente de PAC no Ventura/Sonoma |
|
||||
| `remote_thread_es` | 2024 | Auxiliar de EndpointSecurity usado por vários fornecedores de EDR para exibir eventos `REMOTE_THREAD_CREATE` |
|
||||
|
||||
> Ler o código-fonte desses projetos é útil para entender as mudanças na API introduzidas no macOS 13/14 e para manter a compatibilidade entre Intel ↔ Apple Silicon.
|
||||
|
||||
## Referências
|
||||
|
||||
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
|
||||
- [https://github.com/rodionovd/task_vaccine](https://github.com/rodionovd/task_vaccine)
|
||||
- [https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create](https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user