Este é um resumo do post de [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html), além disso, mais informações sobre o exploit usando esta técnica podem ser encontradas em [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
### Gerenciamento de memória no XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
O **espaço de endereços de memória virtual** para processos de usuário no iOS varia de **0x0 a 0x8000000000**. No entanto, esses endereços não mapeiam diretamente para a memória física. Em vez disso, o **kernel** usa **tabelas de páginas** para traduzir endereços virtuais em **endereços físicos** reais.
#### Níveis de Tabelas de Páginas no iOS
As tabelas de páginas são organizadas hierarquicamente em três níveis:
1.**Tabela de Páginas L1 (Nível 1)**:
* Cada entrada aqui representa uma grande faixa de memória virtual.
* Cobre **0x1000000000 bytes** (ou **256 GB**) de memória virtual.
2.**Tabela de Páginas L2 (Nível 2)**:
* Uma entrada aqui representa uma região menor de memória virtual, especificamente **0x2000000 bytes** (32 MB).
* Algumas entradas em uma tabela de páginas **mapeiam diretamente uma faixa de endereços virtuais** para uma faixa contígua de endereços físicos (como um atalho).
* Se um controle mais fino for necessário, uma entrada em um nível (por exemplo, L1) pode apontar para uma **tabela de páginas filha** no próximo nível (por exemplo, L2).
#### Exemplo: Mapeando um Endereço Virtual
Vamos supor que você tente acessar o endereço virtual **0x1000000000**:
1.**Tabela L1**:
* O kernel verifica a entrada da tabela de páginas L1 correspondente a este endereço virtual. Se tiver um **ponteiro para uma tabela de páginas L2**, ele vai para essa tabela L2.
2.**Tabela L2**:
* O kernel verifica a tabela de páginas L2 para um mapeamento mais detalhado. Se esta entrada apontar para uma **tabela de páginas L3**, ele prossegue para lá.
3.**Tabela L3**:
* O kernel consulta a entrada final L3, que aponta para o **endereço físico** da página de memória real.
#### Exemplo de Mapeamento de Endereço
Se você escrever o endereço físico **0x800004000** no primeiro índice da tabela L2, então:
* Endereços virtuais de **0x1000000000** a **0x1002000000** mapeiam para endereços físicos de **0x800004000** a **0x802004000**.
* Isso é um **mapeamento de bloco** no nível L2.
Alternativamente, se a entrada L2 apontar para uma tabela L3:
* Cada página de 4 KB na faixa de endereços virtuais **0x1000000000 -> 0x1002000000** seria mapeada por entradas individuais na tabela L3.
### Uso físico após a liberação
Um **uso físico após a liberação** (UAF) ocorre quando:
1. Um processo **aloca** alguma memória como **legível e gravável**.
2. As **tabelas de páginas** são atualizadas para mapear essa memória para um endereço físico específico que o processo pode acessar.
3. O processo **desaloca** (libera) a memória.
4. No entanto, devido a um **bug**, o kernel **esquece de remover o mapeamento** das tabelas de páginas, mesmo que marque a memória física correspondente como livre.
5. O kernel pode então **realocar essa memória física "liberada"** para outros fins, como **dados do kernel**.
Isso significa que o processo pode acessar **páginas de memória do kernel**, que podem conter dados ou estruturas sensíveis, potencialmente permitindo que um atacante **manipule a memória do kernel**.
### Estratégia de Exploração: Heap Spray
Como o atacante não pode controlar quais páginas específicas do kernel serão alocadas para a memória liberada, ele usa uma técnica chamada **heap spray**:
1. O atacante **cria um grande número de objetos IOSurface** na memória do kernel.
2. Cada objeto IOSurface contém um **valor mágico** em um de seus campos, facilitando a identificação.
3. Eles **escaneiam as páginas liberadas** para ver se algum desses objetos IOSurface caiu em uma página liberada.
3.**Ler/Escrever na Memória do Kernel**: Manipulando campos no objeto IOSurface, eles ganham a capacidade de realizar **leituras e gravações arbitrárias** na memória do kernel. Isso permite que eles:
if (self_task) *self_task = iosurface_get_receiver(k);
goto sprayDone;
}
}
}
}
sprayDone:
for (int i = 0; i <nSurfaceIDs;i++){
if (surfaceIDs[i] == info.surface) continue;
iosurface_release(client, surfaceIDs[i]);
}
free(surfaceIDs);
return 0;
}
```
### Conseguindo Leitura/Escrita no Kernel com IOSurface
Após conseguir controle sobre um objeto IOSurface na memória do kernel (mapeado para uma página física liberada acessível a partir do espaço do usuário), podemos usá-lo para **operações arbitrárias de leitura e escrita no kernel**.
**Campos Chave em IOSurface**
O objeto IOSurface possui dois campos cruciais:
1.**Ponteiro de Contagem de Uso**: Permite uma **leitura de 32 bits**.
2.**Ponteiro de Timestamp Indexado**: Permite uma **escrita de 64 bits**.
Ao sobrescrever esses ponteiros, redirecionamos eles para endereços arbitrários na memória do kernel, habilitando capacidades de leitura/escrita.
#### Leitura de 32 Bits no Kernel
Para realizar uma leitura:
1. Sobrescreva o **ponteiro de contagem de uso** para apontar para o endereço alvo menos um deslocamento de 0x14 bytes.
2. Use o método `get_use_count` para ler o valor naquele endereço.
1.**Acionar Uso-Físico Após Liberação**: Páginas liberadas estão disponíveis para reutilização.
2.**Spray Objetos IOSurface**: Alocar muitos objetos IOSurface com um "valor mágico" único na memória do kernel.
3.**Identificar IOSurface Acessível**: Localizar um IOSurface em uma página liberada que você controla.
4.**Abusar do Uso-Físico Após Liberação**: Modificar ponteiros no objeto IOSurface para habilitar **leitura/escrita** arbitrária no **kernel** via métodos IOSurface.
Com esses primitivos, o exploit fornece **leituras de 32 bits** e **escritas de 64 bits** na memória do kernel. Passos adicionais de jailbreak podem envolver primitivos de leitura/escrita mais estáveis, que podem exigir a superação de proteções adicionais (por exemplo, PPL em dispositivos arm64e mais novos).