Explotación en iOS

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Mitigaciones de exploits en iOS

1. Code Signing / Runtime Signature Verification

Introducido temprano (iPhone OS → iOS) Esta es una de las protecciones fundamentales: todo el código ejecutable (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) debe estar firmado criptográficamente por una cadena de certificados con raíz en la confianza de Apple. En tiempo de ejecución, antes de cargar un binario en memoria (o antes de realizar saltos a través de ciertos límites), el sistema verifica su firma. Si el código está modificado (bit-flipped, parcheado) o sin firmar, la carga falla.

  • Evita: la etapa “classic payload drop + execute” en cadenas de explotación; inyección arbitraria de código; modificar un binario existente para insertar lógica maliciosa.
  • Detalles del mecanismo:
  • El Mach-O loader (y el dynamic linker) comprueba páginas de código, segmentos, entitlements, team IDs, y que la firma cubra el contenido del archivo.
  • Para regiones de memoria como JIT caches o código generado dinámicamente, Apple exige que las páginas estén firmadas o validadas vía APIs especiales (p. ej. mprotect con cheques de code-sign).
  • La firma incluye entitlements e identificadores; el OS hace cumplir que ciertas APIs o capacidades privilegiadas requieran entitlements específicos que no pueden falsificarse.
Ejemplo Supongamos que un exploit obtiene ejecución de código en un proceso e intenta escribir shellcode en el heap y saltar hacia él. En iOS, esa página tendría que marcarse como ejecutable **y** satisfacer las restricciones de code-signature. Dado que el shellcode no está firmado con el certificado de Apple, el salto falla o el sistema rechaza hacer ejecutable esa región de memoria.

2. CoreTrust

Introducido alrededor de iOS 14+ (o gradualmente en dispositivos más nuevos / iOS posteriores) CoreTrust es el subsistema que realiza la validación de firmas en tiempo de ejecución de binarios (incluyendo binarios del sistema y de usuario) contra la certificación raíz de Apple en lugar de confiar en almacenes de confianza en userland en caché.

  • Evita: manipulación post-instalación de binarios, técnicas de jailbreak que intentan reemplazar o parchear librerías del sistema o apps de usuario; engañar al sistema reemplazando binarios confiables por contrapartes maliciosas.
  • Detalles del mecanismo:
  • En vez de confiar en una base de confianza local o cache de certificados, CoreTrust consulta o se refiere directamente a la raíz de Apple o verifica certificados intermedios en una cadena segura.
  • Asegura que modificaciones (p. ej. en el filesystem) a binarios existentes sean detectadas y rechazadas.
  • Vincula entitlements, team IDs, flags de code signing y otros metadatos al binario en tiempo de carga.
Ejemplo Un jailbreak podría intentar reemplazar `SpringBoard` o `libsystem` con una versión parcheada para obtener persistencia. Pero cuando el loader del OS o CoreTrust verifica, detecta la discordancia de firma (o entitlements modificados) y se niega a ejecutar.

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

Introducido en muchos OS antes; iOS tuvo NX-bit / w^x durante mucho tiempo DEP hace cumplir que las páginas marcadas como writables (para datos) sean no ejecutables, y las páginas marcadas como ejecutables sean no writables. No puedes simplemente escribir shellcode en un heap o stack y ejecutarlo.

  • Evita: ejecución directa de shellcode; el clásico buffer-overflow → salto a shellcode inyectado.
  • Detalles del mecanismo:
  • La MMU / flags de protección de memoria (vía page tables) hacen cumplir la separación.
  • Cualquier intento de marcar una página writable como executable dispara una comprobación del sistema (y está prohibido o requiere aprobación de code-sign).
  • En muchos casos, hacer páginas ejecutables requiere pasar por APIs del OS que imponen restricciones o cheques adicionales.
Ejemplo Un overflow escribe shellcode en el heap. El atacante intenta `mprotect(heap_addr, size, PROT_EXEC)` para hacerlo ejecutable. Pero el sistema se niega o valida que la nueva página debe pasar restricciones de code-sign (lo cual el shellcode no cumple).

4. Address Space Layout Randomization (ASLR)

Introducido en iOS ~4–5 era (aprox. iOS 4–5) ASLR aleatoriza las direcciones base de regiones clave de memoria: librerías, heap, stack, etc., en cada lanzamiento de proceso. Las direcciones de gadgets cambian entre ejecuciones.

  • Evita: hardcodear direcciones de gadgets para ROP/JOP; cadenas de exploit estáticas; saltos a offsets conocidos.
  • Detalles del mecanismo:
  • Cada librería / módulo dinámico cargado se rebasa a un offset aleatorio.
  • Punteros base de stack y heap se aleatorizan (dentro de ciertos límites de entropía).
  • A veces otras regiones (p. ej. mmap allocations) también se aleatorizan.
  • Combinado con mitigaciones de information-leak, obliga al atacante a filtrar primero una dirección o puntero para descubrir las direcciones base en tiempo de ejecución.
Ejemplo Una cadena ROP espera un gadget en `0x….lib + offset`. Pero como `lib` se reubica diferente en cada ejecución, la cadena hardcodeada falla. Un exploit debe primero leak la dirección base del módulo antes de calcular las direcciones de gadgets.

5. Kernel Address Space Layout Randomization (KASLR)

Introducido en iOS ~ (iOS 5 / iOS 6) Análogo a ASLR de usuario, KASLR aleatoriza la base del kernel text y otras estructuras del kernel en el arranque.

  • Evita: exploits a nivel kernel que dependen de ubicaciones fijas de código o datos del kernel; exploits estáticos del kernel.
  • Detalles del mecanismo:
  • En cada arranque, la dirección base del kernel se randomiza (dentro de un rango).
  • Estructuras de datos del kernel (como task_structs, vm_map, etc.) también pueden ser relocadas u offseteadas.
  • Los atacantes deben primero leak pointers del kernel o usar vulnerabilidades de divulgación de información para computar offsets antes de manipular estructuras o código del kernel.
Ejemplo Una vulnerabilidad local apunta a corromper un puntero de función del kernel (p. ej. en una `vtable`) en `KERN_BASE + offset`. Pero como `KERN_BASE` es desconocida, el atacante debe primero leakearla (p. ej. mediante una primitiva de lectura) antes de calcular la dirección correcta para la corrupción.

6. Kernel Patch Protection (KPP / AMCC)

Introducido en iOS más nuevos / hardware A-series (post alrededor iOS 15–16 o chips más recientes) KPP (aka AMCC) monitoriza continuamente la integridad de las páginas de kernel text (vía hash o checksum). Si detecta manipulación (parches, inline hooks, modificaciones de código) fuera de ventanas permitidas, provoca un kernel panic o reboot.

  • Evita: parcheo persistente del kernel (modificar instrucciones del kernel), inline hooks, sobrescrituras de funciones estáticas.
  • Detalles del mecanismo:
  • Un módulo hardware o firmware monitoriza la región de kernel text.
  • Periódica o bajo demanda re-hashea las páginas y las compara con valores esperados.
  • Si hay mismatch fuera de ventanas benignas de actualización, entra en panic (para evitar parches maliciosos persistentes).
  • Los atacantes deben evitar las ventanas de detección o usar rutas legítimas de parcheo.
Ejemplo Un exploit intenta parchear el prólogo de una función del kernel (p. ej. `memcmp`) para interceptar llamadas. Pero KPP detecta que el hash de la página de código ya no coincide con el valor esperado y provoca un kernel panic, dejando el dispositivo inestable antes de que el parche se estabilice.

7. Kernel Text Read‐Only Region (KTRR)

