iOS Exploiting

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

iOS Exploit Mitigations

1. Code Signing / Runtime Signature Verification

Introduced early (iPhone OS → iOS) Esta é uma das proteções fundamentais: todo código executável (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) deve ser assinado criptograficamente por uma cadeia de certificados enraizada na confiança da Apple. Em tempo de execução, antes de carregar um binário na memória (ou antes de realizar saltos através de certas fronteiras), o sistema verifica sua assinatura. Se o código for modificado (bit-flipped, patched) ou não estiver assinado, o load falha.

  • Impede: a etapa “classic payload drop + execute” em cadeias de exploit; injeção arbitrária de código; modificar um binário existente para inserir lógica maliciosa.
  • Detalhe do mecanismo:
  • O Mach-O loader (e o dynamic linker) verifica páginas de código, segmentos, entitlements, team IDs e que a assinatura cobre o conteúdo do arquivo.
  • Para regiões de memória como JIT caches ou código gerado dinamicamente, a Apple exige que as páginas sejam assinadas ou validadas via APIs especiais (ex.: mprotect com verificações de code-sign).
  • A assinatura inclui entitlements e identificadores; o OS impõe que certas APIs ou capacidades privilegiadas exijam entitlements específicos que não podem ser forjados.
Example Suponha que um exploit obtenha code execution em um processo e tente escrever shellcode no heap e saltar para ele. No iOS, aquela página precisaria ser marcada como executável **e** satisfazer as restrições de code-signature. Como o shellcode não é assinado com o certificado da Apple, o salto falha ou o sistema rejeita tornar essa região de memória executável.

2. CoreTrust

Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust é o subsistema que realiza a validação de assinatura em tempo de execução de binários (incluindo binários do sistema e de usuário) contra o certificado root da Apple em vez de confiar em caches de confiança userland.

  • Impede: pós-instalação tampering de binários, técnicas de jailbreaking que tentam trocar ou patchar system libraries ou user apps; enganar o sistema substituindo binários confiáveis por equivalentes maliciosos.
  • Detalhe do mecanismo:
  • Em vez de confiar num banco local de confiança ou cache de certificados, o CoreTrust busca ou referencia o root da Apple diretamente ou verifica certificados intermediários numa cadeia segura.
  • Garante que modificações (ex.: no filesystem) em binários existentes sejam detectadas e rejeitadas.
  • Víncula entitlements, team IDs, flags de code signing e outros metadados ao binário no momento do load.
Example Um jailbreak poderia tentar substituir `SpringBoard` ou `libsystem` por uma versão patched para ganhar persistência. Mas quando o loader do OS ou CoreTrust verifica, ele nota a mismatch na assinatura (ou entitlements modificados) e se recusa a executar.

3. Data Execution Prevention (DEP / NX / W^X)

Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP impõe que páginas marcadas como writables (para dados) sejam non-executable, e páginas marcadas como executáveis sejam non-writable. Você não pode simplesmente escrever shellcode no heap ou stack e executá-lo.

  • Impede: execução direta de shellcode; classic buffer-overflow → jump para shellcode injetado.
  • Detalhe do mecanismo:
  • O MMU / flags de proteção de memória (via page tables) forçam a separação.
  • Qualquer tentativa de marcar uma página writável como executável dispara uma checagem do sistema (e é ou proibida ou requer aprovação de code-sign).
  • Em muitos casos, tornar páginas executáveis exige passar por APIs do OS que impõem restrições ou verificações adicionais.
Example Um overflow escreve shellcode no heap. O atacante tenta `mprotect(heap_addr, size, PROT_EXEC)` para torná-lo executável. Mas o sistema recusa ou valida que a nova página deve passar por constraints de code-sign (o que o shellcode não consegue).

4. Address Space Layout Randomization (ASLR)

Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR randomiza os endereços base de regiões chave da memória: libraries, heap, stack, etc., a cada lançamento de processo. Os endereços de gadgets mudam entre execuções.

  • Impede: hardcoding de endereços de gadgets para ROP/JOP; cadeias de exploit estáticas; saltos cegos para offsets conhecidos.
  • Detalhe do mecanismo:
  • Cada library / módulo dinâmico carregado é rebased em um offset randomizado.
  • Ponteiros base de stack e heap são randomizados (dentro de certos limites de entropia).
  • Às vezes outras regiões (ex.: mmap allocations) também são randomizadas.
  • Combinado com mitigações de information-leak, força o atacante a primeiro vazar um endereço ou pointer para descobrir os bases em tempo de execução.
Example Uma ROP chain espera um gadget em `0x….lib + offset`. Mas como `lib` é relocated de forma diferente a cada execução, a cadeia hardcoded falha. Um exploit deve primeiro vazar a base do módulo antes de computar endereços dos gadgets.

5. Kernel Address Space Layout Randomization (KASLR)

Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) Análogo ao ASLR de usuário, KASLR randomiza a base do kernel text e outras estruturas do kernel no boot.

  • Impede: exploits em nível de kernel que dependem de localização fixa do kernel code ou data; exploits de kernel estáticos.
  • Detalhe do mecanismo:
  • A cada boot, a base do kernel é randomizada (dentro de um range).
  • Estruturas de dados do kernel (como task_structs, vm_map, etc.) também podem ser relocadas ou offsetadas.
  • Atacantes precisam primeiro vazar ponteiros do kernel ou usar vulnerabilidades de information disclosure para computar offsets antes de hijackar estruturas ou código do kernel.
Example Uma vulnerabilidade local visa corromper um kernel function pointer (ex.: em uma `vtable`) em `KERN_BASE + offset`. Mas como `KERN_BASE` é desconhecido, o atacante deve vazá-lo primeiro (ex.: via um primitive de leitura) antes de computar o endereço correto para corromper.

6. Kernel Patch Protection (KPP / AMCC)

Introduced in newer iOS / A-series hardware (post around iOS 15–16 era or newer chips) KPP (aka AMCC) monitora continuamente a integridade das páginas de kernel text (via hash ou checksum). Se detectar tampering (patches, inline hooks, code modifications) fora de janelas permitidas, dispara um kernel panic ou reboot.

  • Impede: patching persistente do kernel (modificar instruções do kernel), inline hooks, sobrescritas estáticas de funções.
  • Detalhe do mecanismo:
  • Um módulo de hardware/firmware monitora a região de kernel text.
  • Periodicamente ou sob demanda re-hasheia as páginas e compara com valores esperados.
  • Se ocorrer mismatch fora de janelas benignas de atualização, provoca um panic no dispositivo (para evitar patches maliciosos persistentes).
  • Atacantes devem ou evitar janelas de detecção ou usar caminhos legítimos de patch.
Example Um exploit tenta patchar o prologue de uma função do kernel (ex.: `memcmp`) para interceptar chamadas. Mas KPP nota que a hash da página de código não corresponde ao valor esperado e aciona um kernel panic, travando o dispositivo antes que o patch se estabilize.

7. Kernel Text Read‐Only Region (KTRR)

Introduced in modern SoCs (post ~A12 / newer hardware) KTRR é um mecanismo hardware-enforced: uma vez que o kernel text é travado cedo durante o boot, ele se torna read-only a partir de EL1 (o kernel), impedindo escritas posteriores em páginas de código.

  • Impede: qualquer modificação ao kernel code após o boot (ex.: patching, in-place code injection) no nível de privilégio EL1.
  • Detalhe do mecanismo:
  • Durante o boot (na fase secure/bootloader), o memory controller (ou uma unidade segura de hardware) marca as páginas físicas contendo kernel text como read-only.
  • Mesmo que um exploit obtenha privilégios completos de kernel, ele não consegue escrever nessas páginas para patchar instruções.
  • Para modificá-las, o atacante deve primeiro comprometer a cadeia de boot, ou subverter o próprio KTRR.
Example Um exploit de escalation de privilégio entra em EL1 e escreve uma trampoline numa função do kernel (ex.: no handler de `syscall`). Mas como as páginas estão marcadas como read-only pelo KTRR, a escrita falha (ou dispara fault), então os patches não são aplicados.

8. Pointer Authentication Codes (PAC)

Introduced with ARMv8.3 (hardware), Apple beginning with A12 / iOS ~12+

  • PAC é um recurso de hardware introduzido no ARMv8.3-A para detectar o tampering de valores de pointer (return addresses, function pointers, certos data pointers) ao embutir uma pequena assinatura criptográfica (um “MAC”) nos bits altos não usados do pointer.
  • A assinatura (“PAC”) é calculada sobre o valor do pointer mais um modifier (um valor de contexto, ex.: stack pointer ou algum dado distintivo). Dessa forma o mesmo valor de pointer em contextos diferentes obtém PACs diferentes.
  • No momento do uso, antes de dereferenciar ou branchar via esse pointer, uma instrução de authenticate verifica o PAC. Se válido, o PAC é removido e obtém-se o pointer puro; se inválido, o pointer fica “poisoned” (ou um fault é gerado).
  • As chaves usadas para produzir/validar PACs vivem em registradores privilegiados (EL1, kernel) e não são diretamente legíveis do modo usuário.
  • Como nem todos os 64 bits de um pointer são usados em muitos sistemas (ex.: espaço de endereçamento de 48 bits), os bits altos são “spare” e podem conter o PAC sem alterar o endereço efetivo.

