Skip to content

AppleAVE2Driver exploit (CVE-2019-8795)

AppleAVE2Driver'ın session-settings path'indeki bir loop counter üzerinde eksik bounds validation, attacker'ın kontrol edilen bir buffer'ı statsMapBufferArray'in sonunun ötesine kadar yürümesine, bir virtual method call ve nihayetinde kernel read/write elde etmesine olanak tanır.

Mechanism

Note

AppleAVE2Driver, yalnızca iOS cihazlarda bulunan bir IOKit video-encoder driver'ıdır. _SetSessionSettings external method'u, userland'den gelen büyük bir bloğu memmove(&client->sessionSettings_block1, userBuf, 0x630) ile doğrudan bir AVEClient kernel objesine kopyalar. İkinci bir field olan memoryInfoCnt2, AVE_DestroyContext içindeki bir loop'un statsMapBufferArray girdilerini temizlerken kaç iterasyon çalıştıracağını kontrol eder. Driver memoryInfoCnt1'i doğrular (max 4) ama memoryInfoCnt2 üzerinde hiç validation yapmaz, dolayısıyla efektif loop bound'u max(memoryInfoCnt1 + 2, memoryInfoCnt2) olur. Kontrol edilen sessionSettings_block1 kopyası struct layout'unda statsMapBufferArray'in hemen ardına düştüğü için, out-of-bounds iterasyonlar attacker'ın kontrol ettiği data'ya index'lenir ve her elemanı sahte bir IOSurfaceBufferMngr* pointer'ına dönüştürür. Cleanup sırasında ~IOSurfaceBufferMngr, offset +0x58'i dereference eder, +0x0'da başka bir pointer yükler, sonra +0x28'deki pointer'ı çağırır — kernel içinde program-counter kontrolü veren, tamamen kontrol edilen bir virtual-dispatch zinciri.

Yardımcı bir bug olan AppleSPUProfileDriver içindeki CVE-2019-8794, userspace'e uninitialized kernel stack byte'ları sızdırır ve overwrite'ı tetiklemeden önce gereken KASLR slide'ı sağlar.

Walkthrough

Exploit, başlangıçta araştırmacı 08Tc3wBB tarafından TyphoonPwn'da gösterilmiş ve sonradan Google Project Zero tarafından analiz edilmiştir; üç primitive'i zincirler:

Step 1 — KASLR Leak (CVE-2019-8794)

AppleSPUProfileDriver, padding'i sıfırlamadan bir userland buffer'ını kernel stack data ile dolduran bir IOKit external method açar. O byte'ları okumak bir kernel pointer'ı açığa çıkarır ve slide'ı ifşa eder.

// Open AppleSPUProfileDriver userclient, call external method index N
// Returned buffer contains uninitialized stack bytes including a kernel ptr
uint64_t kernel_ptr = leaked_buf[OFFSET];
uint64_t kaslr_slide = kernel_ptr - KNOWN_SYMBOL_UNSLID;

Step 2 — IOSurface Properties ile Heap Spray

Bug'ı tetiklemeden önce attacker, IOSurface property-set çağrıları üzerinden birçok OSData blob'u allocate eder. Her blob, sahte bir task port yapısı olarak craft edilir (sonraki pid_for_task() zinciri için):

// spray N fake port objects of known size into kalloc zones
for (int i = 0; i < SPRAY_COUNT; i++) {
    IOSurfaceSetValue(surf, key[i], fake_port_data, sizeof(fake_port_data));
}

Step 3 — OOB Write Trigger (CVE-2019-8795)

AppleAVE2Driver userclient'ını aç, _SetSessionSettings'i şu değerlerle çağır: - memoryInfoCnt1 = 0 (kontrol edilen guard'ı geçer) - memoryInfoCnt2 = LARGE_VALUE (kontrolsüz; loop'u array sınırlarının ötesine sürer) - sessionSettings_block1 bölgesi craft edilmiş IOSurfaceBufferMngr pointer'larıyla doldurulur

// Pseudo-layout inside AVEClient struct:
// [statsMapBufferArray: 6 * ptr_size]
// [sessionSettings_block1: 0x630 bytes]  <-- memmove target
//
// With memoryInfoCnt2 = 8, loop iterates indices 0..7,
// entries 6 and 7 land in sessionSettings_block1 — attacker controlled.
struct SessionSettings ss = { 0 };
ss.memoryInfoCnt2 = 8;
memcpy(ss.statsMapBuffer_ext[6], &fake_mgr, sizeof(fake_mgr));
io_connect_call(ave_conn, SET_SESSION_SETTINGS_SEL, &ss, sizeof(ss), ...);

Step 4 — Kontrol Edilen PC'ye Virtual Dispatch

Userclient kapatıldığında, AVE_DestroyContext her girdi için DeleteMemoryInfo'yu çağırır. OOB girdileri için sahte pointer zincirini yükler:

x0  = statsMapBufferArray[6]    // attacker-controlled IOSurfaceBufferMngr*
x8  = [x0 + 0x00]              // vtable ptr
x8  = [x8 + 0x28]              // virtual method slot
blr x8                          // PC → attacker gadget

Beklenen: kernel panic ya da kaslr_slide + known_gadget_offset adresindeki JOP/ROP gadget'ına kontrol edilen redirect.

Step 5 — Fake Task Port ile Kernel R/W

Kontrol edilen execution, spray'lenen OSData blob'larından birini sahte bir kernel task port'una dönüştüren bir JOP chain'ine pivot edilir. O port üzerindeki sonraki pid_for_task() / mach_vm_read() çağrıları arbitrary kernel memory read verir; mach_vm_write() arbitrary write verir.

Expected output (successful exploit):
[*] KASLR slide: 0x5e34000
[*] Sprayed 2048 fake ports
[*] AVE2 OOB triggered, closing userclient...
[*] Reallocated dangling region with OOL ports
[*] tfp0 acquired: 0x...
[*] kernel R/W established

Detection

  • Olağandışı bir PC ile AppleAVE2Driver veya AVE_DestroyContext'e atıfta bulunan Jetsam / kernel panic log'ları.
  • KDK + kernel debugger: DeleteMemoryInfo sırasında statsMapBufferArray index > 5 olduğunu izle.
  • Syscall auditing: sandbox'lı bir process'ten hem AppleSPUProfileDriver hem AppleAVE2Driver üzerinde olağandışı io_connect_method dizileri.

Mitigation

  • iOS 13 / watchOS 6 / tvOS 13'te patch'lendi (Ekim 2019 security update).
  • Fix: loopMax hesaplanmadan önce if (memoryInfoCnt2 > MAX_ALLOWED) return kIOReturnBadArgument; eşdeğeri bir bounds check ekle.
  • A12+ üzerindeki Pointer Authentication Codes (PAC), JOP/ROP chain'lerini kısıtlar ama ilk OOB trigger'ı engellemez.

References