Introducido en SoCs modernos (post ~A12 / hardware más reciente) KTRR es un mecanismo hardware-forzado: una vez que el kernel text se bloquea temprano durante el boot, se vuelve de solo lectura desde EL1 (el kernel), impidiendo más escrituras en páginas de código.

  • Evita: cualquier modificación al código del kernel después del boot (p. ej. parcheo, in-place code injection) a nivel de privilegio EL1.
  • Detalles del mecanismo:
  • Durante el arranque (en etapas secure/bootloader), el memory controller (o una unidad hardware segura) marca las páginas físicas que contienen kernel text como read-only.
  • Incluso si un exploit logra privilegios completos de kernel, no puede escribir en esas páginas para parchear instrucciones.
  • Para modificarlas, el atacante debe comprometer primero la cadena de arranque, o subvertir KTRR mismo.
Ejemplo Un exploit de escalada de privilegios salta a EL1 y escribe un trampoline en una función del kernel (p. ej. en el handler de `syscall`). Pero como las páginas están marcadas read-only por KTRR, la escritura falla (o provoca un fallo), por lo que los parches no se aplican.

8. Pointer Authentication Codes (PAC)

Introducido con ARMv8.3 (hardware), Apple empezando con A12 / iOS ~12+

  • PAC es una característica hardware introducida en ARMv8.3-A para detectar la manipulación de valores de punteros (return addresses, function pointers, ciertos data pointers) insertando una pequeña firma criptográfica (un “MAC”) en bits altos no usados del puntero.
  • La firma (“PAC”) se calcula sobre el valor del puntero más un modifier (un valor de contexto, p. ej. el stack pointer o algún dato distintivo). De ese modo, el mismo puntero en diferentes contextos tiene un PAC distinto.
  • En tiempo de uso, antes de desreferenciar o ramificar mediante ese puntero, una instrucción de authenticate comprueba el PAC. Si es válido, se elimina el PAC y se obtiene el puntero puro; si es inválido, el puntero queda “poisoned” (o se lanza una excepción).
  • Las claves usadas para producir/validar PACs viven en registros privilegiados (EL1, kernel) y no son accesibles desde user mode.
  • Porque no se usan los 64 bits completos de un puntero en muchos sistemas (p. ej. espacio de direcciones de 48 bits), los bits superiores son “espacio” y pueden contener el PAC sin alterar la dirección efectiva.

Base arquitectónica y tipos de claves

  • ARMv8.3 introduce cinco claves de 128-bit (cada una implementada vía dos registros de 64-bit) para pointer authentication.

  • APIAKey — para instruction pointers (dominio “I”, key A)

  • APIBKey — segunda clave para instruction pointers (dominio “I”, key B)

  • APDAKey — para data pointers (dominio “D”, key A)

  • APDBKey — para data pointers (dominio “D”, key B)

  • APGAKey — clave “genérica”, para firmar datos no-pointer u otros usos genéricos

  • Estas claves se almacenan en registros del sistema privilegiados (accesibles solo en EL1/EL2, etc.), no accesibles desde user mode.

  • El PAC se calcula mediante una función criptográfica (ARM sugiere QARMA como algoritmo) usando:

  1. El valor del puntero (porción canónica)
  2. Un modifier (un valor de contexto, como una sal)
  3. La clave secreta
  4. Algo de lógica interna de ajuste Si el PAC resultante coincide con lo almacenado en los bits altos del puntero, la autenticación tiene éxito.

Familias de instrucciones

La convención de nombres es: PAC / AUT / XPAC, seguido de letras de dominio.

  • PACxx instrucciones firman un puntero e insertan un PAC
  • AUTxx instrucciones autentican + remueven (validan y quitan el PAC)
  • XPACxx instrucciones quitan sin validar

Dominios / sufijos:

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

Hay formas especializadas / alias:

  • PACIASP es la forma corta para PACIA X30, SP (firmar el link register usando SP como modifier)
  • AUTIASP es AUTIA X30, SP (autenticar el link register con SP)
  • Formas combinadas como RETAA, RETAB (authenticate-and-return) o BLRAA (authenticate & branch) existen en extensiones de ARM / soporte del compilador.
  • También variantes con modifier cero: PACIZA / PACIZB donde el modifier es implícitamente cero, etc.

Modifiers

El objetivo principal del modifier es atar el PAC a un contexto específico para que la misma dirección firmada en distintos contextos produzca PACs distintos. Es como añadir una sal a un hash.

Por lo tanto:

  • El modifier es un valor de contexto (otro registro) que se mezcla en el cálculo del PAC. Elecciones típicas: el stack pointer (SP), un frame pointer, o algún ID de objeto.
  • Usar SP como modifier es común para signing de return addresses: el PAC queda ligado al frame de stack específico. Si intentas reutilizar el LR en otro frame, el modifier cambia, por lo que la validación PAC falla.
  • El mismo valor de puntero firmado con modifiers distintos produce PACs distintos.
  • El modifier no necesita ser secreto, pero idealmente no está controlado por el atacante.
  • Para instrucciones que firman o verifican punteros donde no existe un modifier significativo, algunas formas usan cero o una constante implícita.

Customizaciones y observaciones de Apple / iOS / XNU

  • La implementación PAC de Apple incluye diversificadores por arranque de modo que claves o tweaks cambien en cada boot, impidiendo la reutilización entre boots.
  • También incluyen mitigaciones cross-domain para que PACs firmados en user mode no puedan reutilizarse fácilmente en kernel mode, etc.
  • En Apple M1 / Apple Silicon, ingeniería inversa mostró que hay nueve tipos de modifier y registros de sistema específicos de Apple para control de claves.
  • Apple usa PAC en muchos subsistemas del kernel: firmado de return addresses, integridad de punteros en datos del kernel, contextos de thread firmados, etc.
  • Google Project Zero mostró cómo, bajo una primitiva poderosa de lectura/escritura de memoria en kernel, se podrían forjar PACs de kernel (para claves A) en dispositivos A12-era, pero Apple parcheó muchas rutas.
  • En el sistema de Apple, algunas claves son globales al kernel, mientras que procesos user pueden obtener aleatoriedad de claves por proceso.

Bypasses de PAC

  1. Kernel-mode PAC: teórico vs bypasses reales
  • Debido a que las claves y la lógica de PAC del kernel están fuertemente controladas (registros privilegiados, diversificadores, aislamiento de dominios), forjar punteros firmados arbitrarios del kernel es muy difícil.
  • Azad en 2020 “iOS Kernel PAC, One Year Later” reportó que en iOS 12-13 encontró algunos bypasses parciales (gadget signing, reuse de estados firmados, ramas indirectas no protegidas) pero no un bypass genérico completo. bazad.github.io
  • Las customizaciones de Apple (“Dark Magic”) reducen aún más las superficies explotables (domain switching, bits de enable por clave). i.blackhat.com
  • Existe un conocido kernel PAC bypass CVE-2023-32424 en Apple silicon (M1/M2) reportado por Zecao Cai et al. i.blackhat.com
  • Pero estos bypasses a menudo dependen de gadgets muy específicos o bugs de implementación; no son bypasses de propósito general.

Así que el kernel PAC se considera altamente robusto, aunque no perfecto.

  1. Técnicas de bypass en user-mode / runtime PAC

Estas son más comunes, y explotan imperfecciones en cómo PAC se aplica o usa en dynamic linking / runtimes. Abajo están las clases, con ejemplos.