Architectural Basis & Key Types

  • ARMv8.3 introduz cinco chaves de 128-bit (cada uma implementada via dois registradores de 64-bit do sistema) para pointer authentication.

  • APIAKey — para instruction pointers (domínio “I”, chave A)

  • APIBKey — segunda chave para instruction pointers (domínio “I”, chave B)

  • APDAKey — para data pointers (domínio “D”, chave A)

  • APDBKey — para data pointers (domínio “D”, chave B)

  • APGAKey — chave “generic”, para assinar dados não-pointer ou usos genéricos

  • Essas chaves são armazenadas em registradores de sistema privilegiados (acessíveis só em EL1/EL2 etc.), não acessíveis do user mode.

  • O PAC é calculado via uma função criptográfica (ARM sugere QARMA como algoritmo) usando:

  1. O valor do pointer (porção canônica)
  2. Um modifier (um valor de contexto, como um salt)
  3. A secret key
  4. Alguma lógica interna de tweak Se o PAC resultante corresponder ao que está armazenado nos bits altos do pointer, a autenticação sucede.

Instruction Families

A convenção de nomes é: PAC / AUT / XPAC, então as letras do domínio.

  • PACxx instructions assinam um pointer e inserem um PAC
  • AUTxx instructions autenticam + removem (validam e removem o PAC)
  • XPACxx instructions removem sem validar

Domains / sufixos:

MnemonicMeaning / DomainKey / DomainExample Usage in Assembly
PACIASign instruction pointer with APIAKey“I, A”PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1
PACIBSign instruction pointer with APIBKey“I, B”PACIB X2, X3
PACDASign data pointer with APDAKey“D, A”PACDA X4, X5
PACDBSign data pointer with APDBKey“D, B”PACDB X6, X7
PACG / PACGAGeneric (non-pointer) signing with APGAKey“G”PACGA X8, X9, X10 (sign X9 with modifier X10 into X8)
AUTIAAuthenticate APIA-signed instruction pointer & strip PAC“I, A”AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip
AUTIBAuthenticate APIB domain“I, B”AUTIB X2, X3
AUTDAAuthenticate APDA-signed data pointer“D, A”AUTDA X4, X5
AUTDBAuthenticate APDB-signed data pointer“D, B”AUTDB X6, X7
AUTGAAuthenticate generic / blob (APGA)“G”AUTGA X8, X9, X10 (validate generic)
XPACIStrip PAC (instruction pointer, no validation)“I”XPACI X0 — remove PAC from X0 (instruction domain)
XPACDStrip PAC (data pointer, no validation)“D”XPACD X4 — remove PAC from data pointer in X4

There are specialized / alias forms:

  • PACIASP is shorthand for PACIA X30, SP (sign the link register using SP as modifier)
  • AUTIASP is AUTIA X30, SP (authenticate link register with SP)
  • Combined forms like RETAA, RETAB (authenticate-and-return) or BLRAA (authenticate & branch) exist in ARM extensions / compiler support.
  • Also zero-modifier variants: PACIZA / PACIZB where the modifier is implicitly zero, etc.

Modifiers

O objetivo principal do modifier é vincular o PAC a um contexto específico de modo que o mesmo endereço assinado em diferentes contextos gere PACs diferentes. É como adicionar um salt a um hash.

Portanto:

  • O modifier é um valor de contexto (outro registrador) que é misturado na computação do PAC. Escolhas típicas: o stack pointer (SP), um frame pointer, ou algum object ID.
  • Usar SP como modifier é comum para signing do return address: o PAC fica ligado ao frame de stack específico. Se você tentar reutilizar o LR em um frame diferente, o modifier muda e a validação do PAC falha.
  • O mesmo valor de pointer assinado sob modifiers diferentes produz PACs diferentes.
  • O modifier não precisa ser secreto, mas idealmente não é controlado pelo atacante.
  • Para instruções que assinam ou verificam pointers onde não existe um modifier significativo, algumas formas usam zero ou uma constante implícita.

Apple / iOS / XNU Customizations & Observations

  • A implementação de PAC da Apple inclui diversificadores por boot de modo que chaves ou tweaks mudam a cada inicialização, prevenindo reutilização entre boots.
  • Eles também incluem mitigações cross-domain para que PACs assinados em user mode não sejam facilmente reutilizados em kernel mode, etc.
  • No Apple M1 / Apple Silicon, engenharia reversa mostrou que existem nove tipos de modifier e registradores de sistema Apple-specific para controle de chaves.
  • A Apple usa PAC em muitos subsistemas do kernel: signing de return addresses, integridade de pointers em dados do kernel, signed thread contexts, etc.
  • Google Project Zero demonstrou que sob um powerful memory read/write primitive no kernel, era possível forjar kernel PACs (para chaves A) em dispositivos da era A12, mas a Apple corrigiu muitos desses caminhos.
  • No sistema Apple, algumas chaves são globais ao kernel, enquanto processos de usuário podem obter randomness de chave por processo.

PAC Bypasses

  1. Kernel-mode PAC: theoretical vs real bypasses
  • Porque as chaves e a lógica de kernel PAC são rigidamente controladas (registradores privilegiados, diversificadores, isolamento de domínio), forjar pointers assinados arbitrariamente do kernel é muito difícil.
  • Azad’s 2020 “iOS Kernel PAC, One Year Later” reportou que em iOS 12-13 ele encontrou alguns bypasses parciais (signing gadgets, reuse de estados assinados, indirect branches não protegidos) mas nenhum bypass genérico completo. bazad.github.io
  • As customizações “Dark Magic” da Apple estreitaram ainda mais as superfícies exploráveis (domain switching, per-key enabling bits). i.blackhat.com
  • Há um conhecido kernel PAC bypass CVE-2023-32424 em Apple silicon (M1/M2) reportado por Zecao Cai et al. i.blackhat.com
  • Mas esses bypasses frequentemente dependem de gadgets muito específicos ou bugs de implementação; eles não são bypasses de uso geral.

Logo, kernel PAC é considerado altamente robusto, embora não perfeito.

  1. User-mode / runtime PAC bypass techniques

Esses são mais comuns, e exploram imperfeições em como PAC é aplicado ou usado em dynamic linking / runtime frameworks. Abaixo estão classes, com exemplos.

2.1 Shared Cache / A key issues

  • O dyld shared cache é um grande blob pré-linkado de system frameworks e libraries. Porque é tão amplamente compartilhado, function pointers dentro do shared cache são “pre-signed” e então usados por muitos processos. Atacantes miram esses pointers já assinados como “PAC oracles”.

  • Algumas técnicas de bypass tentam extrair ou reutilizar pointers assinados com A-key presentes no shared cache e reaproveitá-los em gadgets.

  • A talk “No Clicks Required” descreve construir um oracle sobre o shared cache para inferir endereços relativos e combinar isso com pointers assinados para contornar PAC. saelo.github.io

  • Além disso, imports de function pointers de shared libraries em userspace foram encontrados insuficientemente protegidos por PAC, permitindo a um atacante obter function pointers sem alterar sua assinatura. (Project Zero bug entry) bugs.chromium.org

2.2 dlsym(3) / dynamic symbol resolution

  • Um bypass conhecido é chamar dlsym() para obter um already signed function pointer (assinado com A-key, diversifier zero) e então usá-lo. Como dlsym retorna um pointer legitimamente assinado, usá-lo contorna a necessidade de forjar PAC.

  • O blog da Epsilon detalha como alguns bypasses exploram isso: chamar dlsym("someSym") retorna um pointer assinado e pode ser usado para indirect calls. blog.epsilon-sec.com

  • A Synacktiv em “iOS 18.4 — dlsym considered harmful” descreve um bug: alguns símbolos resolvidos via dlsym no iOS 18.4 retornam pointers que são incorretamente assinados (ou com diversificadores bugados), permitindo bypass involuntário de PAC. Synacktiv

  • A lógica no dyld para dlsym inclui: quando result->isCode, eles assinam o pointer retornado com __builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), i.e. contexto zero. blog.epsilon-sec.com

Assim, dlsym é um vetor frequente em bypasses de PAC em user-mode.

2.3 Other DYLD / runtime relocations

  • O loader DYLD e a lógica de relocação dinâmica são complexos e às vezes mapeiam páginas temporariamente como read/write para realizar relocations, depois as retornam para read-only. Atacantes exploram essas janelas. A talk da Synacktiv descreve “Operation Triangulation”, um bypass baseado em timing de PAC via relocations dinâmicas. Synacktiv

  • Páginas do DYLD agora são protegidas com SPRR / VM_FLAGS_TPRO (algumas flags de proteção para dyld). Mas versões anteriores tinham guardas mais fracos. Synacktiv

  • Em cadeias de exploit do WebKit, o DYLD loader é frequentemente alvo para bypass de PAC. Os slides mencionam que muitos bypasses de PAC miraram o DYLD loader (via relocation, interposer hooks). Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • Em cadeias de exploit userland, runtime methods do Objective-C como NSPredicate, NSExpression ou NSInvocation são usadas para contrabandear chamadas de controle sem apontar explicitamente para forging de pointers.

  • Em iOS mais antigos (antes do PAC), um exploit usou fake NSInvocation objects para chamar selectors arbitrários em memória controlada. Com PAC, modificações são necessárias. Mas a técnica SLOP (SeLector Oriented Programming) foi estendida sob PAC também. Project Zero

  • A técnica SLOP original permitia encadear chamadas ObjC criando invocations falsas; o bypass depende do fato de que ISA ou selector pointers às vezes não são totalmente protegidos por PAC. Project Zero

  • Em ambientes onde pointer authentication é aplicado parcialmente, methods / selectors / target pointers podem não ter proteção PAC completa, oferecendo espaço para bypass.

Example Flow

Example Signing & Authenticating ``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret

; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer

; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target

; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label

</details>

