iOS Exploiting

Reading time: 59 minutes

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

iOS Exploit Mitigations

1. Code Signing / Runtime Signature Verification

Introduced early (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 enraizada 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 ha sido modificado (bit-flipped, parcheado) o no está firmado, la carga falla.

  • Impide: la etapa “classic payload drop + execute” en chains de exploit; arbitrary code injection; modificar un binario existente para insertar lógica maliciosa.
  • Detalle del mecanismo:
  • El Mach-O loader (y dynamic linker) verifica las 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 verificaciones de code-sign).
  • La firma incluye entitlements e identificadores; el OS obliga que ciertas APIs o capacidades privilegiadas requieran entitlements específicos que no se pueden falsificar.
Ejemplo Supongamos que un exploit obtiene code execution en un proceso e intenta escribir shellcode en el heap y saltar a él. En iOS, esa página tendría que marcarse como executable **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 marcar esa región de memoria como ejecutable.

2. CoreTrust

Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust es el subsistema que realiza la validación de firma en tiempo de ejecución de binarios (incluyendo binarios del sistema y de usuario) frente al certificado raíz de Apple en lugar de confiar en almacenes de confianza en userland.

  • Impide: el tampering post-instalación de binarios, técnicas de jailbreaking que intentan intercambiar o parchear librerías del sistema o apps de usuario; engañar al sistema reemplazando binarios confiables por contrapartes maliciosas.
  • Detalle del mecanismo:
  • En lugar de confiar en una base de confianza local o un caché de certificados, CoreTrust obtiene o se refiere directamente al root 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.
  • Ata 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 ganar persistencia. Pero cuando el loader del OS o CoreTrust verifica, detecta la mismatch de firma (o entitlements modificados) y se niega a ejecutar.

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

Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP hace cumplir que las páginas marcadas como escribibles (para datos) sean no-executables, y las páginas marcadas como ejecutables sean no-escribibles. No puedes simplemente escribir shellcode en una región de heap o stack y ejecutarlo.

  • Impide: la ejecución directa de shellcode; el clásico buffer-overflow → salto al shellcode inyectado.
  • Detalle 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 escribible como executable desencadena una verificación del sistema (y se prohíbe o requiere aprobación de code-sign).
  • En muchos casos, hacer que las páginas sean ejecutables requiere pasar por APIs del OS que imponen restricciones o chequeos adicionales.
Ejemplo Un overflow escribe shellcode en el heap. El atacante intenta `mprotect(heap_addr, size, PROT_EXEC)` para volverla ejecutable. Pero el sistema se niega o valida que la nueva página debe pasar restricciones de code-sign (que el shellcode no cumple).

4. Address Space Layout Randomization (ASLR)

Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR aleatoriza las direcciones base de regiones clave de memoria: librerías, heap, stack, etc., en cada lanzamiento del proceso. Las direcciones de gadgets se mueven entre ejecuciones.

  • Impide: hardcodear direcciones de gadgets para ROP/JOP; exploit chains estáticos; saltos a offsets conocidos.
  • Detalle del mecanismo:
  • Cada librería / módulo dinámico cargado se rebasa a un offset aleatorio.
  • Los punteros base del 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, fuerza al atacante a primero leakear una dirección o puntero para descubrir las direcciones base en tiempo de ejecución.
Ejemplo Un ROP chain espera un gadget en `0x….lib + offset`. Pero como `lib` se reubica de forma diferente en cada ejecución, el chain hardcodeado falla. Un exploit debe primero leakear la dirección base del módulo antes de calcular direcciones de gadgets.

5. Kernel Address Space Layout Randomization (KASLR)

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

  • Impide: exploits a nivel kernel que dependen de ubicaciones fijas de código o datos del kernel; exploits kernel estáticos.
  • Detalle del mecanismo:
  • En cada boot, la dirección base del kernel se aleatoriza (dentro de un rango).
  • Estructuras de datos del kernel (como task_structs, vm_map, etc.) también pueden ser reubicadas o desplazadas.
  • Los atacantes deben primero leakear punteros del kernel o usar vulnerabilidades de divulgación de información para calcular offsets antes de secuestrar estructuras o código del kernel.
Ejemplo Una vulnerabilidad local apunta a corrupir un function pointer del kernel (p.ej. en un `vtable`) en `KERN_BASE + offset`. Pero como `KERN_BASE` es desconocido, el atacante debe leakearlo primero (p.ej. vía un read primitive) antes de calcular la dirección correcta para la corrupción.

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) monitoriza continuamente la integridad de las páginas de kernel text (vía hash o checksum). Si detecta tampering (parches, hooks inline, modificaciones de código) fuera de ventanas permitidas, dispara un kernel panic o reinicio.

  • Impide: parcheo persistente del kernel (modificar instrucciones del kernel), inline hooks, sobrescrituras estáticas de funciones.
  • Detalle del mecanismo:
  • Un módulo hardware o firmware monitoriza la región de kernel text.
  • Periódica o bajo demanda rehasea las páginas y compara con valores esperados.
  • Si hay mismatch fuera de ventanas benignas de actualización, hace panic al dispositivo (para evitar persistencia maliciosa).
  • Los atacantes deben evitar 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 la hash de la página de código ya no coincide con el valor esperado y provoca un kernel panic, bloqueando el dispositivo antes de que el parche se estabilice.

7. Kernel Text Read‐Only Region (KTRR)

Introduced in modern SoCs (post ~A12 / newer hardware) KTRR es un mecanismo aplicado por hardware: 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 a páginas de código.

  • Impide: cualquier modificación al código del kernel después del boot (p.ej. parcheo, in-place code injection) en el nivel de privilegio EL1.
  • Detalle del mecanismo:
  • Durante el boot (en etapa 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 obtiene privilegios completos del kernel, no puede escribir en esas páginas para parchear instrucciones.
  • Para modificarlas, el atacante debe comprometer 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 porque las páginas están bloqueadas como read-only por KTRR, la escritura falla (o provoca fault), por lo que no se aplican parches.

8. Pointer Authentication Codes (PAC)

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

  • PAC es una característica de hardware introducida en ARMv8.3-A para detectar la manipulación de valores de puntero (return addresses, function pointers, ciertos data pointers) al incrustar 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. stack pointer o algún dato distintivo). De ese modo el mismo valor de puntero en contextos distintos obtiene un PAC diferente.
  • En tiempo de uso, antes de desreferenciar o hacer branch vía ese puntero, una instrucción de authenticate comprueba el PAC. Si es válido, el PAC se elimina y se obtiene el puntero puro; si es inválido, el puntero queda “poisoned” (o se lanza un fault).
  • Las claves usadas para producir/validar PAC residen en registros privilegiados (EL1, kernel) y no son accesibles desde user mode.
  • Dado que no se usan todos los 64 bits de un puntero en muchos sistemas (p.ej. espacio de direcciones de 48-bit), los bits altos son “espacio libre” que pueden contener el PAC sin alterar la dirección efectiva.

Architectural Basis & Key Types

  • ARMv8.3 introduce cinco claves de 128-bit (cada una implementada vía dos registros de sistema 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-puntero u otros usos genéricos

  • Estas claves se almacenan en registros de 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 tweak Si el PAC resultante coincide con lo almacenado en los bits altos del puntero, la autenticación tiene éxito.

Instruction Families

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

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

Dominios / sufijos:

MnemónicoSignificado / DominioClave / DominioEjemplo de uso en Assembly
PACIAFirmar instruction pointer con APIAKey“I, A”PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1
PACIBFirmar instruction pointer con APIBKey“I, B”PACIB X2, X3
PACDAFirmar data pointer con APDAKey“D, A”PACDA X4, X5
PACDBFirmar data pointer con APDBKey“D, B”PACDB X6, X7
PACG / PACGAGenérico (no-puntero) firmando con APGAKey“G”PACGA X8, X9, X10 (sign X9 with modifier X10 into X8)
AUTIAAutenticar instruction pointer firmado con APIA & quitar PAC“I, A”AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip
AUTIBAutenticar dominio APIB“I, B”AUTIB X2, X3
AUTDAAutenticar data pointer firmado con APDA“D, A”AUTDA X4, X5
AUTDBAutenticar data pointer firmado con APDB“D, B”AUTDB X6, X7
AUTGAAutenticar genérico / blob (APGA)“G”AUTGA X8, X9, X10 (validate generic)
XPACIQuitar PAC (instruction pointer, sin validación)“I”XPACI X0 — remove PAC from X0 (instruction domain)
XPACDQuitar PAC (data pointer, sin validación)“D”XPACD X4 — remove PAC from data pointer in X4

Hay formas especializadas / alias:

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

Modifiers

El objetivo principal del modifier es vincular el PAC a un contexto específico de modo que la misma dirección firmada en diferentes 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. Opciones típicas: el stack pointer (SP), un frame pointer, o algún ID de objeto.
  • Usar SP como modifier es común para firmar return addresses: el PAC queda ligado al frame de stack específico. Si intentas reutilizar LR en un frame distinto, el modifier cambia, por lo que la validación PAC falla.
  • El mismo valor de puntero firmado bajo modifiers distintos da 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.

Apple / iOS / XNU Customizations & Observations

  • La implementación de PAC de Apple incluye diversificadores por boot para que las claves o tweaks cambien en cada arranque, impidiendo la reutilización entre boots.
  • También incluyen mitigaciones cross-domain para que PACs firmados en user mode no se reutilicen fácilmente en kernel mode, etc.
  • En Apple M1 / Apple Silicon, la 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: firma de return addresses, integridad de punteros en datos del kernel, contexts de threads firmados, etc.
  • Google Project Zero mostró cómo, con un poderoso primitive de lectura/escritura en kernel, uno podría forjar kernel PACs (para A keys) en dispositivos de la era A12, pero Apple parcheó muchas de esas rutas.
  • En el sistema de Apple, algunas claves son globales al kernel, mientras que procesos de usuario pueden obtener randomness por proceso en las claves.

PAC Bypasses

  1. Kernel-mode PAC: theoretical vs real bypasses
  • Dado 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’s 2020 "iOS Kernel PAC, One Year Later" reporta que en iOS 12-13 encontró algunos bypasses parciales (signing gadgets, reutilización 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 (cambio de dominio, bits de habilitación 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 suelen depender de gadgets muy específicos o bugs de implementación; no son soluciones de propósito general.

Por tanto, el kernel PAC se considera altamente robusto, aunque no perfecto.

  1. User-mode / runtime PAC bypass techniques

Estas son más comunes, y explotan imperfecciones en cómo PAC se aplica o se usa en dynamic linking / runtime frameworks. A continuación se describen clases, con ejemplos.

2.1 Shared Cache / A key issues

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

  • Algunas técnicas de bypass intentan extraer o reutilizar punteros firmados con A-key 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, importaciones de function pointers desde librerías compartidas en userspace se encontraron insuficientemente protegidas por PAC, permitiendo a un atacante obtener function pointers sin cambiar su firma. (entrada de bug de Project Zero) bugs.chromium.org

2.2 dlsym(3) / dynamic symbol resolution

  • Un bypass conocido es llamar a dlsym() para obtener un function pointer ya firmado (firmado con A-key, diversifier cero) y luego usarlo. Dado que 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 a dlsym("someSym") produce un puntero firmado y puede usarse para llamadas indirectas. blog.epsilon-sec.com

  • "iOS 18.4 --- dlsym considered harmful" de Synacktiv describe un bug: algunos símbolos resueltos vía dlsym en iOS 18.4 devuelven punteros que están incorrectamente firmados (o con diversificadores buggeados), habilitando un bypass involuntario 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

Así, dlsym es un vector frecuente en bypasses de PAC en user-mode.

2.3 Other DYLD / runtime relocations

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

  • Las páginas 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 chains de exploit de WebKit, el loader DYLD suele ser objetivo para bypasses de PAC. Las slides mencionan que muchos bypasses han atacado el DYLD loader (vía relocación, interposer hooks). Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • En chains de exploit en userland, métodos del runtime Objective-C como NSPredicate, NSExpression o NSInvocation se usan para introducir llamadas de control sin una aparente falsificación de punteros.

  • En iOS antiguo (antes de PAC), un exploit usó fake NSInvocation objects para llamar selectores arbitrarios sobre memoria controlada. Con PAC se requieren modificaciones. Pero la técnica SLOP (SeLector Oriented Programming) se extendió bajo PAC también. Project Zero

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

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

Ejemplo Flow

Ejemplo de firma 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>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)**
**Introduced with ARMv8.5 (later hardware)**
BTI is a hardware feature that checks **indirect branch targets**: when executing `blr` or indirect calls/jumps, the target must begin with a **BTI landing pad** (`BTI j` or `BTI c`). Jumping into gadget addresses that lack the landing pad triggers an exception.

LLVM’s implementation notes three variants of BTI instructions and how they map to branch types.

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

- In code compiled with branch target enforcement, compilers insert a BTI instruction (C, J, or JC) at each valid indirect-branch target (function beginnings or blocks reachable by jumps) so that indirect branches only succeed to those places.
- **Direct branches / calls** (i.e. fixed-address `B`, `BL`) are **not restricted** by BTI. The assumption is that code pages are trusted and attacker cannot change them (so direct branches are safe).
- Also, **RET / return** instructions generally are not restricted by BTI because return addresses are protected via PAC or return signing mechanisms.

#### Mechanism and enforcement

- When the CPU decodes an **indirect branch (BLR / BR)** in a page marked as “guarded / BTI-enabled,” it checks whether the target address’s first instruction is a valid BTI (C, J, or JC as allowed). If not, a **Branch Target Exception** occurs.
- The BTI instruction encoding is designed to reuse opcodes previously reserved for NOPs (in earlier ARM versions). So BTI-enabled binaries remain backward-compatible: on hardware without BTI support, those instructions act as NOPs.
- The compiler passes that add BTIs insert them only where needed: functions that may be called indirectly, or basic blocks targeted by jumps.
- Some patches and LLVM code show that BTI is not inserted for *all* basic blocks — only those that are potential branch targets (e.g. from switch / jump tables).

#### BTI + PAC synergy

PAC protects the pointer value (the source) — ensures the chain of indirect calls / returns hasn't been tampered with.

BTI ensures that even a valid pointer must only target properly marked entry points.

Combined, an attacker needs both a valid pointer with correct PAC and that the target must have a BTI placed there. This increases the hardness of constructing exploit gadgets.

#### 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)**
**Introduced in more recent ARMv8 extensions / iOS support (for hardened kernel)**

#### PAN (Privileged Access Never)

- **PAN** is a feature introduced in **ARMv8.1-A** that prevents **privileged code** (EL1 or EL2) from **reading or writing** memory that is marked as **user-accessible (EL0)**, unless PAN is explicitly disabled.
- The idea: even if the kernel is tricked or compromised, it cannot arbitrarily dereference user-space pointers without first *clearing* PAN, thus reducing risks of **`ret2usr`** style exploits or misuse of user-controlled buffers.
- When PAN is enabled (PSTATE.PAN = 1), any privileged load/store instruction accessing a virtual address that is “accessible at EL0” triggers a **permission fault**.
- The kernel, when it must legitimately access user-space memory (e.g. copy data to/from user buffers), must **temporarily disable PAN** (or switch to “unprivileged load/store” instructions) to allow that access.
- In Linux on ARM64, PAN support was introduced circa 2015: kernel patches added detection of the feature, and replaced `get_user` / `put_user` etc. with variants that clear PAN around user memory accesses.

**Key nuance / limitation / bug**
- As noted by Siguza and others, a specification bug (or ambiguous behavior) in ARM’s design means that **execute-only user mappings** (`--x`) may **not trigger PAN**. In other words, if a user page is marked executable but without read permission, the kernel’s read attempt might bypass PAN because the architecture considers “accessible at EL0” to require readable permission, not just executable. This leads to a PAN bypass in certain configurations.
- Because of that, if iOS / XNU allows execute-only user pages (as some JIT or code-cache setups might), the kernel might accidentally read from them even with PAN enabled. This is a known subtle exploitable area in some ARMv8+ systems.

#### PXN (Privileged eXecute Never)

- **PXN** is a page table flag (in the page table entries, leaf or block entries) that indicates that the page is **non-executable when running in privileged mode** (i.e. when EL1 executes it).
- PXN prevents the kernel (or any privileged code) from jumping into or executing instructions from user-space pages even if control is diverted. In effect, it stops a kernel-level control-flow redirection into user memory.
- Combined with PAN, this ensures that:
1. Kernel cannot (by default) read or write user-space data (PAN)
2. Kernel cannot execute user-space code (PXN)
- In the ARMv8 page table format, the leaf entries have a `PXN` bit (and also `UXN` for unprivileged execute-never) in their attribute bits.

So even if the kernel has a corrupted function pointer pointing to user memory, and it tried to branch there, the PXN bit would cause a fault.

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

To understand how PAN / PXN work, you need to see how ARM’s translation and permission model works (simplified):

- Each page or block entry has attribute fields including **AP[2:1]** for access permissions (read/write, privileged vs unprivileged) and **UXN / PXN** bits for execute-never restrictions.
- When PSTATE.PAN is 1 (enabled), the hardware enforces modified semantics: privileged accesses to pages marked as “accessible by EL0” (i.e. user-accessible) are disallowed (fault).
- Because of the bug mentioned, pages that are marked only executable (no read permission) may not count as “accessible by EL0” under certain implementations, thus bypassing PAN.
- When a page’s PXN bit is set, even if the instruction fetch comes from a higher privilege level, execution is prohibited.

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

In a hardened kernel design (such as what Apple might use):

- The kernel enables PAN by default (so privileged code is constrained).
- In pathways that legitimately need to read or write user buffers (e.g. syscall buffer copy, I/O, read/write user pointer), the kernel temporarily **disables PAN** or uses special instructions to override.
- After finishing user data access, it must re-enable PAN.
- PXN is enforced via page tables: user pages have PXN = 1 (so kernel cannot execute them), kernel pages do not have PXN (so kernel code can execute).
- The kernel must ensure that no code paths cause execution flow into user memory regions (that would bypass PXN) — so exploit chains relying on “jump into user-controlled shellcode” are blocked.

Because of the noted PAN bypass via execute-only pages, in a real system, Apple might disable or disallow execute-only user pages, or patch around the specification weakness.


#### Attack surfaces, bypasses, and mitigations

- **PAN bypass via execute-only pages**: as discussed, the spec allows a gap: user pages with execute-only (no read perm) might not count as “accessible at EL0,” so PAN won’t block kernel reads from such pages under some implementations. This gives the attacker an unusual path to feed data via “execute-only” sections.
- **Temporal window exploit**: if the kernel disables PAN for a window longer than necessary, a race or malicious path might exploit that window to perform unintended user memory access.
- **Forgotten re-enable**: if code paths fail to re-enable PAN, subsequent kernel operations might incorrectly access user memory.
- **Misconfiguration of PXN**: if page tables do not set PXN on user pages or incorrectly map user code pages, the kernel might be tricked into executing user-space code.
- **Speculation / side-channels**: analogous to speculative bypasses, there may be microarchitectural side-effects that cause transient violation of PAN / PXN checks (though such attacks are highly dependent on CPU design).
- **Complex interactions**: In more advanced features (e.g. JIT, shared memory, just-in-time code regions), the kernel may need fine-grained control to permit certain memory accesses or execution in user-mapped regions; designing those safely under PAN/PXN constraints is nontrivial.


#### 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.
<div class="codeblock_filename_container"><span class="codeblock_filename_inner hljs">  </span></div>

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

Si el kernel **no** hubiera puesto PXN en esa página de usuario, entonces la rama podría ejecutarse — lo cual sería inseguro.

Si el kernel olvida volver a habilitar PAN después de acceder a memoria de usuario, se abre una ventana en la que la lógica posterior del kernel podría accidentalmente leer/escribir memoria de usuario arbitraria.

Si el puntero de usuario apunta a una página de solo ejecución (página de usuario con solo permiso de ejecución, sin lectura/escritura), bajo el bug de la especificación PAN, `ldr W2, [X1]` podría **no** provocar fault incluso con PAN habilitado, permitiendo un bypass exploit, dependiendo de la implementación.

</details>

<details>
<summary>Ejemplo</summary>
Una vulnerabilidad del kernel intenta tomar un puntero a función proporcionado por el usuario y llamarlo en contexto del kernel (es decir, `call user_buffer`). Bajo PAN/PXN, esa operación está deshabilitada o provoca un fault.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI significa que el byte superior (más significativo) de un puntero de 64 bits se ignora durante la traducción de direcciones. Esto permite al OS o al hardware insertar bits de tag en el byte superior del puntero sin afectar la dirección real.

- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). Es una característica de hardware (disponible en muchas implementaciones ARMv8+) que **ignora los 8 bits superiores** (bits 63:56) de un puntero de 64 bits al realizar **traducción de direcciones / load/store / instruction fetch**.
- En efecto, la CPU trata un puntero `0xTTxxxx_xxxx_xxxx` (donde `TT` = top byte) como `0x00xxxx_xxxx_xxxx` para efectos de traducción de direcciones, ignorando (enmascarando) el byte superior. El byte superior puede ser usado por software para almacenar **metadata / tag bits**.
- Esto da al software espacio “gratuito” en banda para incrustar un byte de tag en cada puntero sin alterar la ubicación de memoria a la que apunta.
- La arquitectura garantiza que las cargas, stores y fetch de instrucciones traten el puntero con su byte superior enmascarado (es decir, tag eliminado) antes de realizar el acceso real a memoria.

Así TBI desacopla el **puntero lógico** (puntero + tag) de la **dirección física** usada para operaciones de memoria.

#### Why TBI: Use cases and motivation

- **Pointer tagging / metadata**: Puedes almacenar metadata adicional (p. ej. tipo de objeto, versión, límites, etiquetas de integridad) en ese byte superior. Cuando más tarde usas el puntero, el tag se ignora a nivel de hardware, por lo que no necesitas limpiarlo manualmente para el acceso a memoria.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI es el mecanismo de hardware base sobre el que se construye MTE. En ARMv8.5, la **Memory Tagging Extension** usa los bits 59:56 del puntero como un **tag lógico** y lo compara con un **allocation tag** almacenado en memoria.
- **Enhanced security & integrity**: Combinando TBI con pointer authentication (PAC) o comprobaciones en tiempo de ejecución, puedes forzar no solo que el valor del puntero sea correcto sino también que el tag lo sea. Un atacante que sobrescriba un puntero sin el tag correcto producirá un tag no coincidente.
- **Compatibility**: Como TBI es opcional y los bits de tag son ignorados por hardware, el código existente sin tags continúa funcionando normalmente. Los bits de tag se vuelven efectivamente “bits irrelevantes” para código legacy.

#### Example
<details>
<summary>Ejemplo</summary>
Un puntero a función incluía un tag en su byte superior (por ejemplo `0xAA`). Un exploit sobrescribe los bits bajos del puntero pero olvida el tag, de modo que cuando el kernel valida o sanea, el puntero falla o es rechazado.
</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 está diseñado como un **límite de protección intra-kernel**: incluso si el kernel (EL1) está comprometido y tiene capacidades de lectura/escritura, **no debería poder modificar libremente** ciertas páginas sensibles (especialmente tablas de páginas, metadatos de firma de código, páginas de código del kernel, entitlements, trust caches, etc.).
- Efectivamente crea un **“kernel dentro del kernel”** — un componente más pequeño y de confianza (PPL) con **privilegios elevados** que solo él puede modificar páginas protegidas. Otro código del kernel debe llamar a rutinas del PPL para efectuar cambios.
- Esto reduce la superficie de ataque para exploits de kernel: incluso con R/W/execute arbitrario en modo kernel, el código exploit debe además entrar en el dominio PPL (o saltarse PPL) para modificar estructuras críticas.
- En hardware Apple más reciente (A15+ / M2+), Apple está migrando hacia **SPTM (Secure Page Table Monitor)**, que en muchos casos reemplaza a PPL para la protección de page-tables en esas plataformas.

Aquí se describe cómo se cree que opera PPL, basado en análisis públicos:

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

- El hardware de Apple usa un mecanismo llamado **APRR (Access Permission ReRouting)**, que permite que las entradas de tabla de páginas (PTEs) contengan pequeños índices, en lugar de bits de permiso completos. Esos índices se mapean mediante registros APRR a permisos efectivos. Esto permite remapeos dinámicos de permisos por dominio.
- PPL aprovecha APRR para segregar privilegios dentro del contexto del kernel: solo el dominio PPL tiene permiso para actualizar la correspondencia entre índices y permisos efectivos. Es decir, cuando código no-PPL del kernel escribe una PTE o intenta cambiar bits de permiso, la lógica APRR lo impide (o aplica un mapeo de solo lectura).
- El código PPL corre en una región restringida (p. ej. `__PPLTEXT`) que normalmente no es ejecutable o no es escribible hasta que puertas de entrada temporales lo permiten. El kernel llama a puntos de entrada PPL (“PPL routines”) para realizar operaciones sensibles.

#### Gate / Entry & Exit

- Cuando el kernel necesita modificar una página protegida (p. ej. cambiar permisos de una página de código del kernel, o modificar page tables), llama a una rutina wrapper de PPL, que hace validaciones y luego transiciona al dominio PPL. Fuera de ese dominio, las páginas protegidas son efectivamente de solo lectura o no modificables por el kernel principal.
- Durante la entrada a PPL, los mapeos APRR se ajustan para que las páginas en la región PPL se establezcan como **ejecutables y escribibles** dentro de PPL. Al salir, se devuelven a solo lectura / no escribibles. Esto asegura que solo las rutinas PPL auditadas puedan escribir en páginas protegidas.
- Fuera de PPL, intentos del código del kernel por escribir en esas páginas protegidas producirán un fault (permiso denegado) porque el mapeo APRR para ese dominio de código no permite escritura.

#### Protected page categories

Las páginas que PPL típicamente protege incluyen:

- Estructuras de tabla de páginas (entradas de tabla de traducción, metadatos de mapeo)
- Páginas de código del kernel, especialmente aquellas que contienen lógica crítica
- Metadatos de code-sign (trust caches, blobs de firma)
- Tablas de entitlements, tablas de enforcement de firma
- Otras estructuras de kernel de alto valor donde un parche permitiría evadir checks de firma o manipular credenciales

La idea es que incluso si la memoria del kernel está completamente controlada, el atacante no pueda simplemente parchear o reescribir estas páginas, a menos que también comprometa rutinas PPL o las eluda.


#### Known Bypasses & Vulnerabilities

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

- Un writeup público de Project Zero describe un bypass que involucra **stale TLB entries**.
- La 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.

- Este exploit requería control fino del mapeo físico y del comportamiento del TLB. Demuestra que un límite de seguridad que depende de la corrección del TLB / de los mapeos debe ser extremadamente cuidadoso con las invalidaciones de TLB y la consistencia de mapeos.

- Project Zero comentó que bypasses como este son sutiles y raros, pero posibles en sistemas complejos. Aun así, consideran a PPL como una mitigación sólida.

2. **Other potential hazards & constraints**

- Si un exploit de kernel puede entrar directamente en rutinas PPL (mediante llamadas a los wrappers PPL), podría eludir las restricciones. Por tanto, la validación de argumentos es crítica.
- Bugs en el código PPL mismo (p. ej. overflow aritmético, comprobaciones de límites) pueden permitir modificaciones fuera de rango dentro de PPL. Project Zero observó que un bug en `pmap_remove_options_internal()` fue explotado en su bypass.
- El límite PPL está irrevocablemente atado a la aplicación por hardware (APRR, memory controller), por lo que solo es tan fuerte como la implementación hardware.

#### Example
<details>
<summary>Ejemplo de código</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
<div class="codeblock_filename_container"><span class="codeblock_filename_inner hljs">c</span></div>

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

The kernel can do many normal operations, but only through ppl_call_* routines can it change protected mappings or patch code.

Ejemplo Un exploit del kernel intenta sobrescribir la entitlement table, o desactivar la aplicación de code-sign modificando un kernel signature blob. Debido a que esa página está PPL-protected, la escritura se bloquea a menos que se haga 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 datos de credenciales arbitrariamente. En iOS 17+ ciertos dispositivos usan SPTM para aislar aún más las páginas gestionadas por PPL.

PPL → SPTM / Reemplazos / Futuro

  • 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

Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:

  1. Tag assignment
  • When memory is allocated (e.g. in kernel or user space via secure allocators), a secret tag is assigned to that block.
  • The pointer returned to the user or kernel includes that tag in its high bits (using TBI / top byte ignore mechanisms).
  1. Tag checking on access
  • Whenever a load or store is executed using a pointer, the hardware checks that the pointer’s tag matches the memory block’s tag (allocation tag). If mismatch, it faults immediately (since synchronous).
  • Because it's synchronous, there is no “delayed detection” window.
  1. Retagging on free / reuse
  • When memory is freed, the allocator changes the block’s tag (so older pointers with old tags no longer match).
  • A use-after-free pointer would therefore have a stale tag and mismatch when accessed.
  1. Neighbor-tag differentiation to catch overflows
  • Adjacent allocations are given distinct tags. If a buffer overflow spills into neighbor’s memory, tag mismatch causes a fault.
  • This is especially powerful in catching small overflows that cross boundary.
  1. Tag confidentiality enforcement
  • Apple must prevent tag values being leaked (because if attacker learns the tag, they could craft pointers with correct tags).
  • They include protections (microarchitectural / speculative controls) to avoid side-channel leakage of tag bits.
  1. Kernel and user-space integration
  • Apple uses EMTE not just in user-space but also in kernel / OS-critical components (to guard kernel against memory corruption).
  • The hardware/OS ensures tag rules apply even when kernel is executing on behalf of 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 y desafíos

- **Intrablock overflows**: Si el overflow permanece dentro de la misma allocation (no cruza el límite) y la tag se mantiene igual, el tag mismatch no lo detecta.
- **Tag width limitation**: Solo hay unos pocos bits (p. ej. 4 bits, o un dominio pequeño) disponibles para la tag — espacio de nombres limitado.
- **Side-channel leaks**: Si los tag bits pueden ser leak (vía cache / speculative execution), un atacante puede aprender tags válidos y eludir la protección. La enforcement de Tag Confidentiality de Apple está pensada para mitigar esto.
- **Performance overhead**: Las comprobaciones de tag en cada load/store añaden coste; Apple debe optimizar el hardware para minimizar la overhead.
- **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 complica la interoperabilidad.
- **Speculative / transient attacks**: Como con muchas protecciones microarquitectónicas, la speculative execution o fusiones de micro-op pueden eludir comprobaciones de forma transitoria o leak tag bits.
- **Limited to supported regions**: Apple podría aplicar EMTE solo en regiones selectivas y de alto riesgo (kernel, subsistemas críticos para seguridad), no de forma universal.

---

## Mejoras clave / diferencias respecto al MTE estándar

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

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Modo de comprobación** | Soporta modos síncrono y asíncrono. En async, los tag mismatches se reportan más tarde (retrasados) | Apple insiste en el **modo síncrono** por defecto — los tag mismatches se detectan inmediatamente, no se permiten ventanas de delay/race.|
| **Cobertura de memoria no etiquetada** | Los accesos a memoria no etiquetada (p. ej. globals) pueden eludir comprobaciones en algunas implementaciones | EMTE requiere que los accesos desde una región tagged a memoria no etiquetada también validen el conocimiento del tag, haciendo más difícil eludirlo mezclando allocations.|
| **Tag confidentiality / secrecy** | Los tags podrían ser observables o leak vía side channels | Apple añade **Tag Confidentiality Enforcement**, que intenta prevenir el leak de valores de tag (vía speculative side-channels, etc.).|
| **Integración del allocator & retagging** | MTE deja gran parte de la lógica del allocator al software | Los secure typed allocators de Apple (kalloc_type, xzone malloc, etc.) se integran con EMTE: cuando la memoria se allocate o free, los tags se gestionan con granularidad fina.|
| **Siempre activado por defecto** | En muchas plataformas, MTE es opcional o está apagado por defecto | Apple habilita EMTE / MIE por defecto en hardware soportado (p. ej. iPhone 17 / A19) para el kernel y muchos procesos de usuario.|

Debido a que Apple controla tanto el hardware como la pila de software, puede aplicar EMTE de forma estricta, evitar problemas de rendimiento y cerrar agujeros 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. **Asignación de tag**
- Cuando se allocate memoria (p. ej. en el kernel o en user space vía secure allocators), se asigna una **secret tag** a ese bloque.
- El pointer devuelto al usuario o al kernel incluye esa tag en sus bits superiores (usando TBI / top byte ignore mechanisms).

2. **Comprobación de tag en acceso**
- Siempre que se ejecuta un load o store usando un pointer, el hardware verifica que la tag del pointer coincida con la 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 “detección retrasada”.

3. **Retagging al free / reuse**
- Cuando la memoria se libera, el allocator cambia la tag del bloque (por lo que punteros antiguos con tags viejas ya no coinciden).
- Un puntero use-after-free tendrá por tanto una tag obsoleta y causará mismatch al acceder.

4. **Diferenciación de tags entre vecinos para detectar overflows**
- A las allocations adyacentes se les dan tags distintas. Si un buffer overflow se desborda hacia la memoria del vecino, el tag mismatch provoca un fault.
- Esto es especialmente efectivo para capturar pequeños overflows que atraviesan límites.

5. **Tag confidentiality enforcement**
- Apple debe prevenir que los valores de tag sean leaked (porque si un atacante aprende la tag, podría construir pointers con tags correctas).
- Incluyen protecciones (controles microarquitectónicos / speculative) para evitar side-channel leakage de tag bits.

6. **Integración en kernel y user-space**
- Apple usa EMTE no solo en user-space sino también en el kernel / componentes críticos del 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 user space.

Como EMTE está integrado en MIE, Apple emplea EMTE en modo síncrono a través de las superficies de ataque clave, no como una opción de depuración o activación opcional.

---

## Manejo de excepciones en XNU

Cuando ocurre una **exception** (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 **signal** estilo UNIX (como `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).

Este proceso implica múltiples capas de propagación y manejo de excepciones antes de llegar al user space o de ser convertido en una señal BSD.

### Flujo de excepciones (visión general)

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

2.  **El trap handler 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 señal BSD** (para procesos de user-space).

-   **Panic** (para excepciones en kernel-space).


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

La función `exception_triage()` enruta las Mach exceptions a lo largo de la cadena de handlers posibles hasta que uno la maneja o hasta que es finalmente fatal. Está definida en `osfmk/kern/exception.c`.
<div class="codeblock_filename_container"><span class="codeblock_filename_inner hljs">c</span></div>

```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

Flujo típico de llamadas:

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

Si todas fallan → gestionado por bsd_exception() → traducido a una señal como SIGSEGV.

Puertos de excepción

Cada objeto Mach (thread, task, host) puede registrar puertos de excepción, donde 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 (Mach port para recibir 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 thread objetivo, normalmente usando task_set_exception_ports().

Cuando ocurre una excepción:

  • El Mach message se envía al proceso debugger.
  • El debugger puede decidir manejar (resume, 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 lanza un Data Abort.

  2. El kernel trap handler llama a exception_triage(EXC_BAD_ACCESS, ...).

  3. Mensaje enviado a:

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

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

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

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

PAC Exceptions

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

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

Si el binario tiene la flag TFRO_PAC_EXC_FATAL, el kernel trata las fallas de PAC como fatales, evitando la interceptación por el debugger. Esto es para impedir que atacantes usen debuggers para evadir las comprobaciones PAC y está habilitado para platform binaries.

Software Breakpoints

Un software breakpoint (int3 en x86, brk en ARM64) se implementa provocando una falla deliberada.
El debugger lo captura vía el exception port:

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

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

Conversion to BSD Signals

Si ningún handler acepta la excepción:

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

  • Esto mapea Mach exceptions a signals:

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 → Core de exception_triage(), exception_deliver_*().

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

  • osfmk/arm64/trap.c → Trap handlers a bajo nivel.

  • osfmk/mach/exc.h → Exception codes 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 allocations de una sola clase de tamaño.

Desde la captura de pantalla:

Zone NameElement SizeExample Use
default.kalloc.1616 bytesEstructuras kernel muy pequeñas, pointers.
default.kalloc.3232 bytesEstructuras pequeñas, headers de objetos.
default.kalloc.6464 bytesIPC messages, buffers kernel diminutos.
default.kalloc.128128 bytesObjetos medianos como partes de OSObject.
default.kalloc.12801280 bytesEstructuras grandes, metadata de IOSurface/graphics.

Cómo funcionaba:

  • Cada request de allocation se redondeaba hacia arriba al tamaño de zone más cercano. (Ej., una request de 50 bytes cae en la zone kalloc.64).
  • La memoria en cada zone se mantenía en una freelist — chunks liberados por el kernel regresaban a esa zone.
  • Si desbordabas un buffer de 64 bytes, sobrescribirías el siguiente objeto en la misma zone.

Por eso el heap spraying / feng shui era tan efectivo: podías predecir los vecinos de un objeto al rociar allocations 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 pointer 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 allocation 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).

  • El freeing empujaba chunks de vuelta:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

Así que la freelist era simplemente una lista enlazada construida dentro de la propia 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

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

  1. Heap overflow hacia un freed chunk adyacente → sobrescribir su puntero “next”.

  2. Use-after-free escribir en un freed object → sobrescribir su puntero “next”.

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

  • El allocator extrae (pops) el chunk corrompido.

  • Sigue el puntero “next” suministrado por el atacante.

  • Devuelve un puntero a memoria arbitraria, habilitando fake object primitives o sobrescritura dirigida.

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.

Este diseño de freelist hizo que la explotación fuera altamente efectiva pre-hardening: vecinos predecibles por heap sprays, enlaces crudos de freelist, y la ausencia de separación por tipo permitía a atacantes escalar bugs de UAF/overflow hasta obtener control arbitrario de la memoria del kernel.

Heap Grooming / Feng Shui

El objetivo del heap grooming es dar forma a la disposición del heap de modo que, cuando un atacante desencadena un overflow o use-after-free, el objeto objetivo (víctima) quede justo al lado de un objeto controlado por el atacante.
De este modo, cuando ocurre la corrupción de memoria, el atacante puede sobrescribir de forma fiable el objeto víctima con datos controlados.

Pasos:

  1. Spray allocations (fill the holes)
  • Con el tiempo, el kernel heap se fragmenta: algunas zonas tienen huecos donde objetos antiguos fueron liberados.
  • El atacante primero hace muchas asignaciones dummy para llenar esos huecos, de modo que el heap se vuelva “empaquetado” y predecible.
  1. Forzar nuevas páginas
  • Una vez que los huecos están llenos, las siguientes asignaciones deben provenir de páginas nuevas agregadas a la zona.
  • Páginas nuevas implican que los objetos estarán agrupados, no dispersos por memoria fragmentada antigua.
  • Esto da al atacante mucho mejor control sobre los vecinos.
  1. Colocar objetos del atacante
  • El atacante vuelve a sprayear, creando muchos objetos controlados por él en esas páginas nuevas.
  • Estos objetos son previsibles en tamaño y ubicación (ya que todos pertenecen a la misma zona).
  1. Liberar un objeto controlado (crear un hueco)
  • El atacante libera deliberadamente uno de sus propios objetos.
  • Esto crea un “hueco” en el heap, que el allocator reutilizará para la próxima asignación de ese tamaño.
  1. El objeto víctima ocupa el hueco
  • El atacante provoca que el kernel asigne el objeto víctima (el que quiere corromper).
  • Dado que el hueco es la primera ranura disponible en la freelist, la víctima se coloca exactamente donde el atacante liberó su objeto.
  1. Overflow / UAF hacia la víctima
  • Ahora el atacante tiene objetos controlados alrededor de la víctima.
  • Al desbordar desde uno de sus objetos (o reusar uno liberado), puede sobrescribir de forma fiable los campos de memoria de la víctima con valores elegidos.

Por qué funciona:

  • Predictibilidad del allocator por zona: asignaciones del mismo tamaño siempre provienen de la misma zona.
  • Comportamiento de la freelist: nuevas asignaciones reutilizan primero el chunk liberado más recientemente.
  • Heap sprays: el atacante llena la memoria con contenido predecible y controla el layout.
  • Resultado final: el atacante controla dónde cae el objeto víctima y qué datos quedan junto a él.

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

Apple endureció el allocator e hizo que el heap grooming sea mucho más difícil:

1. From Classic kalloc to kalloc_type

  • Antes: existía una sola zona kalloc.<size> para cada clase de tamaño (16, 32, 64, … 1280, etc.). Cualquier objeto de ese tamaño se colocaba ahí → los objetos del atacante podían quedar junto a objetos privilegiados del kernel.
  • Ahora:
  • Los objetos del kernel se asignan desde typed zones (kalloc_type).
  • Cada tipo de objeto (por ejemplo, ipc_port_t, task_t, OSString, OSData) tiene su propia zona dedicada, incluso si tienen el mismo tamaño.
  • El mapeo entre tipo de objeto ↔ zona se genera desde el kalloc_type system en tiempo de compilación.

Un atacante ya no puede garantizar que datos controlados (OSData) acaben adyacentes a objetos sensibles del kernel (task_t) del mismo tamaño.

2. Slabs and Per-CPU Caches

  • El heap está dividido en slabs (páginas de memoria cortadas en chunks de tamaño fijo para esa zona).
  • Cada zona tiene una cache por CPU para reducir la contención.
  • Ruta de asignación:
  1. Intentar la cache por CPU.
  2. Si está vacía, sacar de la global freelist.
  3. Si la freelist está vacía, asignar un nuevo slab (una o más páginas).
  • Beneficio: esta descentralización hace que los heap sprays sean menos deterministas, ya que las asignaciones pueden satisfacerse desde caches de distintas CPUs.

3. Randomization inside zones

  • Dentro de una zona, los elementos liberados no se devuelven en simple orden FIFO/LIFO.
  • XNU moderno usa encoded freelist pointers (safe-linking estilo Linux, introducido ~iOS 14).
  • Cada puntero de freelist está codificado con XOR usando una cookie secreta por zona.
  • Esto evita que un atacante forje un puntero de freelist falso si obtiene una primitiva de escritura.
  • Algunas asignaciones se aleatorizan en su colocación dentro de un slab, por lo que el spraying no garantiza la adyacencia.

4. Guarded Allocations

  • Ciertos objetos críticos del kernel (p. ej., credenciales, estructuras de task) se asignan en guarded zones.
  • Estas zonas insertan guard pages (memoria no mapeada) entre slabs o usan redzones alrededor de objetos.
  • Cualquier overflow hacia la guard page provoca un fallo → pánico inmediato en lugar de corrupción silenciosa.

5. Page Protection Layer (PPL) and SPTM

  • Incluso si controlas un objeto liberado, no puedes modificar toda la memoria del kernel:
  • PPL (Page Protection Layer) hace cumplir que ciertas regiones (p. ej., datos de code signing, entitlements) sean readonly incluso para el propio kernel.
  • En dispositivos A15/M2+, este rol es reemplazado/mejorado por SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
  • Estas capas hardware-forzadas significan que los atacantes no pueden escalar desde una sola corrupción de heap a parcheos arbitrarios de estructuras críticas de seguridad.
  • (Added / Enhanced): además, se usa PAC (Pointer Authentication Codes) en el kernel para proteger punteros (especialmente punteros a funciones, vtables) de modo que forjarlos o corromperlos sea más difícil.
  • (Added / Enhanced): las zonas pueden imponer zone_require / zone enforcement, es decir, que un objeto liberado solo pueda volver a través de su zona tipada correcta; frees cross-zone inválidos pueden provocar panic o ser rechazados. (Apple insinúa esto en sus posts sobre memory safety)

6. Large Allocations

  • No todas las asignaciones pasan por kalloc_type.
  • Solicitudes muy grandes (por encima de ~16 KB) evitan las typed zones y se sirven directamente desde kernel VM (kmem) vía asignaciones de páginas.
  • Estas son menos predecibles, pero también menos explotables, ya que no comparten slabs con otros objetos.

7. Allocation Patterns Attackers Target

Aun con estas protecciones, los atacantes siguen buscando:

  • Reference count objects: si puedes manipular contadores retain/release, puedes causar use-after-free.
  • Objects with function pointers (vtables): corromper uno sigue dando control de flujo.
  • Shared memory objects (IOSurface, Mach ports): siguen siendo objetivos porque hacen de puente user ↔ kernel.

Pero — a diferencia de antes — no puedes simplemente sprayear OSData y esperar que quede al lado de un task_t. Necesitas bugs específicos de tipo o info leaks para tener éxito.

Example: Allocation Flow in Modern Heap

Supón que userspace llama a IOKit para asignar un objeto OSData:

  1. Type lookupOSData mapea a la zona kalloc_type_osdata (tamaño 64 bytes).
  2. Comprobar la cache por CPU para elementos libres.
  • Si se encuentra → devolver uno.
  • Si está vacía → ir a la global freelist.
  • Si la freelist está vacía → asignar un nuevo slab (página de 4KB → 64 chunks de 64 bytes).
  1. Devolver el chunk al llamante.

Protección de punteros de freelist:

  • Cada chunk liberado almacena la dirección del siguiente chunk libre, pero codificada con una clave secreta.
  • Sobrescribir ese campo con datos del atacante no funcionará a menos que conozcas la clave.

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)

En versiones recientes de los OS de Apple (especialmente iOS 17+), Apple introdujo un allocator de userland más seguro, xzone malloc (XZM). Este es el análogo en espacio de usuario de kalloc_type del kernel, aplicando awareness por tipo, aislamiento de metadata y salvaguardas de memory tagging.

Goals & Design Principles

  • Type segregation / type awareness: agrupar asignaciones por tipo o uso (puntero vs datos) para prevenir type confusion y reutilización cross-type.
  • Metadata isolation: separar metadata del heap (p. ej., freelists, bits de tamaño/estado) del payload de los objetos para que escrituras OOB afecten menos la metadata.
  • Guard pages / redzones: insertar páginas no mapeadas o padding alrededor de asignaciones para detectar sobreflows.
  • Memory tagging (EMTE / MIE): trabajar junto al tagging hardware para detectar use-after-free, OOB y accesos inválidos.
  • Scalable performance: mantener baja sobrecarga, evitar fragmentación excesiva y soportar muchas asignaciones por segundo con baja latencia.

Architecture & Components

A continuación los elementos principales del allocator xzone:

Segment Groups & Zones

  • Segment groups particionan el espacio de direcciones por categorías de uso: p. ej. data, pointer_xzones, data_large, pointer_large.
  • Cada segment group contiene segments (rangos VM) que alojan asignaciones para esa categoría.
  • Asociado a cada segmento hay una metadata slab (área VM separada) que almacena metadata (p. ej., bits free/used, clases de tamaño) para ese segmento. Esta metadata out-of-line (OOL) asegura que la metadata no esté mezclada con los payloads de objetos, mitigando la corrupción por overflows.
  • Los segments se cortan en chunks (slices) que a su vez se subdividen en blocks (unidades de asignación). Un chunk está ligado a una clase de tamaño y segment group específico (es decir, todos los blocks en un chunk comparten mismo tamaño y categoría).
  • Para asignaciones pequeñas/medias, se usan chunks de tamaño fijo; para grandes/huge, puede mapearse por separado.

Chunks & Blocks

  • Un chunk es una región (a menudo varias páginas) dedicada a asignaciones de una clase de tamaño dentro de un group.
  • Dentro de un chunk, los blocks son ranuras disponibles para asignaciones. Los blocks liberados se rastrean mediante la metadata slab — p. ej., mediante bitmaps o freelists almacenados out-of-line.
  • Entre chunks (o dentro), pueden insertarse guard slices / guard pages (p. ej., slices no mapeadas) para detectar escrituras OOB.

Type / Type ID

  • Cada sitio de asignación (o llamada a malloc, calloc, etc.) se asocia con un type identifier (malloc_type_id_t) que codifica qué tipo de objeto se está asignando. Ese type ID se pasa al allocator, que lo usa para seleccionar qué zona/segment servir la asignación.
  • Debido a esto, aun si dos asignaciones tienen el mismo tamaño, pueden ir a zonas totalmente diferentes si sus tipos difieren.
  • En versiones tempranas de iOS 17, no todas las APIs (p. ej., CFAllocator) eran totalmente type-aware; Apple abordó algunas de esas debilidades en iOS 18.

Allocation & Freeing Workflow

Aquí hay un flujo de alto nivel de cómo operan asignación y liberación en xzone:

  1. malloc / calloc / realloc / typed alloc es invocado con un tamaño y un type ID.
  2. El allocator usa el type ID para elegir el segment group / zone correcta.
  3. Dentro de esa zone/segment, busca un chunk que tenga blocks libres del tamaño solicitado.
  • Puede consultar local caches / per-thread pools o free block lists desde la metadata.
  • Si no hay block libre, puede asignar un nuevo chunk en esa zona.
  1. Se actualiza la metadata slab (bit de libre limpiado, bookkeeping).
  2. Si memory tagging (EMTE) está en juego, el block devuelto recibe una tag, y la metadata se actualiza para reflejar su estado “live”.
  3. Cuando se llama a free():
  • El block se marca como liberado en la metadata (vía OOL slab).
  • El block puede colocarse en una free list o en pool para reutilización.
  • Opcionalmente, el contenido del block puede limpiarse o envenenarse para reducir leaks o explotación de UAF.
  • La tag hardware asociada al block puede invalidarse o retaggearse.
  • Si un chunk entero queda libre (todos los blocks liberados), el allocator puede reclamar ese chunk (desmapearlo o retornarlo al OS) bajo presión de memoria.

Security Features & Hardening

Estas son las defensas integradas en el xzone moderno:

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)

  • MIE (Memory Integrity Enforcement) de Apple es el framework hardware + OS que lleva Enhanced Memory Tagging Extension (EMTE) a modo siempre activo y sincrónico en superficies de ataque principales.
  • El allocator xzone es un pilar fundamental de MIE en user space: las asignaciones hechas vía xzone reciben tags, y los accesos son verificados por hardware.
  • En MIE, el allocator, la asignación de tags, la gestión de metadata y la enforcement de confidencialidad de tags están integrados para asegurar que errores de memoria (p. ej., lecturas obsoletas, OOB, UAF) se detecten inmediatamente, no que se exploten más tarde.

Si quieres, también puedo generar una cheat-sheet 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 isntall 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.

iMessage/Media Parser Zero-Click Chains

Imessage Media Parser Zero Click Coreaudio Pac Bypass

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