2.1 Shared Cache / problemas con la key A

  • El dyld shared cache es un gran blob pre-linked de system frameworks y librerías. Debido a que es tan compartido, function pointers dentro del shared cache están “pre-signed” y luego usados por muchos procesos. Los atacantes apuntan a esos punteros ya firmados como “PAC oracles”.

  • Algunas técnicas de bypass intentan extraer o reutilizar punteros firmados con la key A presentes en el shared cache y reutilizarlos en gadgets.

  • La charla “No Clicks Required” describe construir un oracle sobre el shared cache para inferir direcciones relativas y combinar eso con punteros firmados para bypassear PAC. saelo.github.io

  • Además, imports de function pointers de librerías compartidas en userspace fueron hallados insuficientemente protegidos por PAC en ciertos casos, permitiendo al atacante obtener function pointers sin cambiar su firma. (entrada de bug de Project Zero) bugs.chromium.org

2.2 dlsym(3) / resolución dinámica de símbolos

  • Un bypass conocido es llamar a dlsym() para obtener un function pointer ya firmado (signed con A-key, diversifier cero) y usarlo. Porque dlsym devuelve un puntero legítimamente firmado, usarlo evita la necesidad de forjar PAC.

  • El blog de Epsilon detalla cómo algunos bypasses explotan esto: llamar dlsym("someSym") devuelve un puntero firmado y puede usarse para llamadas indirectas. blog.epsilon-sec.com

  • Synacktiv en “iOS 18.4 — dlsym considered harmful” describe un bug: algunos símbolos resueltos vía dlsym en iOS 18.4 retornan punteros que están incorrectamente firmados (o con diversificadores defectuosos), habilitando bypasses involuntarios de PAC. Synacktiv

  • La lógica en dyld para dlsym incluye: cuando result->isCode, firman el puntero retornado con __builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), es decir, contexto cero. blog.epsilon-sec.com

Por eso, dlsym es un vector frecuente en bypasses de PAC en user-mode.

2.3 Otras relocations de DYLD / runtime

  • El loader DYLD y la lógica de relocación dinámica es compleja y a veces mapea temporalmente páginas como read/write para realizar relocaciones, luego las vuelve read-only. Los atacantes explotan estas ventanas. La charla de Synacktiv describe “Operation Triangulation”, un bypass basado en timing de PAC vía relocaciones dinámicas. Synacktiv

  • Las páginas de DYLD ahora están protegidas con SPRR / VM_FLAGS_TPRO (algunas flags de protección para dyld). Pero versiones anteriores tenían protecciones más débiles. Synacktiv

  • En cadenas de exploit de WebKit, el loader DYLD suele ser objetivo para bypasses de PAC. Las slides mencionan que muchos bypasses de PAC han atacado el loader DYLD (vía relocación, interposer hooks). Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • En cadenas de exploit en userland, métodos del runtime Objective-C como NSPredicate, NSExpression o NSInvocation se usan para introducir llamadas de control sin necesidad de forjar punteros visiblemente.

  • En iOS más antiguo (antes de PAC), un exploit usaba fake NSInvocation objects para invocar selectors arbitrarios sobre memoria controlada. Con PAC se requiere modificar la técnica. Pero la técnica SLOP (SeLector Oriented Programming) se ha extendido bajo PAC también. Project Zero

  • La técnica SLOP original permitía encadenar llamadas ObjC creando invocations falsas; el bypass se apoya en que a veces los punteros ISA o selector no están completamente protegidos por PAC. Project Zero

  • En entornos donde pointer authentication se aplica parcialmente, métodos / selectors / target pointers pueden no tener siempre protección PAC, dejando espacio para bypass.

Ejemplo de flujo

Ejemplo de firmado y autenticación ``` ; 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>Ejemplo</summary>
Un desbordamiento de buffer sobrescribe una dirección de retorno en la stack. El atacante escribe la dirección del gadget objetivo pero no puede calcular el PAC correcto. Cuando la función retorna, la instrucción CPU `AUTIA` falla porque hay un desajuste de PAC. La cadena falla.
El análisis de Project Zero sobre A12 (iPhone XS) mostró cómo se usa el PAC de Apple y métodos para forjar PACs si un atacante tiene un primitive de lectura/escritura de memoria.
</details>


### 9. **Branch Target Identification (BTI)**
**Introducido con ARMv8.5 (hardware posterior)**
BTI es una característica de hardware que comprueba los **targets de ramas indirectas**: al ejecutar `blr` o llamadas/saltos indirectos, el objetivo debe comenzar con un **BTI landing pad** (`BTI j` o `BTI c`). Saltar a direcciones de gadget que carezcan del landing pad provoca una excepción.

La implementación de LLVM señala tres variantes de instrucciones BTI y cómo se asignan a tipos de branch.

| Variante BTI | Qué permite (qué tipos de branch) | Colocación típica / caso de uso |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets de ramas indirectas estilo *call* (p. ej. `BLR`, o `BR` usando X16/X17) | Colocado en la entrada de funciones que pueden ser llamadas indirectamente |
| **BTI J** | Targets de ramas estilo *jump* (p. ej. `BR` usado para tail calls) | Colocado al inicio de bloques alcanzables por jump tables o tail-calls |
| **BTI JC** | Actúa como C y J | Puede ser objetivo de llamadas o saltos |

- En código compilado con enforcement de branch target, los compiladores insertan una instrucción BTI (C, J o JC) en cada target válido de rama indirecta (comienzos de funciones o bloques alcanzables por saltos) para que las ramas indirectas solo tengan éxito hacia esos lugares.
- **Branches / llamadas directas** (i.e. direcciones fijas `B`, `BL`) **no están restringidas** por BTI. La suposición es que las páginas de código son de confianza y el atacante no puede cambiarlas (por eso las ramas directas son seguras).
- Además, las instrucciones **RET / return** generalmente no están restringidas por BTI porque las direcciones de retorno están protegidas vía PAC o mecanismos de firma de retorno.

#### Mecanismo y aplicación

- Cuando la CPU decodifica una **branch indirecta (BLR / BR)** en una página marcada como “guarded / BTI-enabled”, comprueba si la primera instrucción de la dirección objetivo es un BTI válido (C, J, o JC según lo permitido). Si no lo es, ocurre una **Branch Target Exception**.
- La codificación de la instrucción BTI está diseñada para reutilizar opcodes reservados anteriormente para NOPs (en versiones anteriores de ARM). Por eso los binarios con BTI siguen siendo retrocompatibles: en hardware sin soporte BTI, esas instrucciones actúan como NOPs.
- Los passes del compilador que añaden BTIs los insertan solo donde se necesitan: funciones que pueden ser llamadas indirectamente, o bloques básicos apuntados por saltos.
- Algunos parches y código de LLVM muestran que BTI no se inserta en *todos* los bloques básicos — solo en aquellos que son potencialmente objetivos de branch (p. ej. de switch / jump tables).

#### Sinergia BTI + PAC

PAC protege el valor del puntero (la fuente) — asegura que la cadena de llamadas/retornos indirectos no haya sido manipulada.

BTI asegura que incluso un puntero válido solo pueda dirigirse a puntos de entrada correctamente marcados.

Combinados, un atacante necesita tanto un puntero válido con PAC correcto como que el objetivo tenga un BTI colocado allí. Esto aumenta la dificultad de construir gadgets explotables.

#### Ejemplo


<details>
<summary>Ejemplo</summary>
Un exploit intenta pivotar hacia un gadget en `0xABCDEF` que no empieza con `BTI c`. La CPU, al ejecutar `blr x0`, comprueba el objetivo y falla porque la alineación/instrucción no incluye un landing pad válido. Así, muchos gadgets se vuelven inutilizables a menos que incluyan el prefijo BTI.
</details>


### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Introducido en extensiones ARMv8 más recientes / soporte iOS (para kernel hardened)**

#### PAN (Privileged Access Never)

- **PAN** es una característica introducida en **ARMv8.1-A** que evita que el código **privilegiado** (EL1 o EL2) **lea o escriba** memoria marcada como **accesible por usuario (EL0)**, a menos que PAN se desactive explícitamente.
- La idea: incluso si el kernel es engañado o comprometido, no puede desreferenciar arbitrariamente punteros de user-space sin primero *limpiar* PAN, reduciendo así riesgos de exploits estilo **`ret2usr`** o uso indebido de buffers controlados por el usuario.
- Cuando PAN está habilitado (PSTATE.PAN = 1), cualquier instrucción privilegiada de carga/almacenamiento que acceda a una dirección virtual “accesible en EL0” provoca una **falla de permisos**.
- El kernel, cuando necesita legítimamente acceder a memoria user-space (p. ej. copiar datos desde/hacia buffers user), debe **deshabilitar temporalmente PAN** (o usar instrucciones de load/store “no privilegiadas”) para permitir ese acceso.
- En Linux en ARM64, el soporte PAN se introdujo alrededor de 2015: parches al kernel añadieron detección de la característica y reemplazaron `get_user` / `put_user` etc. por variantes que limpian PAN alrededor de accesos a memoria usuario.

**Matiz / limitación / bug**
- Como han señalado Siguza y otros, un bug de especificación (o comportamiento ambiguo) en el diseño de ARM significa que los mapeos user execute-only (`--x`) pueden **no activar PAN**. En otras palabras, si una página user está marcada ejecutable pero sin permiso de lectura, el intento del kernel de leer podría eludir PAN porque la arquitectura considera “accesible en EL0” como que requiere permiso de lectura, no solo ejecutable. Esto conduce a un bypass de PAN en ciertas configuraciones.
- Debido a eso, si iOS / XNU permite páginas user execute-only (como podría ocurrir en algunos setups de JIT o code-cache), el kernel podría leer accidentalmente desde ellas incluso con PAN habilitado. Esta es un área sutil conocida y explotable en algunos sistemas ARMv8+.

#### PXN (Privileged eXecute Never)

- **PXN** es un flag de la page table (en las entradas de tabla de páginas, leaf o block entries) que indica que la página **no es ejecutable cuando se ejecuta en modo privilegiado** (i.e. cuando EL1 ejecuta).
- PXN evita que el kernel (o cualquier código privilegiado) salte o ejecute instrucciones desde páginas de user-space incluso si se desvía el control. En efecto, impide una redirección de control a código user desde nivel kernel.
- Combinado con PAN, esto asegura que:
1. El kernel no puede (por defecto) leer o escribir datos de user-space (PAN)
2. El kernel no puede ejecutar código de user-space (PXN)
- En el formato de tabla de páginas ARMv8, las entradas leaf tienen un bit `PXN` (y también `UXN` para execute-never en modo no privilegiado) en sus bits de atributos.

Así, incluso si el kernel tiene un puntero a función corrompido apuntando a user memory, y tratara de ramificar allí, el bit PXN provocaría una falla.

#### Modelo de permisos de memoria y cómo PAN y PXN se mapean a bits de la tabla de páginas

Para entender cómo funcionan PAN / PXN, hay que ver cómo opera el modelo de traducción y permisos de ARM (simplificado):

- Cada página o block entry tiene campos de atributos incluyendo **AP[2:1]** para permisos de acceso (lectura/escritura, privilegiado vs no privilegiado) y bits **UXN / PXN** para restricciones de execute-never.
- Cuando PSTATE.PAN es 1 (habilitado), el hardware aplica semánticas modificadas: los accesos privilegiados a páginas marcadas como “accesibles por EL0” (i.e. accesibles por el usuario) quedan deshabilitados (falla).
- Debido al bug mencionado, las páginas marcadas solo como ejecutables (sin permiso de lectura) pueden no contar como “accesibles por EL0” bajo ciertas implementaciones, por lo que PAN no las bloquearía.
- Cuando el bit PXN de una página está establecido, incluso si la fetch de instrucción proviene de un nivel de mayor privilegio, la ejecución está prohibida.

#### Uso del kernel de PAN / PXN en un OS hardened (p. ej. iOS / XNU)

En un diseño de kernel hardened (como el que Apple podría usar):

- El kernel habilita PAN por defecto (por lo que el código privilegiado está restringido).
- En rutas que legítimamente necesitan leer o escribir buffers user (p. ej. copia de buffers de syscall, I/O, read/write de punteros de usuario), el kernel **deshabilita temporalmente PAN** o usa instrucciones especiales para anularlo.
- Tras terminar el acceso a datos de usuario, debe volver a habilitar PAN.
- PXN se aplica vía tablas de páginas: las páginas user tienen PXN = 1 (por lo que el kernel no puede ejecutarlas), las páginas del kernel no tienen PXN (por lo que el código kernel puede ejecutarlas).
- El kernel debe asegurar que ningún camino de ejecución redirija el flujo hacia regiones de memoria user (eso evitaría bypass de PXN) — por lo que las cadenas de explotación que dependen de “saltar al shellcode controlado por el usuario” quedan bloqueadas.

Debido al bypass de PAN vía páginas execute-only, en un sistema real Apple podría deshabilitar o prohibir páginas user execute-only, o parchear alrededor de la debilidad de la especificación.

#### Superficies de ataque, bypasses y mitigaciones

- **PAN bypass vía páginas execute-only**: como se discutió, la especificación permite una brecha: páginas user con execute-only (sin permiso de lectura) podrían no contarse como “accesibles en EL0”, por lo que PAN no bloquearía lecturas del kernel en algunas implementaciones. Esto da al atacante una vía inusual para alimentar datos mediante secciones “execute-only”.
- **Exploit por ventana temporal**: si el kernel desactiva PAN por una ventana más larga de la necesaria, una carrera o ruta maliciosa podría explotar esa ventana para realizar accesos a memoria user no deseados.
- **Olvido de re-habilitar**: si los caminos de código fallan al volver a habilitar PAN, operaciones kernel posteriores podrían acceder incorrectamente a memoria user.
- **Mala configuración de PXN**: si las tablas de páginas no establecen PXN en páginas user o mapean incorrectamente páginas de código user, el kernel podría ser engañado para ejecutar código user.
- **Especulación / canales laterales**: análogo a bypasses especulativos, puede haber efectos microarquitecturales transitorios que causen violaciones temporales de las comprobaciones PAN / PXN (aunque tales ataques dependen mucho del diseño de la CPU).
- **Interacciones complejas**: en características más avanzadas (p. ej. JIT, shared memory, regiones de código just-in-time), el kernel puede necesitar control fino para permitir ciertos accesos o ejecuciones en regiones mapeadas por user; diseñar eso de forma segura bajo las restricciones PAN/PXN no es trivial.

#### Ejemplo

<details>
<summary>Ejemplo de código</summary>
Aquí hay secuencias pseudo-assembly ilustrativas que muestran habilitar/deshabilitar PAN alrededor del acceso a memoria user, y cómo podría ocurrir una falla.

// 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

If the kernel had **not** set PXN on that user page, then the branch might succeed — which would be insecure.

If the kernel forgets to re-enable PAN after user memory access, it opens a window where further kernel logic might accidentally read/write arbitrary user memory.

If the user pointer is into an execute-only page (user page with only execute permission, no read/write), under the PAN spec bug, `ldr W2, [X1]` might **not** fault even with PAN enabled, enabling a bypass exploit, depending on implementation.

</details>

<details>
<summary>Example</summary>
A kernel vulnerability tries to take a user-provided function pointer and call it in kernel context (i.e. `call user_buffer`). Under PAN/PXN, that operation is disallowed or faults.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI means the top byte (most-significant byte) of a 64-bit pointer is ignored by address translation. This lets OS or hardware embed **tag bits** in the pointer’s top byte without affecting the actual address.

- 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**: You can store extra metadata (e.g. object type, version, bounds, integrity tags) in that top byte. When you later use the pointer, the tag is ignored at hardware level, so you don’t need to strip manually for the memory access.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is the base hardware mechanism that MTE builds on. In ARMv8.5, the **Memory Tagging Extension** uses bits 59:56 of the pointer as a **logical tag** and checks it against an **allocation tag** stored in memory.
- **Enhanced security & integrity**: By combining TBI with pointer authentication (PAC) or runtime checks, you can force not just the pointer value but also the tag to be correct. An attacker overwriting a pointer without the correct tag will produce a mismatched tag.
- **Compatibility**: Because TBI is optional and tag bits are ignored by hardware, existing untagged code continues to operate normally. The tag bits effectively become “don’t care” bits for legacy code.

#### Example
<details>
<summary>Example</summary>
A function pointer included a tag in its top byte (say `0xAA`). An exploit overwrites the pointer low bits but neglects the tag, so when the kernel verifies or sanitizes, the pointer fails or is rejected.
</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 is designed as an **intra-kernel protection boundary**: even if the kernel (EL1) is compromised and has read/write capabilities, **it should not be able to freely modify** certain **sensitive pages** (especially page tables, code-signing metadata, kernel code pages, entitlements, trust caches, etc.).
- It effectively creates a **“kernel within the kernel”** — a smaller trusted component (PPL) with **elevated privileges** that alone can modify protected pages. Other kernel code must call into PPL routines to effect changes.
- This reduces the attack surface for kernel exploits: even with full arbitrary R/W/execute in kernel mode, exploit code must also somehow get into the PPL domain (or bypass PPL) to modify critical structures.
- On newer Apple silicon (A15+ / M2+), Apple is transitioning to **SPTM (Secure Page Table Monitor)**, which in many cases replaces PPL for page-table protection on those platforms.

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

El kernel puede realizar muchas operaciones normales, pero solo a través de las rutinas ppl_call_* puede cambiar mapeos protegidos o parchear código.

Ejemplo Un kernel exploit intenta sobrescribir la entitlement table, o deshabilitar el enforcement de code-sign modificando un kernel signature blob. Como esa página está protegida por PPL, la escritura queda bloqueada a menos que se realice a través de la interfaz PPL. Así que incluso con ejecución de código en el kernel, no puedes eludir las restricciones de code-sign ni modificar arbitrariamente credential data. En iOS 17+ ciertos dispositivos usan SPTM para aislar aún más las páginas gestionadas por PPL.

PPL → SPTM / Replacements / Future

  • On Apple’s modern SoCs (A15 or later, M2 or later), Apple supports SPTM (Secure Page Table Monitor), which replaces PPL for page table protections.
  • Apple calls out in documentation: “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.”
  • The SPTM architecture likely shifts more policy enforcement into a higher-privileged monitor outside kernel control, further reducing the trust boundary.

MTE | EMTE | MIE

Aquí hay una descripción a alto nivel de cómo opera EMTE bajo la configuración MIE de Apple:

  1. Tag assignment
  • Cuando se asigna memoria (p. ej. en kernel o user space vía secure allocators), a ese bloque se le asigna un secret tag.
  • El puntero devuelto al usuario o al kernel incluye ese tag en sus bits altos (usando TBI / top byte ignore mechanisms).
  1. Tag checking on access
  • Siempre que se ejecuta una load o store usando un puntero, el hardware verifica que el tag del puntero coincida con el tag del bloque de memoria (allocation tag). Si hay mismatch, falla inmediatamente (ya que es synchronous).
  • Debido a que es synchronous, no existe una ventana de “delayed detection”.
  1. Retagging on free / reuse
  • Cuando la memoria se libera, el allocator cambia el tag del bloque (por lo que punteros antiguos con tags viejos ya no coinciden).
  • Un puntero use-after-free por tanto tendrá un tag stale y dará mismatch al acceder.
  1. Neighbor-tag differentiation to catch overflows
  • A las allocations adyacentes se les asignan tags distintos. Si un buffer overflow se desborda hacia la memoria del vecino, el tag mismatch provoca una fault.
  • Esto es especialmente eficaz para detectar small overflows que cruzan el boundary.
  1. Tag confidentiality enforcement
  • Apple debe evitar que los valores de tag sean leaked (porque si un attacker aprende el tag, podría crear punteros con los tags correctos).
  • Incluyen protecciones (microarchitectural / speculative controls) para evitar side-channel leakage de los bits de tag.
  1. Kernel and user-space integration
  • Apple usa EMTE no solo en user-space sino también en componentes críticos del kernel/OS (para proteger al kernel contra memory corruption).
  • El hardware/OS asegura que las reglas de tag se apliquen incluso cuando el kernel ejecuta en nombre de user space.
Ejemplo ``` 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>

#### Limitaciones & desafíos

- **Intrablock overflows**: Si el overflow permanece dentro de la misma asignación (no cruza el límite) y el tag sigue siendo el mismo, la comprobación de tag no lo detecta.
- **Tag width limitation**: Solo hay unos pocos bits (p.ej. 4 bits, o un dominio pequeño) disponibles para el tag—espacio de nombres limitado.
- **Side-channel leaks**: Si los bits de tag pueden ser leaked (vía caché / ejecución especulativa), un atacante podría aprender tags válidos y eludir la protección. Apple’s tag confidentiality enforcement está pensada para mitigar esto.
- **Performance overhead**: Las comprobaciones de tag en cada load/store añaden coste; Apple debe optimizar el hardware para reducir la sobrecarga.
- **Compatibility & fallback**: En hardware antiguo o en partes que no soportan EMTE, debe existir un fallback. Apple afirma que MIE solo está habilitado en dispositivos con soporte.
- **Complex allocator logic**: El allocator debe gestionar tags, retagging, alinear límites y evitar colisiones por mis-tag. Bugs en la lógica del allocator podrían introducir vulnerabilidades.
- **Mixed memory / hybrid areas**: Parte de la memoria puede permanecer untagged (legacy), lo que hace la interoperabilidad más compleja.
- **Speculative / transient attacks**: Como con muchas protecciones microarquitecturales, la ejecución especulativa o la fusión de micro-ops podrían eludir comprobaciones de forma transitoria o leak bits de tag.
- **Limited to supported regions**: Apple podría aplicar EMTE solo en áreas selectivas y de alto riesgo (kernel, subsistemas críticos de seguridad), no de forma universal.

---

## Principales mejoras / diferencias respecto a MTE estándar

Aquí están las mejoras y cambios que Apple enfatiza:

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Soporta modos síncrono y asíncrono. En async, los tag mismatches se reportan más tarde (retrasados) | Apple insiste en **synchronous mode** por defecto—los tag mismatches se detectan inmediatamente, sin ventanas de delay/race. |
| **Coverage of non-tagged memory** | Accesos a memoria no-taged (p.ej. globals) pueden eludir comprobaciones en algunas implementaciones | EMTE requiere que los accesos desde una región taggeada a memoria no-taged también validen el conocimiento del tag, dificultando el bypass mediante mezcla de allocations. |
| **Tag confidentiality / secrecy** | Los tags podrían ser observables o filtrarse vía side channels | Apple añade **Tag Confidentiality Enforcement**, que intenta impedir la leakabilidad de los valores de tag (vía canales especulativos, etc.). |
| **Allocator integration & retagging** | MTE deja gran parte de la lógica del allocator al software | Los allocators tipados seguros de Apple (kalloc_type, xzone malloc, etc.) se integran con EMTE: cuando se asigna o libera memoria, los tags se gestionan con granularidad fina. |
| **Always-on by default** | En muchas plataformas, MTE es opcional o está desactivado por defecto | Apple habilita EMTE / MIE por defecto en hardware soportado (p.ej. iPhone 17 / A19) para kernel y muchos procesos de usuario. |

Debido a que Apple controla tanto el hardware como la pila de software, puede hacer cumplir EMTE de forma estricta, evitar problemas de rendimiento y cerrar huecos de side-channel.

---

## Cómo funciona EMTE en la práctica (Apple / MIE)

Aquí hay una descripción a alto nivel de cómo opera EMTE bajo la configuración MIE de Apple:

1. **Tag assignment**
- Cuando se asigna memoria (p.ej. en kernel o espacio de usuario vía secure allocators), se asigna un **secret tag** al bloque.
- El pointer devuelto al usuario o al kernel incluye ese tag en sus bits altos (usando TBI / top byte ignore mechanisms).

2. **Tag checking on access**
- Siempre que se ejecuta un load o store usando un pointer, el hardware comprueba que el tag del pointer coincide con el tag del bloque de memoria (allocation tag). Si hay mismatch, falla inmediatamente (ya que es síncrono).
- Al ser síncrono, no existe una ventana de “detención retrasada”.

3. **Retagging on free / reuse**
- Cuando se libera memoria, el allocator cambia el tag del bloque (por lo que pointers antiguos con tags obsoletos ya no coinciden).
- Un pointer use-after-free por tanto tendrá un tag desactualizado y dará mismatch al acceder.

4. **Neighbor-tag differentiation to catch overflows**
- A las allocations adyacentes se les dan tags distintos. Si un buffer overflow desborda hacia la memoria del vecino, el tag mismatch provoca un fallo.
- Esto es especialmente efectivo para detectar pequeños overflows que cruzan el límite.

5. **Tag confidentiality enforcement**
- Apple debe evitar que los valores de tag sean expuestos (porque si un atacante aprende el tag, podría construir pointers con tags correctos).
- Incluyen protecciones (controles microarquitecturales / especulativos) para evitar side-channel leakage de bits de tag.

6. **Kernel and user-space integration**
- Apple usa EMTE no solo en user-space sino también en componentes críticos del kernel/OS (para proteger el kernel contra corrupción de memoria).
- El hardware/OS asegura que las reglas de tag se apliquen incluso cuando el kernel ejecuta en nombre del espacio de usuario.

Porque EMTE está integrado en MIE, Apple usa EMTE en modo síncrono en las superficies de ataque clave, no como opción o modo de depuración.

---

## Exception handling in XNU

Cuando ocurre una **excepción** (p.ej., `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, etc.), la capa **Mach** del kernel XNU es responsable de interceptarla antes de que se convierta en una señal estilo UNIX (como `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).

Este proceso implica múltiples capas de propagación y manejo de excepciones antes de alcanzar el espacio de usuario o de ser convertido en una BSD signal.


### Flujo de excepciones (alto nivel)

1.  **La CPU dispara una excepción síncrona** (p.ej., desreferencia de pointer inválido, fallo de PAC, instrucción ilegal, etc.).

2.  **El handler de trap de bajo nivel** se ejecuta (`trap.c`, `exception.c` en el source de XNU).

3.  El trap handler llama a **`exception_triage()`**, el núcleo del manejo de excepciones Mach.

4.  `exception_triage()` decide cómo enrutar la excepción:

-   Primero al **exception port del thread**.

-   Luego al **exception port del task**.

-   Luego al **exception port del host** (a menudo `launchd` o `ReportCrash`).

Si ninguno de estos ports maneja la excepción, el kernel puede:

-   **Convertirla en una BSD signal** (para procesos de user-space).

-   **Provocar un panic** (para excepciones en kernel-space).


### Función principal: `exception_triage()`