<details>
<summary>Example</summary>
A buffer overflow overwrites a return address on the stack. The attacker writes the target gadget address but cannot compute the correct PAC. When the function returns, the CPU’s `AUTIA` instruction faults because the PAC mismatch. The chain fails.
Project Zero’s analysis on A12 (iPhone XS) showed how Apple’s PAC is used and methods of forging PACs if an attacker has a memory read/write primitive.
</details>


### 9. **Branch Target Identification (BTI)**
**Introduzido com ARMv8.5 (hardware posterior)**
BTI é uma feature de hardware que verifica **indirect branch targets**: ao executar `blr` ou chamadas/jumps indiretos, o target deve começar com um **BTI landing pad** (`BTI j` ou `BTI c`). Saltar para endereços de gadget que não têm esse landing pad dispara uma exceção.

As notas de implementação do LLVM descrevem três variantes das instruções BTI e como elas mapeiam para tipos de branch.

| BTI Variant | What it permits (which branch types) | Typical placement / use case |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets of *call*-style indirect branches (e.g. `BLR`, or `BR` using X16/X17) | Put at entry of functions that may be called indirectly |
| **BTI J** | Targets of *jump*-style branches (e.g. `BR` used for tail calls) | Placed at the beginning of blocks reachable by jump tables or tail-calls |
| **BTI JC** | Acts as both C and J | Can be targeted by either call or jump branches |

- Em código compilado com branch target enforcement, os compiladores inserem uma instrução BTI (C, J, ou JC) em cada indirect-branch target válido (começos de funções ou blocos alcançáveis por jumps) para que indirect branches só tenham sucesso nesses lugares.
- **Direct branches / calls** (isto é, endereços fixos `B`, `BL`) **não são restringidos** por BTI. A suposição é que code pages são confiáveis e um atacante não pode alterá-las (logo direct branches são seguros).
- Também, instruções de **RET / return** geralmente não são restringidas por BTI porque os endereços de retorno são protegidos via PAC ou mecanismos de return signing.

#### Mechanism and enforcement

- Quando a CPU decodifica um **indirect branch (BLR / BR)** numa página marcada como “guarded / BTI-enabled,” ela verifica se a primeira instrução do endereço alvo é um BTI válido (C, J, ou JC conforme permitido). Se não for, ocorre uma **Branch Target Exception**.
- A codificação da instrução BTI foi projetada para reutilizar opcodes previamente reservados para NOPs (em versões anteriores do ARM). Assim, binários com BTI permanecem backward-compatible: em hardware sem suporte a BTI, essas instruções atuam como NOPs.
- Os passes do compilador que adicionam BTIs inserem-nos apenas onde necessário: funções que podem ser chamadas indiretamente, ou basic blocks alvo de jumps.
- Alguns patches e código do LLVM mostram que BTI não é inserido para *todos* os basic blocks — apenas aqueles que são potenciais targets de branch (por exemplo, provenientes de switch / jump tables).

#### BTI + PAC synergy

PAC protege o valor do ponteiro (a origem) — garante que a cadeia de indirect calls / returns não foi adulterada.

BTI garante que mesmo um ponteiro válido só pode apontar para entry points devidamente marcados.

Combinados, um atacante precisa tanto de um ponteiro válido com PAC correto quanto do target contendo um BTI. Isso aumenta a dificuldade de construir gadgets de exploit.

#### Example


<details>
<summary>Example</summary>
An exploit tries to pivot into gadget at `0xABCDEF` that doesn’t start with `BTI c`. The CPU, upon executing `blr x0`, checks the target and faults because the instruction alignment doesn’t include a valid landing pad. Thus many gadgets become unusable unless they include BTI prefix.
</details>


### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Introduzido em extensões ARMv8 mais recentes / suporte iOS (para kernel hardened)**

#### PAN (Privileged Access Never)

- **PAN** é uma feature introduzida no **ARMv8.1-A** que previne que código **privilegiado** (EL1 ou EL2) **leia ou escreva** memória marcada como **user-accessible (EL0)**, a menos que PAN seja explicitamente desabilitado.
- A ideia: mesmo que o kernel seja enganado ou comprometido, ele não pode desreferenciar arbitrariamente pointers de user-space sem primeiro *limpar* PAN, reduzindo riscos de exploits do tipo **`ret2usr`** ou uso indevido de buffers controlados pelo usuário.
- Quando PAN está habilitado (PSTATE.PAN = 1), qualquer instrução privileged de load/store acessando um endereço virtual que seja “accessible at EL0” dispara uma **permission fault**.
- O kernel, quando precisa legitimamente acessar memória user-space (ex.: copiar dados de/para buffers do usuário), deve **temporariamente desabilitar PAN** (ou usar instruções de “unprivileged load/store”) para permitir esse acesso.
- No Linux on ARM64, o suporte a PAN foi introduzido por volta de 2015: patches no kernel adicionaram detecção da feature, e substituíram `get_user` / `put_user` etc. por variantes que limpam PAN ao redor dos acessos à memória do usuário.

**Key nuance / limitation / bug**
- Como notado por Siguza e outros, um bug de especificação (ou comportamento ambíguo) no design do ARM significa que mappings de usuário **execute-only** (`--x`) podem **não disparar PAN**. Em outras palavras, se uma página de usuário for marcada executável mas sem permissão de leitura, a tentativa do kernel de ler dela pode ignorar PAN porque a arquitetura considera “accessible at EL0” como requerendo permissão de leitura, não apenas executável. Isso leva a um bypass de PAN em certas configurações.
- Por causa disso, se iOS / XNU permitem páginas de usuário execute-only (como alguns setups de JIT ou code-cache podem fazer), o kernel pode acidentalmente ler delas mesmo com PAN habilitado. Esta é uma área sutil e conhecida por ser potencialmente explorável em alguns sistemas ARMv8+.

#### PXN (Privileged eXecute Never)

- **PXN** é um bit nas page table entries (em entradas leaf ou block) que indica que a página é **não-executável quando rodando em modo privilegiado** (i.e. quando EL1 executa nela).
- PXN evita que o kernel (ou qualquer código privilegiado) pule para ou execute instruções de páginas de user-space mesmo se o controle for desviado. Na prática, impede redirecionamento de control-flow no kernel para memória do usuário.
- Combinado com PAN, isso garante que:
1. O kernel não pode (por padrão) ler ou escrever dados do user-space (PAN)
2. O kernel não pode executar código do user-space (PXN)
- No formato de page table do ARMv8, as entradas leaf têm um bit `PXN` (e também `UXN` para unprivileged execute-never) nos bits de atributo.

Assim, mesmo que o kernel tenha um function pointer corrompido apontando para memória de usuário e tente branchar para lá, o bit PXN causaria uma falha.

#### Memory-permission model & how PAN and PXN map to page table bits

Para entender como PAN / PXN funcionam, é preciso ver como a tradução e o modelo de permissões do ARM operam (simplificado):

- Cada page ou block entry tem campos de atributo incluindo **AP[2:1]** para access permissions (read/write, privileged vs unprivileged) e bits **UXN / PXN** para restrições execute-never.
- Quando PSTATE.PAN está 1 (habilitado), o hardware aplica semântica modificada: acessos privilegiados a páginas marcadas como “accessible by EL0” (i.e. acessíveis pelo usuário) são proibidos (fault).
- Por causa do bug mencionado, páginas marcadas apenas como executáveis (sem permissão de leitura) podem não contar como “accessible by EL0” em algumas implementações, assim contornando PAN.
- Quando o bit PXN de uma página está setado, mesmo que o fetch de instrução venha de um nível de privilégio mais alto, a execução é proibida.

#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)

Numa arquitetura de kernel hardened (como a que a Apple pode usar):

- O kernel habilita PAN por padrão (assim código privilegiado fica restrito).
- Em caminhos que precisam legitimamente ler ou escrever buffers do usuário (ex.: copia de buffer de syscall, I/O, read/write user pointer), o kernel temporariamente **desabilita PAN** ou usa instruções especiais para sobrepor.
- Depois de terminar o acesso a dados do usuário, deve re-habilitar PAN.
- PXN é imposto via page tables: páginas de usuário têm PXN = 1 (assim o kernel não pode executá-las), páginas do kernel não têm PXN (então o código do kernel pode executar).
- O kernel deve garantir que nenhum caminho de execução cause fluxo para regiões de memória do usuário (o que contornaria PXN) — assim chains de exploit que dependem de “jump into user-controlled shellcode” são bloqueadas.

Devido ao bypass de PAN via páginas execute-only mencionado, num sistema real, a Apple poderia desabilitar ou não permitir páginas execute-only de usuário, ou contornar a fraqueza da especificação com patches.

#### Attack surfaces, bypasses, and mitigations

- **PAN bypass via execute-only pages**: como discutido, a spec deixa uma lacuna: páginas de usuário com execute-only (sem permissão de leitura) podem não ser consideradas “accessible at EL0,” então PAN não bloqueará leituras do kernel nessas páginas em algumas implementações. Isso dá ao atacante um caminho incomum para fornecer dados via seções “execute-only”.
- **Temporal window exploit**: se o kernel desabilitar PAN por uma janela maior do que o necessário, uma race ou caminho malicioso pode explorar essa janela para realizar acessos não intencionais à memória do usuário.
- **Forgotten re-enable**: se caminhos de código falham em re-habilitar PAN, operações subsequentes do kernel podem acessar memória de usuário de forma incorreta.
- **Misconfiguration of PXN**: se as page tables não setarem PXN nas páginas de usuário ou mapearem incorretamente páginas de código de usuário, o kernel pode ser enganado a executar código do user-space.
- **Speculation / side-channels**: análogo a bypasses especulativos, pode haver efeitos microarquiteturais transitórios que causem violações temporárias das checagens PAN / PXN (embora tais ataques dependam muito do design da CPU).
- **Complex interactions**: em features mais avançadas (ex.: JIT, shared memory, regiões de código just-in-time), o kernel pode precisar de controlo fino para permitir certos acessos ou execuções em regiões mapeadas no usuário; projetar isso de forma segura sob as restrições PAN/PXN não é trivial.

#### Example

<details>
<summary>Code Example</summary>
Here are illustrative pseudo-assembly sequences showing enabling/disabling PAN around user memory access, and how a fault might occur.

// Suppose kernel entry point, PAN is enabled (privileged code cannot access user memory by default)

; Kernel receives a syscall with user pointer in X0 ; wants to read an integer from user space mov X1, X0 ; X1 = user pointer

; disable PAN to allow privileged access to user memory MSR PSTATE.PAN, #0 ; clear PAN bit, disabling the restriction

ldr W2, [X1] ; now allowed load from user address

; re-enable PAN before doing other kernel logic MSR PSTATE.PAN, #1 ; set PAN

; … further kernel work …

; Later, suppose an exploit corrupts a pointer to a user-space code page and jumps there BR X3 ; branch to X3 (which points into user memory)

; Because the target page is marked PXN = 1 for privileged execution, ; the CPU throws an exception (fault) and rejects execution

Se o kernel tivesse **não** definido PXN nessa página de usuário, então o branch poderia ter sucesso — o que seria inseguro.

Se o kernel esquecer de reativar PAN após acesso à memória do usuário, abre-se uma janela onde lógica adicional do kernel pode acidentalmente ler/gravar memória de usuário arbitrária.

Se o ponteiro do usuário aponta para uma página execute-only (página de usuário com apenas permissão de execução, sem leitura/gravação), sob o bug de especificação do PAN, `ldr W2, [X1]` pode **não** causar fault mesmo com PAN habilitado, permitindo um exploit de bypass, dependendo da implementação.

</details>

<details>
<summary>Example</summary>
Uma vulnerabilidade no kernel tenta pegar um ponteiro de função fornecido pelo usuário e chamá-lo em contexto de kernel (i.e. `call user_buffer`). Sob PAN/PXN, essa operação é proibida ou causa fault.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI significa que o top byte (byte mais significativo) de um ponteiro 64-bit é ignorado pela tradução de endereços. Isso permite que o OS ou o hardware embuta **tag bits** no top byte do ponteiro sem afetar o endereço real.

- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). It is a hardware feature (available in many ARMv8+ implementations) that **ignores the top 8 bits** (bits 63:56) of a 64-bit pointer when performing **address translation / load/store / instruction fetch**.
- In effect, the CPU treats a pointer `0xTTxxxx_xxxx_xxxx` (where `TT` = top byte) as `0x00xxxx_xxxx_xxxx` for the purposes of address translation, ignoring (masking off) the top byte. The top byte can be used by software to store **metadata / tag bits**.
- This gives software “free” in-band space to embed a byte of tag in each pointer without altering which memory location it refers to.
- The architecture ensures that loads, stores, and instruction fetch treat the pointer with its top byte masked (i.e. tag stripped off) before performing the actual memory access.

Thus TBI decouples the **logical pointer** (pointer + tag) from the **physical address** used for memory operations.

#### Why TBI: Use cases and motivation

- **Pointer tagging / metadata**: Você pode armazenar metadados extras (e.g. tipo de objeto, versão, bounds, integrity tags) nesse top byte. Quando você depois usa o ponteiro, o tag é ignorado ao nível do hardware, então não é preciso removê-lo manualmente para o acesso à memória.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI é o mecanismo de hardware base sobre o qual MTE se constrói. Em ARMv8.5, a **Memory Tagging Extension** usa os bits 59:56 do ponteiro como uma **logical tag** e os compara com um **allocation tag** armazenado na memória.
- **Enhanced security & integrity**: Combinando TBI com pointer authentication (PAC) ou checagens em tempo de execução, você pode exigir que não só o valor do ponteiro, mas também o tag esteja correto. Um atacante que sobrescreve um ponteiro sem o tag correto resultará em tag mismatch.
- **Compatibility**: Como TBI é opcional e os tag bits são ignorados pelo hardware, código legado sem tags continua a operar normalmente. Os tag bits efetivamente tornam-se bits “don’t care” para código antigo.

#### Example
<details>
<summary>Example</summary>
Um ponteiro de função incluía um tag no seu top byte (por exemplo `0xAA`). Um exploit sobrescreve os bits baixos do ponteiro mas negligencia o tag, então quando o kernel verifica ou sanitiza, o ponteiro falha ou é rejeitado.
</details>

---

### 12. **Page Protection Layer (PPL)**
**Introduced in late iOS / modern hardware (iOS ~17 / Apple silicon / high-end models)** (some reports show PPL circa macOS / Apple silicon, but Apple is bringing analogous protections to iOS)

- PPL é projetado como uma **fronteira de proteção intra-kernel**: mesmo se o kernel (EL1) for comprometido e tiver capacidades de leitura/gravação, **não deveria ser capaz de modificar livremente** certas **páginas sensíveis** (especialmente page tables, metadata de code-signing, páginas de código do kernel, entitlements, trust caches, etc.).
- Ele efetivamente cria um **“kernel dentro do kernel”** — um componente menor e confiável (PPL) com **privilégios elevados** que sozinho pode modificar páginas protegidas. Outro código do kernel deve chamar rotinas do PPL para efetuar mudanças.
- Isso reduz a superfície de ataque para exploits de kernel: mesmo com R/W/execute arbitrário em modo kernel, o código do exploit também precisa, de alguma forma, entrar no domínio PPL (ou contorná-lo) para modificar estruturas críticas.
- Em Apple silicon mais recentes (A15+ / M2+), a Apple está migrando para **SPTM (Secure Page Table Monitor)**, que em muitos casos substitui PPL para proteção de page-tables nessas plataformas.

Here’s how PPL is believed to operate, based on public analysis:

#### Use of APRR / permission routing (APRR = Access Permission ReRouting)

- Apple hardware uses a mechanism called **APRR (Access Permission ReRouting)**, which allows page table entries (PTEs) to contain small indices, rather than full permission bits. Those indices are mapped via APRR registers to actual permissions. This allows dynamic remapping of permissions per domain.
- PPL leverages APRR to segregate privilege within kernel context: only the PPL domain is permitted to update the mapping between indices and effective permissions. That is, when non-PPL kernel code writes a PTE or tries to flip permission bits, the APRR logic disallows it (or enforces read-only mapping).
- PPL code itself runs in a restricted region (e.g. `__PPLTEXT`) which is normally non-executable or non-writable until entry gates temporarily allow it. The kernel calls PPL entry points (“PPL routines”) to perform sensitive operations.

#### Gate / Entry & Exit

- When the kernel needs to modify a protected page (e.g. change permissions of a kernel code page, or modify page tables), it calls into a **PPL wrapper** routine, which does validation and then transitions into the PPL domain. Outside that domain, the protected pages are effectively read-only or non-modifiable by the main kernel.
- During PPL entry, the APRR mappings are adjusted so that memory pages in the PPL region are set to **executable & writable** within PPL. Upon exit, they are returned to read-only / non-writable. This ensures that only well-audited PPL routines can write to protected pages.
- Outside PPL, attempts by kernel code to write to those protected pages will fault (permission denied) because the APRR mapping for that code domain doesn’t permit writing.

#### Protected page categories

The pages that PPL typically protects include:

- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, especially those containing critical logic
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Other high-value kernel structures where a patch would allow bypassing signature checks or credentials manipulation

The idea is that even if the kernel memory is fully controlled, the attacker cannot simply patch or rewrite these pages, unless they also compromise PPL routines or bypass PPL.


#### Known Bypasses & Vulnerabilities

1. **Project Zero’s PPL bypass (stale TLB trick)**

- A public writeup by Project Zero describes a bypass involving **stale TLB entries**.
- The idea:

1. Allocate two physical pages A and B, mark them as PPL pages (so they are protected).
2. Map two virtual addresses P and Q whose L3 translation table pages come from A and B.
3. Spin a thread to continuously access Q, keeping its TLB entry alive.
4. Call `pmap_remove_options()` to remove mappings starting at P; due to a bug, the code mistakenly removes the TTEs for both P and Q, but only invalidates the TLB entry for P, leaving Q’s stale entry live.
5. Reuse B (page Q’s table) to map arbitrary memory (e.g. PPL-protected pages). Because the stale TLB entry still maps Q’s old mapping, that mapping remains valid for that context.
6. Through this, the attacker can put writable mapping of PPL-protected pages in place without going through PPL interface.

- This exploit required fine control of physical mapping and TLB behavior. It demonstrates that a security boundary relying on TLB / mapping correctness must be extremely careful about TLB invalidations and mapping consistency.

- Project Zero commented that bypasses like this are subtle and rare, but possible in complex systems. Still, they regard PPL as a solid mitigation.

2. **Other potential hazards & constraints**

- If a kernel exploit can directly enter PPL routines (via calling the PPL wrappers), it might bypass restrictions. Thus argument validation is critical.
- Bugs in the PPL code itself (e.g. arithmetic overflow, boundary checks) can allow out-of-bounds modifications inside PPL. Project Zero observed that such a bug in `pmap_remove_options_internal()` was exploited in their bypass.
- The PPL boundary is irrevocably tied to hardware enforcement (APRR, memory controller), so it's only as strong as the hardware implementation.



#### Example
<details>
<summary>Code Example</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry)  // call PPL wrapper
}

// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}

// If kernel code outside PPL does:
*pt_addr = new_entry  // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page

O kernel pode realizar muitas operações normais, mas somente através das rotinas ppl_call_* ele pode alterar mapeamentos protegidos ou patch code.

Exemplo Um exploit de kernel tenta sobrescrever a entitlement table, ou desabilitar a enforcement de code-sign modificando um kernel signature blob. Como essa página é PPL-protected, a escrita é bloqueada a menos que seja feita através da PPL interface. Então, mesmo com execução de código no kernel, você não pode contornar as restrições de code-sign ou modificar dados de credenciais arbitrariamente. No iOS 17+ certos dispositivos usam SPTM para isolar ainda mais páginas gerenciadas pelo PPL.

PPL → SPTM / Substituições / Futuro

  • Nos SoCs modernos da Apple (A15 ou posterior, M2 ou posterior), a Apple suporta SPTM (Secure Page Table Monitor), que substitui o PPL para proteções da tabela de páginas.
  • A Apple destaca na documentação: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
  • A arquitetura SPTM provavelmente desloca mais aplicação de políticas para um monitor com maior privilégio fora do controle do kernel, reduzindo ainda mais a boundary de confiança.

MTE | EMTE | MIE

Aqui está uma descrição em nível mais alto de como o EMTE opera dentro do setup MIE da Apple:

  1. Atribuição de tag
  • Quando memória é alocada (por exemplo no kernel ou em user space via secure allocators), uma secret tag é atribuída a esse bloco.
  • O ponteiro retornado ao usuário ou ao kernel inclui essa tag em seus bits altos (usando mecanismos TBI / top byte ignore).
  1. Verificação de tag no acesso
  • Sempre que um load ou store é executado usando um ponteiro, o hardware verifica que a tag do ponteiro bate com a tag do bloco de memória (allocation tag). Se houver mismatch, ocorre uma falha imediatamente (como é síncrono).
  • Por ser síncrono, não existe uma janela de “delayed detection”.
  1. Re-etiquetagem ao free / reuse
  • Quando a memória é liberada, o allocator muda a tag do bloco (então ponteiros antigos com tags antigas não mais batem).
  • Um ponteiro use-after-free portanto teria uma tag stale e ocasionaria mismatch ao ser acessado.
  1. Diferenciação de tags vizinhas para capturar overflows
  • Alocações adjacentes recebem tags distintas. Se um buffer overflow extravasar para a memória do vizinho, o mismatch de tag causa uma falha.
  • Isso é especialmente poderoso para capturar pequenos overflows que atravessam fronteiras.
  1. Aplicação de confidencialidade das tags
  • A Apple deve prevenir que valores de tag sejam leaked (porque se o atacante descobrir a tag, ele poderia forjar ponteiros com as tags corretas).
  • Eles incluem proteções (microarquiteturais / controles especulativos) para evitar vazamentos por side-channel dos bits de tag.
  1. Integração kernel e user-space
  • A Apple usa EMTE não apenas em user-space, mas também em componentes críticos do kernel / OS (para proteger o kernel contra corrupção de memória).
  • O hardware/OS garante que as regras de tag se apliquem mesmo quando o kernel está executando em nome do user space.
Exemplo ``` Allocate A = 0x1000, assign tag T1 Allocate B = 0x2000, assign tag T2

// pointer P points into A with tag T1 P = (T1 << 56) | 0x1000

// Valid store *(P + offset) = value // tag T1 matches allocation → allowed

// Overflow attempt: P’ = P + size_of_A (into B region) *(P’ + delta) = value → pointer includes tag T1 but memory block has tag T2 → mismatch → fault

// Free A, allocator retags it to T3 free(A)

// Use-after-free: *(P) = value → pointer still has old tag T1, memory region is now T3 → mismatch → fault

</details>

#### Limitações & desafios

- **Transbordamentos intrablocos**: Se o overflow permanece dentro da mesma alocação (não atravessa a fronteira) e a tag permanece a mesma, a mismatch de tag não o detecta.
- **Limitação da largura da tag**: Apenas alguns bits (ex.: 4 bits, ou domínio pequeno) estão disponíveis para a tag—namespace limitado.
- **Side-channel leaks**: Se bits de tag podem ser leaked (via cache / speculative execution), o atacante pode aprender tags válidas e contornar. A Tag Confidentiality Enforcement da Apple visa mitigar isso.
- **Sobrecarga de performance**: Verificações de tag em cada load/store adicionam custo; a Apple precisa otimizar o hardware para reduzir essa sobrecarga.
- **Compatibilidade & fallback**: Em hardware mais antigo ou em partes que não suportam EMTE, deve existir um fallback. A Apple afirma que MIE é habilitado apenas em dispositivos com suporte.
- **Lógica complexa do allocator**: O allocator precisa gerenciar tags, retagging, alinhar fronteiras e evitar colisões de mis-tags. Bugs na lógica do allocator podem introduzir vulnerabilidades.
- **Memória mista / áreas híbridas**: Parte da memória pode permanecer sem tags (legado), tornando a interoperabilidade mais complicada.
- **Ataques especulativos / transitórios**: Como em muitas proteções microarquiteturais, execução especulativa ou fusões de micro-ops podem contornar verificações transitoriamente ou vazar bits de tag.
- **Limitado a regiões suportadas**: A Apple pode impor EMTE apenas em áreas seletivas e de alto risco (kernel, subsistemas críticos de segurança), não universalmente.



---

## Melhorias chave / diferenças comparado ao MTE padrão

Aqui estão as melhorias e mudanças que a Apple enfatiza:

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Modo de verificação** | Suporta modos síncrono e assíncrono. No modo assíncrono, discrepâncias de tag são reportadas depois (atrasadas) | A Apple insiste no **modo síncrono** por padrão—discrepâncias de tag são capturadas imediatamente, sem janelas de atraso/raça permitidas.|
| **Cobertura de memória não etiquetada** | Acessos a memória não etiquetada (ex.: globais) podem ignorar verificações em algumas implementações | EMTE exige que acessos de uma região tagueada para memória não tagueada também validem o conhecimento da tag, dificultando bypass por mistura de alocações.|
| **Confidencialidade / segredo das tags** | Tags podem ser observáveis ou leaked via side channels | A Apple adiciona **Tag Confidentiality Enforcement**, que tenta prevenir a leak de valores de tag (via speculative side-channels etc.).|
| **Integração com allocator & retagging** | MTE deixa grande parte da lógica do allocator para o software | Os allocators tipados seguros da Apple (kalloc_type, xzone malloc, etc.) integram-se com EMTE: quando memória é alocada ou liberada, as tags são gerenciadas com granularidade fina.|
| **Sempre habilitado por padrão** | Em muitas plataformas, MTE é opcional ou desligado por padrão | A Apple habilita EMTE / MIE por padrão em hardware suportado (ex.: iPhone 17 / A19) para kernel e muitos processos de usuário.|

Porque a Apple controla tanto o hardware quanto a pilha de software, ela pode impor o EMTE de forma rígida, evitar armadilhas de performance e fechar brechas de side-channel.

---

## Como EMTE funciona na prática (Apple / MIE)

Aqui está uma descrição em alto nível de como o EMTE opera no setup MIE da Apple:

1. **Atribuição de tag**
- Quando memória é alocada (ex.: no kernel ou no espaço do usuário via allocators seguros), uma **secret tag** é atribuída a esse bloco.
- O ponteiro retornado ao usuário ou kernel inclui essa tag nos bits altos (usando TBI / top byte ignore mechanisms).

2. **Verificação de tag no acesso**
- Sempre que um load ou store é executado usando um ponteiro, o hardware verifica se a tag do ponteiro bate com a tag do bloco de memória (allocation tag). Se houver mismatch, gera fault imediatamente (já que é síncrono).
- Por ser síncrono, não há janela de detecção “adiada”.

3. **Retagging ao free / reuse**
- Quando memória é liberada, o allocator altera a tag do bloco (assim ponteiros antigos com tags antigas não coincidem mais).
- Um ponteiro use-after-free terá portanto uma tag obsoleta e causará mismatch ao ser acessado.

4. **Diferenciação de tags entre vizinhos para pegar overflows**
- Alocações adjacentes recebem tags distintas. Se um buffer overflow vaza para a memória do vizinho, o mismatch de tag causa fault.
- Isso é especialmente eficaz para capturar pequenos overflows que atravessam fronteira.

5. **Aplicação de confidencialidade de tags**
- A Apple precisa impedir que valores de tag sejam vazados (pois, se o atacante aprender a tag, ele poderia forjar ponteiros com tags corretas).
- Eles incluem proteções (controles microarquiteturais / especulativos) para evitar leakage de bits de tag.

6. **Integração com kernel e espaço de usuário**
- A Apple usa EMTE não apenas no espaço do usuário, mas também no kernel / componentes críticos do OS (para proteger o kernel contra corrupção de memória).
- O hardware/OS garante que as regras de tag se apliquem mesmo quando o kernel está executando em nome do espaço do usuário.

Porque EMTE está incorporado no MIE, a Apple usa EMTE em modo síncrono nas superfícies de ataque chave, não como algo opt-in ou apenas para debugging.

---

## Exception handling in XNU

Quando uma **exception** ocorre (ex.: `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, etc.), a **Mach layer** do kernel XNU é responsável por interceptá‑la antes que ela se torne um **signal** estilo UNIX (como `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).

Esse processo envolve múltiplas camadas de propagação e tratamento de exceção antes de chegar ao espaço do usuário ou ser convertido em um BSD signal.


### Exception Flow (High-Level)

1.  **CPU dispara uma exceção síncrona** (ex.: desreferência de ponteiro inválido, falha de PAC, instrução ilegal, etc.).

2.  **O trap handler de baixo nível** é executado (`trap.c`, `exception.c` no source do XNU).

3.  O trap handler chama **`exception_triage()`**, o núcleo do tratamento de exceções Mach.

4.  `exception_triage()` decide como rotear a exceção:

-   Primeiro para a **thread's exception port**.

-   Depois para a **task's exception port**.

-   Depois para a **host's exception port** (frequentemente `launchd` ou `ReportCrash`).

Se nenhuma dessas ports tratar a exceção, o kernel pode:

-   **Converter em um BSD signal** (para processos em espaço do usuário).

-   **Panicar** (para exceções em espaço do kernel).


### Core Function: `exception_triage()`

A função `exception_triage()` roteia exceções Mach pela cadeia de handlers possíveis até que alguma trate a exceção ou até que ela seja finalmente fatal. Está definida em `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

Fluxo típico de chamadas:

exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()

Se todos falharem → é tratado por bsd_exception() → traduzido em um sinal como SIGSEGV.

Exception Ports

Cada objeto Mach (thread, task, host) pode registrar exception ports, para onde as mensagens de exceção são enviadas.

Elas são definidas pela API:

task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()

Each exception port has:

  • A mask (quais exceções ela quer receber)
  • A port name (Mach port para receber mensagens)
  • A behavior (como o kernel envia a mensagem)
  • A flavor (qual thread state incluir)

Debuggers and Exception Handling

Um debugger (por exemplo, LLDB) configura uma exception port na task ou thread alvo, normalmente usando task_set_exception_ports().

Quando uma exceção ocorre:

  • A mensagem Mach é enviada para o processo do debugger.
  • O debugger pode decidir tratar (retomar, modificar registradores, pular instrução) ou não tratar a exceção.
  • Se o debugger não tratar, a exceção propaga para o próximo nível (task → host).

Flow of EXC_BAD_ACCESS

  1. A thread desreferencia um ponteiro inválido → CPU gera Data Abort.

  2. O handler de trap do kernel chama exception_triage(EXC_BAD_ACCESS, ...).

  3. Mensagem enviada para:

  • Thread port → (o debugger pode interceptar breakpoint).

  • Se o debugger ignorar → Task port → (handler a nível de processo).

  • Se ignorado → Host port (normalmente ReportCrash).

  1. Se ninguém tratar → bsd_exception() traduz para SIGSEGV.

PAC Exceptions

Quando Pointer Authentication (PAC) falha (mismatch de assinatura), uma exceção Mach especial é levantada:

  • EXC_ARM_PAC (tipo)
  • Os códigos podem incluir detalhes (ex.: tipo de key, tipo de ponteiro).

Se o binário tiver a flag TFRO_PAC_EXC_FATAL, o kernel trata falhas de PAC como fatais, contornando a interceptação pelo debugger. Isso evita que atacantes usem debuggers para contornar checagens PAC e está habilitado para platform binaries.

Software Breakpoints

Um software breakpoint (int3 no x86, brk no ARM64) é implementado causando uma falha deliberada.
O debugger captura isso via exception port:

  • Modifica o instruction pointer ou memória.
  • Restaura a instrução original.
  • Retoma a execução.

Esse mesmo mecanismo é o que permite “capturar” uma exceção PAC — a não ser que TFRO_PAC_EXC_FATAL esteja setada, caso em que nunca chega ao debugger.

Conversion to BSD Signals

Se nenhum handler aceitar a exceção:

  • O kernel chama task_exception_notify() → bsd_exception().

  • Isso mapeia exceções Mach para sinais:

Mach ExceptionSignal
EXC_BAD_ACCESSSIGSEGV or SIGBUS
EXC_BAD_INSTRUCTIONSIGILL
EXC_ARITHMETICSIGFPE
EXC_SOFTWARESIGTRAP
EXC_BREAKPOINTSIGTRAP
EXC_CRASHSIGKILL
EXC_ARM_PACSIGILL (on non-fatal)

### Key Files in XNU Source

  • osfmk/kern/exception.c → Núcleo de exception_triage(), exception_deliver_*().

  • bsd/kern/kern_sig.c → Lógica de entrega de sinais.

  • osfmk/arm64/trap.c → Handlers de trap de baixo nível.

  • osfmk/mach/exc.h → Códigos e estruturas de exceção.

  • osfmk/kern/task.c → Configuração de exception port para tasks.


Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)

O kernel usava um zone allocator (kalloc) dividido em “zonas” de tamanho fixo. Cada zona armazenava apenas alocações de uma única classe de tamanho.

From the screenshot:

Zone NameElement SizeExample Use
default.kalloc.1616 bytesVery small kernel structs, pointers.
default.kalloc.3232 bytesSmall structs, object headers.
default.kalloc.6464 bytesIPC messages, tiny kernel buffers.
default.kalloc.128128 bytesMedium objects like parts of OSObject.
default.kalloc.12801280 bytesLarge structures, IOSurface/graphics metadata.

Como funcionava:

  • Cada pedido de alocação era arredondado para cima até o tamanho da zona mais próxima. (Ex.: um pedido de 50 bytes ia para a zona kalloc.64).
  • A memória em cada zona era mantida em uma freelist — chunks liberados pelo kernel voltavam para aquela zona.
  • Se você overflowasse um buffer de 64 bytes, você sobrescreveria o próximo objeto na mesma zona.

É por isso que heap spraying / feng shui era tão eficaz: você podia prever objetos vizinhos ao fazer alocações da mesma classe de tamanho.

The freelist

Dentro de cada kalloc zone, objetos liberados não eram devolvidos diretamente ao sistema — iam para uma freelist, uma lista ligada de chunks disponíveis.

  • Quando um chunk era liberado, o kernel escrevia um ponteiro no início desse chunk → o endereço do próximo chunk livre na mesma zona.

  • A zona mantinha um ponteiro HEAD para o primeiro chunk livre.

  • A alocação sempre usava o HEAD atual:

  1. Pop HEAD (retorna essa memória para o chamador).

  2. Atualiza HEAD = HEAD->next (armazenado no cabeçalho do chunk liberado).

  • Freeing empurrava chunks de volta:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

Então a freelist era apenas uma lista ligada construída dentro da própria memória liberada.

Normal state:

Zone page (64-byte chunks for example):
[ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ]

Freelist view:
HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL
(next ptrs stored at start of freed chunks)

Explorando o freelist

Como os primeiros 8 bytes de um free chunk = freelist pointer, um atacante poderia corrompê-lo:

  1. Heap overflow into an adjacent freed chunk → sobrescrever seu ponteiro “next”.

  2. Use-after-free write into a freed object → sobrescrever seu ponteiro “next”.

Então, na próxima alocação desse tamanho:

  • O allocator retira o chunk corrompido.

  • Segue o ponteiro “next” fornecido pelo atacante.

  • Retorna um pointer para memória arbitrária, permitindo fake object primitives ou targeted overwrite.

Exemplo visual de freelist poisoning:

Before corruption:
HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL

After attacker overwrite of F1->next:
HEAD ──► [ F1 ]
(next) ──► 0xDEAD_BEEF_CAFE_BABE  (attacker-chosen)

Next alloc of this zone → kernel hands out memory at attacker-controlled address.

This freelist design tornou a exploração altamente eficaz antes das hardenings: vizinhos previsíveis a partir de heap sprays, raw pointer freelist links, e nenhuma separação de tipos permitia que atacantes escalassem bugs de UAF/overflow para controle arbitrário da memória do kernel.

Heap Grooming / Feng Shui

The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
Dessa forma, quando ocorre corrupção de memória, o atacante pode sobrescrever de forma confiável o objeto vítima com dados controlados.

Steps:

  1. Spray allocations (fill the holes)
  • Com o tempo, o heap do kernel fica fragmentado: algumas zonas têm buracos onde objetos antigos foram freed.
  • O atacante primeiro faz muitas alocações dummy para preencher essas lacunas, de modo que o heap fique “compacto” e previsível.
  1. Force new pages
  • Uma vez que os buracos estão preenchidos, as próximas alocações deverão vir de novas páginas adicionadas à zona.
  • Páginas novas significam que os objetos ficarão agrupados, não espalhados por memória fragmentada antiga.
  • Isso dá ao atacante muito mais controle sobre os vizinhos.
  1. Place attacker objects
  • O atacante então faz spray novamente, criando muitos objetos controlados por ele nessas novas páginas.
  • Esses objetos têm tamanho e posicionamento previsíveis (já que pertencem à mesma zone).
  1. Free a controlled object (make a gap)
  • O atacante deliberadamente libera um dos seus próprios objetos.
  • Isso cria um “buraco” no heap, que o allocator reutilizará para a próxima alocação daquele tamanho.
  1. Victim object lands in the hole
  • O atacante dispara o kernel para alocar o objeto vítima (aquele que quer corromper).
  • Como o buraco é o primeiro slot disponível na freelist, a vítima é colocada exatamente onde o atacante liberou seu objeto.
  1. Overflow / UAF into victim
  • Agora o atacante tem objetos controlados ao redor da vítima.
  • Ao fazer overflow a partir de um dos seus próprios objetos (ou reutilizar um objeto freed), ele pode sobrescrever de forma confiável os campos de memória da vítima com valores escolhidos.

Por que funciona:

  • Predictability do zone allocator: alocações do mesmo tamanho sempre vêm da mesma zone.
  • Comportamento do freelist: novas alocações reutilizam primeiro o chunk mais recentemente freed.
  • Heap sprays: o atacante preenche a memória com conteúdo previsível e controla o layout.
  • Resultado final: o atacante controla onde o objeto vítima é colocado e que dados ficam ao lado dele.

Modern Kernel Heap (iOS 15+/A12+ SoCs)

A Apple hardened o allocator e tornou o heap grooming muito mais difícil:

1. From Classic kalloc to kalloc_type

  • Antes: existia uma única zone kalloc.<size> para cada classe de tamanho (16, 32, 64, … 1280, etc.). Qualquer objeto desse tamanho era colocado lá → objetos controlados pelo atacante podiam ficar ao lado de objetos privilegiados do kernel.
  • Agora:
  • Objetos do kernel são alocados a partir de typed zones (kalloc_type).
  • Cada tipo de objeto (ex.: ipc_port_t, task_t, OSString, OSData) tem sua própria zone dedicada, mesmo que tenham o mesmo tamanho.
  • O mapeamento entre tipo de objeto ↔ zone é gerado pelo kalloc_type system em tempo de compilação.

Um atacante não pode mais garantir que dados controlados (OSData) acabem adjacentes a objetos sensíveis do kernel (task_t) do mesmo tamanho.

2. Slabs and Per-CPU Caches

  • O heap é dividido em slabs (páginas de memória esculpidas em chunks de tamanho fixo para aquela zone).
  • Cada zone tem um per-CPU cache para reduzir contenção.
  • Caminho de alocação:
  1. Tentar o per-CPU cache.
  2. Se vazio, puxar da global freelist.
  3. Se a freelist estiver vazia, alocar um novo slab (uma ou mais páginas).
  • Benefício: essa descentralização torna heap sprays menos determinísticos, já que alocações podem ser satisfeitas a partir dos caches de CPUs diferentes.

3. Randomization inside zones

  • Dentro de uma zone, elementos freed não são retornados em simples ordem FIFO/LIFO.
  • O XNU moderno usa encoded freelist pointers (estilo safe-linking do Linux, introduzido ~iOS 14).
  • Cada freelist pointer é XOR-encoded com um cookie secreto por zone.
  • Isso impede que atacantes forjem um ponteiro de freelist falso se ganharem um primitive de escrita.
  • Algumas alocações são randomizadas na sua posição dentro de um slab, então spraying não garante adjacência.

4. Guarded Allocations

  • Certos objetos críticos do kernel (ex.: credenciais, estruturas de task) são alocados em guarded zones.
  • Essas zones inserem guard pages (memória não mapeada) entre slabs ou usam redzones ao redor dos objetos.
  • Qualquer overflow na guard page dispara um fault → panic imediato em vez de corrupção silenciosa.

5. Page Protection Layer (PPL) and SPTM

  • Mesmo que você controle um objeto freed, não pode modificar toda a memória do kernel:
  • PPL (Page Protection Layer) assegura que certas regiões (ex.: dados de code signing, entitlements) sejam read-only mesmo para o próprio kernel.
  • Em dispositivos A15/M2+, esse papel é substituído/aperfeiçoado por SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
  • Essas camadas impostas por hardware significam que atacantes não conseguem escalar de uma única corrupção de heap para patchar arbitrariamente estruturas críticas de segurança.
  • (Adicionado / Melhorado): também, PAC (Pointer Authentication Codes) é usado no kernel para proteger pointers (especialmente function pointers, vtables) de modo que forjá-los ou corrompê-los fique mais difícil.
  • (Adicionado / Melhorado): zones podem impor zone_require / zone enforcement, i.e. que um objeto freed só pode retornar através da sua zone tipada correta; frees cross-zone inválidos podem causar panic ou ser rejeitados. (A Apple alude a isso em posts sobre memory safety)

6. Large Allocations

  • Nem todas as alocações passam por kalloc_type.
  • Requests muito grandes (acima de ~16 KB) bypassam typed zones e são servidos diretamente via kernel VM (kmem) por alocações de páginas.
  • Essas são menos previsíveis, mas também menos exploráveis, já que não compartilham slabs com outros objetos.

7. Allocation Patterns Attackers Target

Mesmo com essas proteções, atacantes ainda procuram:

  • Reference count objects: se você consegue manipular retain/release counters, pode causar use-after-free.
  • Objects with function pointers (vtables): corromper um ainda pode levar a control flow.
  • Shared memory objects (IOSurface, Mach ports): continuam sendo alvos porque fazem ponte entre user ↔ kernel.

Mas — ao contrário de antes — você não pode simplesmente spray OSData e esperar que fique vizinho de um task_t. Você precisa de bugs específicos por tipo ou info leaks para ter sucesso.

Example: Allocation Flow in Modern Heap

Suponha que userspace chama IOKit para alocar um objeto OSData:

  1. Type lookupOSData mapeia para a zone kalloc_type_osdata (tamanho 64 bytes).
  2. Checar o per-CPU cache por elementos livres.
  • Se encontrado → retornar um.
  • Se vazio → ir para a global freelist.
  • Se a freelist estiver vazia → alocar um novo slab (página de 4KB → 64 chunks de 64 bytes).
  1. Retornar o chunk para o chamador.

Proteção de freelist pointer:

  • Cada chunk freed armazena o endereço do próximo chunk livre, mas codificado com uma chave secreta.
  • Sobrescrever esse campo com dados do atacante não funcionará a menos que você conheça a chave.

Comparison Table

FeatureOld Heap (Pre-iOS 15)Modern Heap (iOS 15+ / A12+)
Allocation granularityFixed size buckets (kalloc.16, kalloc.32, etc.)Size + type-based buckets (kalloc_type)
Placement predictabilityHigh (same-size objects side by side)Low (same-type grouping + randomness)
Freelist managementRaw pointers in freed chunks (easy to corrupt)Encoded pointers (safe-linking style)
Adjacent object controlEasy via sprays/frees (feng shui predictable)Hard — typed zones separate attacker objects
Kernel data/code protectionsFew hardware protectionsPPL / SPTM protect page tables & code pages, and PAC protects pointers
Allocation reuse validationNone (freelist pointers raw)zone_require / zone enforcement
Exploit reliabilityHigh with heap spraysMuch lower, requires logic bugs or info leaks
Large allocations handlingAll small allocations managed equallyLarge ones bypass zones → handled via VM

Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)

Em versões recentes dos OS da Apple (especialmente iOS 17+), a Apple introduziu um allocator de userland mais seguro, xzone malloc (XZM). Este é o análogo em user-space do kalloc_type do kernel, aplicando awareness por tipo, isolamento de metadata e proteções de memory tagging.

Goals & Design Principles

  • Type segregation / type awareness: agrupar alocações por tipo ou uso (pointer vs data) para prevenir type confusion e reuse cross-type.
  • Metadata isolation: separar metadata do heap (ex.: free lists, bits de tamanho/estado) dos payloads dos objetos para que OOB writes tenham menos chance de corromper metadata.
  • Guard pages / redzones: inserir páginas não mapeadas ou padding ao redor das alocações para detectar overflows.
  • Memory tagging (EMTE / MIE): trabalhar em conjunto com tagging por hardware para detectar use-after-free, OOB e acessos inválidos.
  • Scalable performance: manter overhead baixo, evitar fragmentação excessiva e suportar muitas alocações por segundo com baixa latência.

Architecture & Components

Abaixo os elementos principais do allocator xzone:

Segment Groups & Zones

  • Segment groups particionam o espaço de endereços por categorias de uso: ex.: data, pointer_xzones, data_large, pointer_large.
  • Cada segment group contém segments (ranges de VM) que hospedam alocações para aquela categoria.
  • Associado a cada segment há um metadata slab (área VM separada) que armazena metadata (ex.: bits free/used, classes de tamanho) para aquele segment. Essa out-of-line (OOL) metadata garante que metadata não fique misturada com payloads de objetos, mitigando corrupção por overflows.
  • Segments são esculpidos em chunks (slices) que, por sua vez, são subdivididos em blocks (unidades de alocação). Um chunk está ligado a uma classe de tamanho e a um segment group específico (i.e. todos os blocks em um chunk compartilham o mesmo tamanho & categoria).
  • Para alocações small/medium, usa-se chunks de tamanho fixo; para large/huge, pode mapear separadamente.

Chunks & Blocks

  • Um chunk é uma região (frequentemente várias páginas) dedicada a alocações de uma classe de tamanho dentro de um group.
  • Dentro de um chunk, blocks são slots disponíveis para alocação. Blocks freed são rastreados via metadata slab — ex.: via bitmaps ou free lists armazenadas out-of-line.
  • Entre chunks (ou dentro), guard slices / guard pages podem ser inseridos (ex.: slices não mapeadas) para detectar writes OOB.

Type / Type ID

  • Cada site de alocação (ou chamada a malloc, calloc, etc.) está associado a um type identifier (um malloc_type_id_t) que codifica que tipo de objeto está sendo alocado. Esse type ID é passado ao allocator, que o usa para selecionar qual zone / segment servir a alocação.
  • Por isso, mesmo se duas alocações têm o mesmo tamanho, podem ir para zonas inteiramente diferentes se seus tipos diferirem.
  • Em versões iniciais do iOS 17, nem todas as APIs (ex.: CFAllocator) eram totalmente type-aware; a Apple corrigiu algumas dessas fraquezas no iOS 18.

Allocation & Freeing Workflow

Aqui um fluxo de alto nível de como alocação e liberação operam no xzone:

  1. malloc / calloc / realloc / typed alloc é invocado com um tamanho e type ID.
  2. O allocator usa o type ID para escolher o segment group / zone correto.
  3. Dentro daquela zone/segment, procura um chunk que tenha blocks livres do tamanho pedido.
  • Pode consultar local caches / per-thread pools ou free block lists da metadata.
  • Se não houver block livre disponível, pode alocar um novo chunk naquela zone.
  1. O metadata slab é atualizado (bit de free limpo, bookkeeping).
  2. Se memory tagging (EMTE) estiver em uso, o bloco retornado recebe um tag, e a metadata é atualizada para refletir seu estado “live”.
  3. Quando free() é chamado:
  • O block é marcado como freed na metadata (via slab OOL).
  • O block pode ser colocado em uma free list ou pooled para reuse.
  • Opcionalmente, o conteúdo do block pode ser limpado ou poisoned para reduzir vazamentos de dados ou exploração de UAF.
  • A tag de hardware associada ao block pode ser invalidada ou re-tagged.
  • Se um chunk inteiro ficar free (todos os blocks liberados), o allocator pode reclaim esse chunk (unmap ou devolver ao OS) sob pressão de memória.

Security Features & Hardening

Defesas embutidas no xzone:

FeaturePurposeNotes
Metadata decouplingPrevent overflow from corrupting metadataMetadata lives in separate VM region (metadata slab)
Guard pages / unmapped slicesCatch out-of-bounds writesHelps detect buffer overflows rather than silently corrupting adjacent blocks
Type-based segregationPrevent cross-type reuse & type confusionEven same-size allocations from different types go to different zones
Memory Tagging (EMTE / MIE)Detect invalid access, stale references, OOB, UAFxzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”)
Delayed reuse / poisoning / zapReduce chance of use-after-free exploitationFreed blocks may be poisoned, zeroed, or quarantined before reuse
Chunk reclamation / dynamic unmappingReduce memory waste and fragmentationEntire chunks may be unmapped when unused
Randomization / placement variationPrevent deterministic adjacencyBlocks in a chunk and chunk selection may have randomized aspects
Segregation of “data-only” allocationsSeparate allocations that don’t store pointersReduces attacker control over metadata or control fields

Interaction with Memory Integrity Enforcement (MIE / EMTE)

  • O MIE da Apple (Memory Integrity Enforcement) é o framework hardware + OS que traz o Enhanced Memory Tagging Extension (EMTE) para modo sempre-on e síncrono em superfícies de ataque principais.
  • O allocator xzone é uma fundação do MIE em user space: alocações via xzone recebem tags, e acessos são verificados pelo hardware.
  • No MIE, o allocator, atribuição de tags, gerenciamento de metadata e enforcement de confidencialidade das tags são integrados para garantir que erros de memória (ex.: stale reads, OOB, UAF) sejam detectados imediatamente, e não explorados depois.

Se quiser, eu também posso gerar um cheat-sheet ou diagrama dos internals do xzone para o seu livro. Quer que eu faça isso a seguir?
:contentReference[oai:20]{index=20}

(Old) Physical Use-After-Free via IOSurface

ios Physical UAF - IOSurface


Ghidra Install BinDiff

Faça o download do BinDiff DMG em https://www.zynamics.com/bindiff/manual e instale-o.

Abra o Ghidra com ghidraRun e vá em File –> Install Extensions, pressione o botão add e selecione o caminho /Applications/BinDiff/Extra/Ghidra/BinExport e clique OK e isntall it mesmo que haja uma incompatibilidade de versão.

Using BinDiff with Kernel versions

  1. Vá à página https://ipsw.me/ e faça o download das versões do iOS que você quer diffar. Estas serão arquivos .ipsw.
  2. Descomprima até obter o formato bin do kernelcache de ambos os arquivos .ipsw. Você tem informações sobre como fazer isso em:

macOS Kernel Extensions & Kernelcache

  1. Abra o Ghidra com ghidraRun, crie um novo projeto e carregue os kernelcaches.
  2. Abra cada kernelcache para que sejam automaticamente analisados pelo Ghidra.
  3. Então, na janela do projeto do Ghidra, clique com o botão direito em cada kernelcache, selecione Export, selecione o formato Binary BinExport (v2) for BinDiff e exporte-os.
  4. Abra o BinDiff, crie um novo workspace e adicione um novo diff indicando como primary file o kernelcache que contém a vulnerabilidade e como secondary file o kernelcache patched.

Finding the right XNU version

Se você quer checar por vulnerabilidades em uma versão específica do iOS, pode verificar qual versão do XNU o iOS usa em [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).

Por exemplo, as versões 15.1 RC, 15.1 e 15.1.1 usam a versão Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.

JSKit-Based Safari Chains and PREYHUNTER Stagers

Renderer RCE abstraction with JSKit

  • Reusable entry: Recent chains in-the-wild abusaram de um bug do WebKit JIT (patched como CVE-2023-41993) puramente para obter arbitary read/write em JavaScript. O exploit imediatamente pivota para um framework comprado chamado JSKit, então qualquer bug futuro no Safari só precisa entregar o mesmo primitive.
  • Version abstraction & PAC bypasses: O JSKit inclui suporte para uma ampla gama de releases iOS junto com múltiplos módulos selecionáveis de Pointer Authentication Code bypass. O framework fingerprinta o build alvo, seleciona a lógica de PAC bypass apropriada, e verifica cada passo (validação do primitive, lançamento de shellcode) antes de progredir.
  • Manual Mach-O mapping: O JSKit parseia headers Mach-O diretamente da memória, resolve os símbolos que precisa dentro de imagens dyld-cached, e pode mapear manualmente payloads Mach-O adicionais sem escrevê-los no disco. Isso mantém o processo renderer apenas em memória e evita checks de code-signature ligados a artefatos do filesystem.
  • Portfolio model: Strings de debug como “exploit number 7” mostram que os fornecedores mantêm múltiplos exploits WebKit intercambiáveis. Uma vez que o primitive JS corresponda à interface do JSKit, o resto da cadeia permanece inalterado entre campanhas.

Kernel bridge: IPC UAF -> code-sign bypass pattern

  • Kernel IPC UAF (CVE-2023-41992): A segunda etapa, ainda rodando dentro do contexto do Safari, dispara um use-after-free no código IPC do kernel, realoca o objeto freed a partir do userland, e abusa dos dangling pointers para pivotar para arbitrary kernel read/write. A etapa também reutiliza material de PAC bypass previamente calculado pelo JSKit em vez de re-derivá-lo.
  • Code-signing bypass (CVE-2023-41991): Com R/W do kernel disponível, o exploit patcha o trust cache / estruturas de code-signing para que payloads unsigned executem como system. A etapa então expõe um serviço leve de kernel R/W para payloads posteriores.
  • Composed pattern: Essa cadeia demonstra uma receita reutilizável que os defensores devem esperar adiante:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager

PREYHUNTER helper & watcher modules

  • Watcher anti-analysis: Um binário watcher dedicado perfila continuamente o dispositivo e aborta a kill-chain quando um ambiente de pesquisa é detectado. Ele inspeciona security.mac.amfi.developer_mode_status, a presença de um console diagnosticd, localidades US ou IL, vestígios de jailbreak such as Cydia, processos como bash, tcpdump, frida, sshd, ou checkrain, mobile AV apps (McAfee, AvastMobileSecurity, NortonMobileSecurity), configurações de proxy HTTP customizadas, e root CAs customizadas. Falhar em qualquer verificação bloqueia a entrega de payloads subsequentes.

  • Helper surveillance hooks: O componente helper comunica-se com outros estágios através de /tmp/helper.sock, depois carrega conjuntos de hooks chamados DMHooker e UMHooker. Esses hooks interceptam caminhos de áudio VOIP (as gravações são salvas em /private/var/tmp/l/voip_%lu_%u_PART.m4a), implementam um keylogger de sistema, capturam fotos sem UI, e hookam SpringBoard para suprimir notificações que essas ações normalmente levantariam. O helper, portanto, atua como uma camada furtiva de validação + vigilância leve antes de implantes mais pesados como Predator serem dropados.

  • HiddenDot indicator suppression in SpringBoard: Com injeção de código a nível de kernel, Predator hooka SBSensorActivityDataProvider._handleNewDomainData: (o ponto de agregação para atividade de sensores). O hook zera o ponteiro Objective-C self (x0) de modo que a chamada se torna [nil _handleNewDomainData:newData], descartando atualizações de câmera/microfone e suprimindo tanto o ponto verde quanto o laranja.

  • Mach exception-based hooking flow (DMHooker): Hooks são implementados via EXC_BREAKPOINT + exception ports, então thread_set_state muta registradores e a execução é retomada. Código de retorno 2 significa “continue with modified thread state.”

  • PAC-aware redirection for camera access checks: Em mediaserverd, um pattern-scan (e.g., memmem) localiza uma rotina privada próxima a FigVideoCaptureSourceCreateWithSourceInfo dentro de CMCapture.framework. O hook retorna 3 para redirecionar usando um endereço de retorno em cache pré-assinado com PAC, satisfazendo o PAC enquanto contorna a verificação.

  • VoIP capture pipeline in mediaserverd: Hooka AudioConverterNew e AudioConverterConvertComplexBuffer+52 para interceptar buffers, inferir a sample rate a partir dos tamanhos dos buffers, converter float32 PCM → int16 com NEON, downmix de 4 canais para estéreo, e persistir via ExtAudioFileWrite(). O módulo VoIP em si não suprime indicadores, então os operadores devem habilitar HiddenDot separadamente.

WebKit DFG Store-Barrier UAF + ANGLE PBO OOB (iOS 26.1)

Webkit Dfg Store Barrier Uaf Angle Oob

iMessage/Media Parser Zero-Click Chains

Imessage Media Parser Zero Click Coreaudio Pac Bypass

References

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks