ipc_kmsg spray / ikm_size corruption¶
Mach message body'leri, kapasitesi
ikm_size'da kaydedilen kernelipc_kmsgallocation'larında buffer'lanır; message'ları spray'lemekkalloc'u deterministik olarak groom eder ve bir victimikm_size'ı şişiren bir heap overflow, kernel'ın sonraki message'ları gerçek buffer'ın ötesine yazmasını sağlar — klasik bir iOS OOB-write-to-anything primitive'i (extra_recipe / mach_portal soyu).
Mechanism¶
Note
Bir Mach message uçuştayken, kernel onu kalloc'tan allocate edilmiş bir struct ipc_kmsg'ye kopyalar. Buffer'ın gerçek kapasitesi kendi header alanında, ikm_size'da saklanır. Send path'indeki bounds check o alana güvenir: kabaca if (msg_and_trailer_size > kmsg->ikm_size - max_desc) /* too big */. İnvariant şu: ikm_size her zaman gerçek allocation boyutuna eşittir. İki gerçek bunu exploit edilebilir kılar:
- Grooming.
ikm_kmsgallocation'ları tamamen attacker-tarafından sürülür — seçilmiş bir body boyutunda N message gönder ve seçilmiş birkalloczone'una N adet aynı boyutta object yerleştir; onu linear allocation'a tüket ki bir sonraki victim object bitişik düşsün.ipc_kmsg'nin kendisi spray'dir. - Corruption. Bir heap overflow bug'ı, attacker-kontrollü bir allocation'ın ötesine bitişik bir
ipc_kmsg'ye yazar ve onunikm_size'ını yükseltirse, oipc_kmsgartık olduğundan daha büyük olduğunu sanır. Kernel'ın ona buffer'ladığı bir sonraki message (örneğin bir exception message veya bir preallocated port üzerinden teslim edilen biri), şişirilmişikm_size'a karşı bounds-check edilir ve gerçek buffer'ı bitişik heap object'ine taşırır — uzunluğunu ve içeriğini attacker'ın kontrol ettiği bir out-of-bounds write.
Yapı (xnu osfmk/ipc/ipc_kmsg.h):
struct ipc_kmsg {
mach_msg_size_t ikm_size; /* recorded buffer capacity */
struct ipc_kmsg *ikm_next; /* freelist / queue linkage */
struct ipc_kmsg *ikm_prev;
mach_msg_header_t *ikm_header; /* the actual message header */
/* ... */
};
Bir port preallocated bir ipc_kmsg ile oluşturulabilir (IP_PREALLOC davranışıyla mach_port_allocate_full): kernel, port'a gönderilen message'lar için taze allocate etmek yerine o tek buffer'ı yeniden kullanır; belirli, konumlanmış bir ipc_kmsg'yi kontrol edilebilir ve yeniden-tetiklenebilir kılan şey budur.
Walkthrough¶
Ian Beer'in extra_recipe (iOS 10) zincirini izleyerek:
- kalloc zone'unu message'larla groom et. Hedef zone'u doldurmak ve taze, bitişik slab page'leri zorlamak için sabit bir body boyutunda çok sayıda Mach message gönder. Body'ler grooming spray'i olarak da iş görür.
mach_msg_header_t *m = build_message(body_size); /* sizes the ipc_kmsg */
for (int i = 0; i < N; i++)
mach_msg(m, MACH_SEND_MSG, m->msgh_size, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-
Victim bitişikliğini düzenle. Exploit, kernel map'i öyle groom eder ki kontrol edilebilir bir allocation, bir hedef
ipc_kmsg'nin hemen önünde durur (ve sonraki leak'ler için yakınında bir OOL-ports array'i ve bir IOSurface/OSDataspray'i). Beer'in layout'u 8-page'lik biripc_kmsg'yi 8-page'lik bir out-of-line ports array'inin yanına yerleştirir, etraflarına IOSurface property'leri üzerinden ~80 MBOSDataspray'lenmiş. -
ikm_size'ı overflow et. Memory-corruption bug'ını tetikle, attacker buffer'ının ötesine komşuipc_kmsg'ye birkaç kontrollü byte yaz ve onunikm_size'ını yükselt (overflow byte sayısı çalışma zamanında hesaplanır). Victimipc_kmsgartık takip eden allocation'ın ilk kısmıyla örtüşür. -
OOB write'a çevir. Bozuk (preallocated)
ipc_kmsg'ye bir message gönder. Send-path kontrolü şişirilmişikm_size'ı kullanır, böylece message body'si gerçek buffer'ın ötesine bitişik object'e (örneğin OOL ports array'i) yazılır ve attacker'ın sahte Mach port'ları / object pointer'ları forge etmesine izin verir. -
Yükselt. Örtüşen IOSurface
OSData'sını geri okumak kernel pointer'larını (KASLR) açığa çıkarır ve overwrite edilmiş ports array'inden port'lar forge etmektfp0/ kernel task port'una doğru daha güçlü bir read/write'ı bootstrap eder.
Preallocated port'lar neden önemli
Normal bir mach_msg gönderimi yeniden konumlandıramayacağın yeni bir ipc_kmsg allocate eder. Preallocated bir buffer'la oluşturulan bir port, bir ipc_kmsg'yi bilinen, groom'lanmış bir konuma pinler ve kernel'ın her gönderimde tam olarak o buffer'ı yeniden doldurmasına izin verir — tek-seferlik bir bitişikliği aynı bozuk ikm_size'a karşı tekrarlanabilir bir OOB-write tetikleyicisine çevirir.
Warning
Overflow, ikm_size'ı sonraki write'ı hâlâ map'li bellek içinde tutan bir değere yükseltmeli, yoksa OOB write'ın kendisi panic verir. Grooming güvenilirliği kırılgandır: spray'inle tetikleyici arasındaki başıboş kernel allocation'ları bitişikliği bozar, bu yüzden exploit'ler genellikle bir zone-GC / oturma geçişi çalıştırır ve yeniden spray'ler. Apple sonradan ipc_kmsg allocation'larını izole etti ve size hardening ekledi, bu yüzden tam olarak bu primitive modern xnu'da değişmeden hayatta kalmaz.
Detection¶
- Aynı body boyutlarıyla
mach_msggönderim taşkınları (zone grooming) ve preallocated port'ların yoğun kullanımı anormaldir. - Zone-poisoning /
kallocred-zoning veikm_sizesanity assertion'ları şişirilmiş-kapasite write'ını yakalar. ipc_kmsg_copyin*/ message-send path'lerinden kaynaklanan bir OOB write'ı gösteren panic logları.
Mitigation¶
- Modern xnu,
ipc_kmsg'yi dedicated allocation'lara (kalloc_type) ayırır veikm_size'ı doğrular; hem bitişiklik spray'ini hem de bozuk alana olan güveni kırar. - PAC ve zone freelist randomization, spray'in dayandığı öngörülebilir layout'u yener.