La función `exception_triage()` enruta las excepciones Mach a lo largo de la cadena de posibles manejadores hasta que uno la maneja o hasta que es finalmente fatal. Está definida en `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

Flujo de llamadas típico:

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

Si todos fallan → manejado por bsd_exception() → traducido en una señal como SIGSEGV.

Puertos de excepción

Cada objeto Mach (hilo, tarea, host) puede registrar puertos de excepción, a los que se envían los mensajes de excepción.

Están definidos por la API:

task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()

Cada puerto de excepción tiene:

  • Un mask (qué excepciones quiere recibir)
  • Un port name (puerto Mach que recibe los mensajes)
  • Un behavior (cómo el kernel envía el mensaje)
  • Un flavor (qué thread state incluir)

Debuggers and Exception Handling

Un debugger (p. ej., LLDB) configura un exception port en la tarea o hilo objetivo, normalmente usando task_set_exception_ports().

Cuando ocurre una excepción:

  • El mensaje Mach se envía al proceso del debugger.
  • El debugger puede decidir manejar (reanudar, modificar registros, saltar instrucción) o no manejar la excepción.
  • Si el debugger no la maneja, la excepción se propaga al siguiente nivel (task → host).

Flow of EXC_BAD_ACCESS

  1. El thread desreferencia un puntero inválido → la CPU genera un Data Abort.

  2. El manejador de traps del kernel llama a exception_triage(EXC_BAD_ACCESS, ...).

  3. Se envía un mensaje a:

  • Thread port → (el debugger puede interceptar con breakpoint).

  • Si el debugger lo ignora → Task port → (manejador a nivel de proceso).

  • Si se ignora → Host port (usualmente ReportCrash).

  1. Si nadie lo maneja → bsd_exception() lo traduce a SIGSEGV.

PAC Exceptions

Cuando falla Pointer Authentication (PAC) (desajuste de firma), se levanta una excepción Mach especial:

  • EXC_ARM_PAC (tipo)
  • Los códigos pueden incluir detalles (p. ej., tipo de key, tipo de puntero).

Si el binario tiene la bandera TFRO_PAC_EXC_FATAL, el kernel trata las fallas de PAC como fatales, evitando la intercepción por parte del debugger. Esto se hace para impedir que atacantes usen debuggers para eludir las comprobaciones PAC y está habilitado para platform binaries.

Software Breakpoints

Un breakpoint de software (int3 en x86, brk en ARM64) se implementa provocando deliberadamente una falla.
El debugger lo captura a través del exception port:

  • Modifica el instruction pointer o la memoria.
  • Restaura la instrucción original.
  • Reanuda la ejecución.

Este mismo mecanismo es lo que te permite “capturar” una excepción PAC — a menos que TFRO_PAC_EXC_FATAL esté activado, en cuyo caso nunca llega al debugger.

Conversion to BSD Signals

Si ningún manejador acepta la excepción:

  • El kernel llama a task_exception_notify() → bsd_exception().

  • Esto mapea excepciones Mach a señales:

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 señales.

  • osfmk/arm64/trap.c → Manejadores de traps a bajo nivel.

  • osfmk/mach/exc.h → Códigos de excepción y estructuras.

  • osfmk/kern/task.c → Configuración de task exception port.


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

El kernel usaba un zone allocator (kalloc) dividido en “zones” de tamaño fijo.
Cada zone solo almacenaba asignaciones de una única clase de tamaño.

De la captura:

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.

Cómo funcionaba:

  • Cada solicitud de asignación se redondeaba hacia arriba al tamaño de zone más cercano. (P. ej., una petición de 50 bytes cae en la zone kalloc.64).
  • La memoria en cada zone se mantenía en una freelist — los chunks liberados por el kernel volvían a esa zone.
  • Si desbordabas un buffer de 64 bytes, sobrescribías el siguiente objeto en la misma zone.

Por eso el heap spraying / feng shui fue tan efectivo: podías predecir vecinos de objetos al pulverizar asignaciones de la misma clase de tamaño.

The freelist

Dentro de cada kalloc zone, los objetos liberados no se devolvían directamente al sistema — iban a una freelist, una lista enlazada de chunks disponibles.

  • Cuando se liberaba un chunk, el kernel escribía un puntero al inicio de ese chunk → la dirección del siguiente chunk libre en la misma zone.

  • La zone mantenía un puntero HEAD al primer chunk libre.

  • La asignación siempre usaba el HEAD actual:

  1. Pop HEAD (devolver esa memoria al llamador).

  2. Actualizar HEAD = HEAD->next (almacenado en el header del chunk liberado).

  • Liberar empujaba chunks de vuelta:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

Así que la freelist era simplemente una lista enlazada construida dentro de la memoria liberada.

Estado normal:

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)

Explotando el freelist

Porque los primeros 8 bytes de un free chunk = freelist pointer, un atacante podría corromperlo:

  1. Heap overflow hacia un chunk liberado adyacente → sobrescribir su “next” pointer.

  2. Use-after-free escribir en un objeto liberado → sobrescribir su “next” pointer.

Entonces, en la siguiente asignación de ese tamaño:

  • El allocator pops el chunk corrompido.

  • Sigue el “next” pointer proporcionado por el atacante.

  • Devuelve un puntero a memoria arbitraria, habilitando fake object primitives o overwrite dirigido.

Ejemplo 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 made exploitation highly effective pre-hardening: predictable neighbors from heap sprays, raw pointer freelist links, and no type separation allowed attackers to escalate UAF/overflow bugs into arbitrary kernel memory control.

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.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.

Pasos:

  1. Spray allocations (fill the holes)
  • Over time, the kernel heap gets fragmented: some zones have holes where old objects were freed.
  • The attacker first makes lots of dummy allocations to fill these gaps, so the heap becomes “packed” and predictable.
  1. Force new pages
  • Once the holes are filled, the next allocations must come from new pages added to the zone.
  • Fresh pages mean objects will be clustered together, not scattered across old fragmented memory.
  • This gives the attacker much better control of neighbors.
  1. Place attacker objects
  • The attacker now sprays again, creating lots of attacker-controlled objects in those new pages.
  • These objects are predictable in size and placement (since they all belong to the same zone).
  1. Free a controlled object (make a gap)
  • The attacker deliberately frees one of their own objects.
  • This creates a “hole” in the heap, which the allocator will later reuse for the next allocation of that size.
  1. Victim object lands in the hole
  • The attacker triggers the kernel to allocate the victim object (the one they want to corrupt).
  • Since the hole is the first available slot in the freelist, the victim is placed exactly where the attacker freed their object.
  1. Overflow / UAF into victim
  • Now the attacker has attacker-controlled objects around the victim.
  • By overflowing from one of their own objects (or reusing a freed one), they can reliably overwrite the victim’s memory fields with chosen values.

Por qué funciona:

  • Zone allocator predictability: allocations of the same size always come from the same zone.
  • Freelist behavior: new allocations reuse the most recently freed chunk first.
  • Heap sprays: attacker fills memory with predictable content and controls layout.
  • End result: attacker controls where the victim object lands and what data sits next to it.

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

Apple hardened the allocator and made heap grooming much harder:

1. From Classic kalloc to kalloc_type

  • Before: a single kalloc.<size> zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects.
  • Now:
  • Kernel objects are allocated from typed zones (kalloc_type).
  • Each type of object (e.g., ipc_port_t, task_t, OSString, OSData) has its own dedicated zone, even if they’re the same size.
  • The mapping between object type ↔ zone is generated from the kalloc_type system at compile time.

An attacker can no longer guarantee that controlled data (OSData) ends up adjacent to sensitive kernel objects (task_t) of the same size.

2. Slabs and Per-CPU Caches

  • The heap is divided into slabs (pages of memory carved into fixed-size chunks for that zone).
  • Each zone has a per-CPU cache to reduce contention.
  • Allocation path:
  1. Try per-CPU cache.
  2. If empty, pull from the global freelist.
  3. If freelist is empty, allocate a new slab (one or more pages).
  • Benefit: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches.

3. Randomization inside zones

  • Within a zone, freed elements are not handed back in simple FIFO/LIFO order.
  • Modern XNU uses encoded freelist pointers (safe-linking like Linux, introduced ~iOS 14).
  • Each freelist pointer is XOR-encoded with a per-zone secret cookie.
  • This prevents attackers from forging a fake freelist pointer if they gain a write primitive.
  • Some allocations are randomized in their placement within a slab, so spraying doesn’t guarantee adjacency.

4. Guarded Allocations

  • Certain critical kernel objects (e.g., credentials, task structures) are allocated in guarded zones.
  • These zones insert guard pages (unmapped memory) between slabs or use redzones around objects.
  • Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption.

5. Page Protection Layer (PPL) and SPTM

  • Even if you control a freed object, you can’t modify all of kernel memory:
  • PPL (Page Protection Layer) enforces that certain regions (e.g., code signing data, entitlements) are read-only even to the kernel itself.
  • On A15/M2+ devices, this role is replaced/enhanced by SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
  • These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures.
  • (Added / Enhanced): also, PAC (Pointer Authentication Codes) is used in the kernel to protect pointers (especially function pointers, vtables) so that forging or corrupting them becomes harder.
  • (Added / Enhanced): zones may enforce zone_require / zone enforcement, i.e. that an object freed can only be returned through its correct typed zone; invalid cross-zone frees may panic or be rejected. (Apple alludes to this in their memory safety posts)

6. Large Allocations

  • Not all allocations go through kalloc_type.
  • Very large requests (above ~16 KB) bypass typed zones and are served directly from kernel VM (kmem) via page allocations.
  • These are less predictable, but also less exploitable, since they don’t share slabs with other objects.

7. Allocation Patterns Attackers Target

Even with these protections, attackers still look for:

  • Reference count objects: if you can tamper with retain/release counters, you may cause use-after-free.
  • Objects with function pointers (vtables): corrupting one still yields control flow.
  • Shared memory objects (IOSurface, Mach ports): these are still attack targets because they bridge user ↔ kernel.

But — unlike before — you can’t just spray OSData and expect it to neighbor a task_t. You need type-specific bugs or info leaks to succeed.

Example: Allocation Flow in Modern Heap

Suppose userspace calls into IOKit to allocate an OSData object:

  1. Type lookupOSData maps to kalloc_type_osdata zone (size 64 bytes).
  2. Check per-CPU cache for free elements.
  • If found → return one.
  • If empty → go to global freelist.
  • If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
  1. Return chunk to caller.

Freelist pointer protection:

  • Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
  • Overwriting that field with attacker data won’t work unless you know the key.

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)

In recent Apple OS versions (especially iOS 17+), Apple introduced a more secure userland allocator, xzone malloc (XZM). This is the user-space analog to the kernel’s kalloc_type, applying type awareness, metadata isolation, and memory tagging safeguards.

Goals & Design Principles

  • Type segregation / type awareness: group allocations by type or usage (pointer vs data) to prevent type confusion and cross-type reuse.
  • Metadata isolation: separate heap metadata (e.g. free lists, size/state bits) from object payloads so that out-of-bounds writes are less likely to corrupt metadata.
  • Guard pages / redzones: insert unmapped pages or padding around allocations to catch overflows.
  • Memory tagging (EMTE / MIE): work in conjunction with hardware tagging to detect use-after-free, out-of-bounds, and invalid accesses.
  • Scalable performance: maintain low overhead, avoid excessive fragmentation, and support many allocations per second with low latency.

Architecture & Components

Below are the main elements in the xzone allocator:

Segment Groups & Zones

  • Segment groups partition the address space by usage categories: e.g. data, pointer_xzones, data_large, pointer_large.
  • Each segment group contains segments (VM ranges) that host allocations for that category.
  • Associated with each segment is a metadata slab (separate VM area) that stores metadata (e.g. free/used bits, size classes) for that segment. This out-of-line (OOL) metadata ensures that metadata is not intermingled with object payloads, mitigating corruption from overflows.
  • Segments are carved into chunks (slices) which in turn are subdivided into blocks (allocation units). A chunk is tied to a specific size class and segment group (i.e. all blocks in a chunk share the same size & category).
  • For small / medium allocations, it will use fixed-size chunks; for large/huges, it may map separately.

Chunks & Blocks

  • A chunk is a region (often several pages) dedicated to allocations of one size class within a group.
  • Inside a chunk, blocks are slots available for allocations. Freed blocks are tracked via the metadata slab — e.g. via bitmaps or free lists stored out-of-line.
  • Between chunks (or within), guard slices / guard pages may be inserted (e.g. unmapped slices) to catch out-of-bounds writes.

Type / Type ID

  • Every allocation site (or call to malloc, calloc, etc.) is associated with a type identifier (a malloc_type_id_t) which encodes what kind of object is being allocated. That type ID is passed to the allocator, which uses it to select which zone / segment to serve the allocation.
  • Because of this, even if two allocations have the same size, they may go into entirely different zones if their types differ.
  • In early iOS 17 versions, not all APIs (e.g. CFAllocator) were fully type-aware; Apple addressed some of those weaknesses in iOS 18.

Allocation & Freeing Workflow

Here is a high-level flow of how allocation and deallocation operate in xzone:

  1. malloc / calloc / realloc / typed alloc is invoked with a size and type ID.
  2. The allocator uses the type ID to pick the correct segment group / zone.
  3. Within that zone/segment, it seeks a chunk that has free blocks of the requested size.
  • It may consult local caches / per-thread pools or free block lists from metadata.
  • If no free block is available, it may allocate a new chunk in that zone.
  1. The metadata slab is updated (free bit cleared, bookkeeping).
  2. If memory tagging (EMTE) is in play, the returned block gets a tag assigned, and metadata is updated to reflect its “live” state.
  3. When free() is called:
  • The block is marked as freed in metadata (via OOL slab).
  • The block may be placed into a free list or pooled for reuse.
  • Optionally, block contents may be cleared or poisoned to reduce data leaks or use-after-free exploitation.
  • The hardware tag associated with the block may be invalidated or re-tagged.
  • If an entire chunk becomes free (all blocks freed), the allocator may reclaim that chunk (unmap it or return to OS) under memory pressure.

Security Features & Hardening

These are the defenses built into modern userland 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)

  • Apple’s MIE (Memory Integrity Enforcement) is the hardware + OS framework that brings Enhanced Memory Tagging Extension (EMTE) into always-on, synchronous mode across major attack surfaces.
  • xzone allocator is a fundamental foundation of MIE in user space: allocations done via xzone get tags, and accesses are checked by hardware.
  • In MIE, the allocator, tag assignment, metadata management, and tag confidentiality enforcement are integrated to ensure that memory errors (e.g. stale reads, OOB, UAF) are caught immediately, not exploited later.

Si quieres, también puedo generar una hoja de referencia o un diagrama de los internos de xzone para tu libro. ¿Quieres que lo haga a continuación?
:contentReference[oai:20]{index=20}

(Old) Physical Use-After-Free via IOSurface

ios Physical UAF - IOSurface


Ghidra Install BinDiff

Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.

Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and install it even if there is a version mismatch.

Using BinDiff with Kernel versions

  1. Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be .ipsw files.
  2. Decompress until you get the bin format of the kernelcache of both .ipsw files. You have information on how to do this on:

macOS Kernel Extensions & Kernelcache

  1. Open Ghidra with ghidraRun, create a new project and load the kernelcaches.
  2. Open each kernelcache so they are automatically analyzed by Ghidra.
  3. Then, on the project Window of Ghidra, right click each kernelcache, select Export, select format Binary BinExport (v2) for BinDiff and export them.
  4. Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.

Finding the right XNU version

If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).

For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version 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 in-the-wild chains abused a WebKit JIT bug (patched as CVE-2023-41993) purely to gain JavaScript-level arbitrary read/write. The exploit immediately pivots into a purchased framework called JSKit, so any future Safari bug only needs to deliver the same primitive.
  • Version abstraction & PAC bypasses: JSKit bundles support for a wide range of iOS releases together with multiple, selectable Pointer Authentication Code bypass modules. The framework fingerprints the target build, selects the appropriate PAC bypass logic, and verifies every step (primitive validation, shellcode launch) before progressing.
  • Manual Mach-O mapping: JSKit parses Mach-O headers directly from memory, resolves the symbols it needs inside dyld-cached images, and can manually map additional Mach-O payloads without writing them to disk. This keeps the renderer process in-memory only and evades code-signature checks tied to filesystem artifacts.
  • Portfolio model: Debug strings such as “exploit number 7” show that the suppliers maintain multiple interchangeable WebKit exploits. Once the JS primitive matches JSKit’s interface, the rest of the chain is unchanged across campaigns.

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

  • Kernel IPC UAF (CVE-2023-41992): The second stage, still running inside the Safari context, triggers a kernel use-after-free in IPC code, re-allocates the freed object from userland, and abuses the dangling pointers to pivot into arbitrary kernel read/write. The stage also reuses PAC bypass material previously computed by JSKit instead of re-deriving it.
  • Code-signing bypass (CVE-2023-41991): With kernel R/W available, the exploit patches the trust cache / code-signing structures so unsigned payloads execute as system. The stage then exposes a lightweight kernel R/W service to later payloads.
  • Composed pattern: This chain demonstrates a reusable recipe that defenders should expect going forward:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager

Módulos helper & watcher de PREYHUNTER

  • Watcher anti-analysis: Un binario watcher dedicado perfila continuamente el dispositivo y aborta la kill-chain cuando se detecta un entorno de investigación. Inspecciona security.mac.amfi.developer_mode_status, la presencia de una consola diagnosticd, locales US o IL, rastros de jailbreak como Cydia, procesos como bash, tcpdump, frida, sshd, o checkrain, apps de AV móviles (McAfee, AvastMobileSecurity, NortonMobileSecurity), configuraciones HTTP proxy personalizadas y root CAs personalizadas. Si falla cualquier verificación se bloquea la entrega de payloads.
  • Helper surveillance hooks: El componente helper se comunica con otras etapas a través de /tmp/helper.sock, luego carga conjuntos de hooks llamados DMHooker y UMHooker. Estos hooks interceptan rutas de audio VOIP (las grabaciones se almacenan en /private/var/tmp/l/voip_%lu_%u_PART.m4a), implementan un keylogger a nivel de sistema, capturan fotos sin UI y hookean SpringBoard para suprimir las notificaciones que normalmente generarían esas acciones. Por tanto, el helper actúa como una capa sigilosa de validación + light-surveillance antes de desplegar implantes más pesados como Predator.

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

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks