1 puan yazan GN⁺ 2025-05-25 | 2 yorum | WhatsApp'ta paylaş
  • tachy0n, iOS 13.0~13.5 üzerinde çalışan bir kernel yetki yükseltme exploit’iydi; 23 Mayıs 2020’de unc0ver v5.0.0’a 0day olarak dahil edildi ve o dönemin en güncel iOS sürümünü doğrudan hedef aldı
  • Esas nokta, lio_listio içindeki Lightspeed yarış koşuluydu; kalloc.16 nesnesini iki kez serbest bırakarak farklı nesnelerin aynı belleği göstermesini sağlayabiliyordu
  • Aynı aileden bug’lar daha önce iOS 11’i hedefleyen Spice jailbreak/untether’da da kullanılmıştı; ancak uygulama sandbox’ı ile racoon ortamı yetkiler, sandbox ve spray teknikleri açısından oldukça farklı kısıtlamalara sahipti
  • unc0ver için exploit, tekrar tekrar OSData çakışması elde ediyor; IOBufferMemoryDescriptor ve IOAcceleratorFamily2 kullanarak fake task ve fake mach port oluşturuyordu
  • iOS 14 sonrasında Apple, allocator ayrıştırma, sequestering, PAC ve nesne bazlı hardening ile exploit stratejisinin kendisini engelleme yönüne geçti; halka açık iOS kernel exploit bilgisi ise iOS 13 dönemine kıyasla ciddi biçimde geride kaldı

tachy0n’un yayımlanmasının neden sıra dışı olduğu

  • tachy0n, iOS 13.0’dan 13.5’e kadar etkileyen eski bir exploit’ti ve 23 Mayıs 2020’de unc0ver v5.0.0’a dahil edilerek yayımlandı
  • O günün ölçütleriyle standart bir kernel yerel yetki yükseltmesi (kernel LPE) olsa da, güncel iOS sürümünü etkileyen bir 0day jailbreak olarak yayımlanması nadirdi
  • Apple, exploit’in yayımlanmasından yaklaşık 1 hafta sonra yalnızca bu bug’a yönelik bir yama dağıttı
  • Bu bug iOS 13.5’te 0day’di, ancak daha önce 1day biçiminde zaten kullanılmıştı
  • Pwn20wnd, uygulama sandbox’ından erişilebilen bir 0day aradı; çıkış noktası, bilinen 1day’ler için yapılan regresyon testleriydi
    • iOS 12’deki SockPuppet, iOS 12.3’te yamalanmıştı ancak iOS 12.4’te tekrar ortaya çıktı
    • Bu örnek, Apple’ın bu tür bug’lar için regresyon testi eksikliğini ortaya koydu; Pwn20wnd da bilinen bazı 1day’ler için regresyon testleri uygulayarak hit elde etti

Lightspeed: lio_listio yarış koşulu

  • tachy0n’un temel bug’ı Synacktiv’in Lightspeed bug’ıdır ve CVE-2020-9859 ile CVE-2018-4344 olasılığıyla ilişkilidir
  • Zafiyet, asenkron veya toplu dosya I/O işlemi yapan lio_listio syscall’ındadır
  • Kernel, gönderilen I/O işlerini izlemek için aio_lio_context yapısını ayırır
    • Yapıda io_waiter, io_issued, io_completed alanları bulunur
  • Gerçek işler ayrı bir thread’de yürütülür ve tüm I/O tamamlandığında do_aio_completion ilgili context’i serbest bırakır
  • Hiçbir iş zamanlanmadıysa context’i lio_listio’nun mevcut thread’inin serbest bırakması gerekir
  • Sorun, bu kontrolde bir yarış koşulu bulunmasıdır
    • Başka bir thread’e iş gönderilmiş olabilir; ancak kontrol anından önce iş çoktan tamamlanıp context serbest bırakılmış olabilir
    • Bu durumda lio_listio, dangling pointer haline gelmiş lio_context’i yeniden kontrol eder

double free’nin exploit’e dönüşme akışı

  • Exploit için gereken sıra şöyledir
    • lio_listio, lio_context’i ayırır
    • İş tamamlanır ve do_aio_completion, lio_context’i serbest bırakır
    • Serbest bırakılan bellek, saldırganın kontrol ettiği bir nesne olarak yeniden ayrılır ve lio_context->io_issued == 0 gibi görünmesi sağlanır
    • lio_listio bunu görüp saldırgan nesnesini yeniden serbest bırakır
    • Aynı bellek bir başka nesne olarak tekrar ayrılır ve iki farklı allocation’ın aynı belleği göstermesi sağlanır
  • 64 bit cihazlarda lio_context, en küçük zone olan kalloc.16 içine girer
  • iOS 14 öncesinde nesne türünden bağımsız olarak aynı boyut temelinde aynı allocation site paylaşılıyordu
    • C++ nesneleri, pointer dizileri ve kullanıcının sağladığı veri buffer’ları aynı boyuttaki bucket içinde birbirlerinin belleğini yeniden kullanabiliyordu
  • Bu double free, arada yeniden allocation yoksa kolayca ölümcül duruma giden tipik double free’lerden farklı çalışır
    • lio_context->io_issued, allocation sırasında 0 olmaz
    • Serbest bırakıldıktan sonra allocator ilk 8 byte’ı canary değeri ve freelist pointer’ı ya da nesne adresinin XOR değeriyle ezer
    • Bu nedenle ikinci free ancak arada yeniden allocation varsa ve 4~7. byte’lar 0 ise gerçekleşir
  • Gerçek exploit bu yarışı birçok kez yeniden deneyebiliyordu; sistemdeki başka bir nesnenin tesadüfen gereken byte’ları 0 yapıp double free’yi tetiklemesi pratikte nadirdi

Spice’ta daha önceki kullanım

  • Aynı bug, iOS 11.x’i hedefleyen Spice jailbreak/untether’da da kullanıldı
  • Spice, Sparkey ve littlelailo ile birlikte Jake Blair ekibi tarafından geliştiriliyordu; o dönemde en güncel sürüm iOS 13.x idi
  • Amaç, hem uygulama ortamında hem de racoon ortamında mach port forgery oluşturmaktı
    • iOS 14 öncesi kernel exploit’lerinde, kullanıcının sağladığı bir değerin mach port pointer’ı olarak yorumlatılması sonraki aşamaları belirleyici biçimde kolaylaştırıyordu
  • Lightspeed ile mach port forgery oluşturmanın temel akışı şöyleydi
    • lio_context için ilk free tetiklenir
    • Boyutu 1 veya 2 olan OOL mach ports descriptor içeren mach message spray edilir
    • İlk entry MACH_PORT_NULL bırakılarak kalloc.16 içine girmesi ve io_issued’ın 0 gibi görünmesi sağlanır
    • İkinci free ile OOL mach ports dizisi serbest bırakılır
    • kalloc.16 içine kontrollü veri spray edilerek mach ports dizisi fake pointer ile değiştirilir

Uygulama sandbox’ı ile racoon arasındaki kısıt farkları

  • A7~A9(X) üzerinde PAN olmadığı için yalnızca mmap ve mlock ile userland adresleri kernel pointer’ı gibi dereference edilebiliyordu
  • A10 ve A11 de desteklenmek istendi; ancak uygulama sandbox’ında uygun bir kernel adres sızıntısı ve kontrollü veri yerleştirme hedefi bulunamadığı için tamamlanamadı
  • Kullanılmak istenen 1day’ler arasında Ian Beer’in kernel stack bilgi sızıntısı ve backboardd sandbox escape’i vardı
    • Plan, paylaşımlı bellek pointer’ını leak etmek veya kernel __DATA segmentine veri yerleştirmekti
    • Uygun target bulunamadığından uygulama yolunda A10/A11 desteği gerçekleşmedi
  • racoon yolu farklı koşullara sahipti
    • root olarak çalışır, ancak normal uygulamalardan daha sıkı bir sandbox’a sahiptir
    • IOSurface erişimi olmadığı için IOSurface::setValue üzerinden yaygın OSUnserializeXML spray’i kullanılamıyordu
    • Bunun yerine RootDomainUserClient::secureSleepSystemOptions içindeki OSUnserializeXML çağrısı kullanılarak bazı nesneler leak biçiminde spray edilebiliyordu
  • racoon, tüm sysctl okuma ve yazmalarına izin veren bir sandbox profiline sahipti ve root yetkisi de vardı
    • Kernel slide biliniyorsa kernel __DATA içindeki sysctl global’i, bilinen adreste bir veri deposu gibi kullanılabiliyordu
    • Spice’ta vm.swapfileprefix seçildi
  • Kernel slide elde etmek için panicall’ın CVE-2018-4413’ü kullanıldı
    • sysctl_procargsx bilgi sızıntısı, kernel_map içindeki neredeyse bir sayfalık başlatılmamış kernel belleğini leak edebiliyordu
    • Böylece kernel kodu ve heap pointer’ları elde edilip A7~A11 işlenebiliyordu
    • Uygulama sandbox’ında sysctl_procargsx engellendiği için aynı yöntem mümkün değildi

unc0ver için tachy0n exploit yapısı

  • unc0ver hedefi A8~A13 olduğu için A10+’ı yok saymak veya userland dereference’a güvenmek mümkün değildi
  • Exploit, başarısız olabilecek bellek corruption aşaması dikkate alınarak iki katmanlı yapı olarak tasarlandı
    • Alt katman, lio_listio çağıran freerer thread’leri ile IOSurface üzerinden OSData unserialize eden racer thread’lerini çalıştırır
    • Varsayılan değerler 4 freerer ve 16 racer’dır; ayarlanabilir
  • IOSurface üzerinden unserialize edilen veri, birden fazla OSData entry içeren bir OSDictionary idi
    • io_issued’a karşılık gelen konum 0 olmalıydı
    • 0x41414141, 0x69696969 gibi magic value’lar ve key değeri k, overlap tespitinde kullanıldı
  • Yarıştan sonra tüm OSData değerleri kontrol edilir
    • Magic value’su değişen nesnelerin sistemdeki başka bir nesne tarafından alındığı varsayılır ve sonradan cleanup hedefi olarak işaretlenir
    • Key ile buffer içindeki k değeri farklıysa başka bir OSData nesnesinin aynı backing buffer’ı gösterdiği bir overlap oluştuğuna karar verilir
  • Koddaki maybe_reyoink ve overlap fonksiyonları bu çakışma bilgisini oluşturup üst katmana aktarır
  • Üst katman, çakışan OSData nesnelerini kullanarak fake mach port oluşturur
    • Bir OSData free edilir
    • OOL port descriptor içeren mach message spray edilir
    • Diğer OSData free edilir
    • Fake task port pointer’ı içeren yeni OSData olarak yeniden ayrılır

Bilinen kernel adresine kontrollü veri yerleştirmek

  • Exploit, OOL ports descriptor dizisi olarak yeniden ayrılmış içeriği OSData şeklinde okuyarak mach port’un raw kernel pointer’ını leak edebiliyordu
  • Sonraki aşamalarda bunu task port ve IOSurfaceRoot service port adreslerini leak etmek için kullanır; ancak asıl sorun, kontrol edilebilir buffer’ın kernel adresini istikrarlı biçimde elde etmekti
  • XNU kaynağında bulunan aday IOMemoryDescriptor idi
    • _ranges alanı bir IOVirtualRange dizisidir; tek bir IOVirtualRange tam olarak kalloc.16 içine sığar
    • Ancak sıradan IOMemoryDescriptor, yalnızca bir range varsa heap allocation yerine _singleRange kullanır
  • IOBufferMemoryDescriptor ise istisnai olarak tek bir range için IONew(IOAddressRange, 1) çağırıp heap allocation yapar
  • Bunu keyfi olarak ayırıp kullanıcı adres alanına map edebilen kullanışlı yer IOAcceleratorFamily2’nin AGX arayüzüydü
    • IOGraphicsAccelerator2 üzerinden type 0 userclient açılınca IOAccelContext2 elde edilir
    • ::clientMemoryForType() ile üç memory descriptor map edilebilir
    • type 0, 0x8000 byte olduğundan victim descriptor’ı tanımlamada kullanıldı
  • Exploit şu döngüyü kullanır
    • IOAccelContext2 açılır ve çakışan iki OSData alınır
    • Bir OSData free edilir
    • Önceden açılmış IOAccelSharedUserClient2, IOConnectAddClient() ile bağlanır
    • Kalan OSData okunur; ilk 8 byte’ın page-aligned kernel pointer, sonraki 8 byte’ın ise 0x8000 olup olmadığı kontrol edilir
    • Koşul sağlanmazsa IOAccelContext2 kapatılıp tekrar denenir

pageable memory, fake port, zone_require

  • Memory descriptor process’e map edilip kernel adresi öğrenildikten sonra da belleğin kIOMemoryPageable olarak oluşturulması sorunu kalıyordu
  • Fake mach port ve fake task nesnesine preemption kapalıyken erişilebileceği için, kernel tarafında ilgili page’in fault-in edilmesi gerekiyordu
  • Bu, IOAccelContext2::processSidebandBuffer’ı dolaylı çağıran IOAccelContext2::submit_data_buffers external method 2’nin iki kez çağrılmasıyla çözüldü
    • Paylaşımlı belleğin başlangıcından 0x10 byte sonraki yapı okunur
    • İlk yapı tok == 0x100 olacak ve tüm page’i kaplayacak şekilde hazırlanır; böylece ikinci page’e ilerlenir
    • İkinci page’e daha sonra fake object verileri yerleştirilebilir
  • Sonraki aşamalar fake task, fake port, OOL descriptor switcheroo ve keyfi okuma primitive’i kurmaya gider
  • zone_require bypass’ı da gerekiyordu
    • O dönemde zone_require, zone_map dışındaki page’lere izin veriyor ve page’in ilk 0x20 byte’ını metadata gibi yorumluyordu
    • Doğru zone index’i koymak, istenen zone için geçiş bileti gibi kullanılabiliyordu
    • Bu yüzden task için bir page ve mach port için bir page olmak üzere toplam iki page gerekiyordu
  • Bu exploit şu anda GitHub’da açık olarak bulunuyor

Yayın sonrası analiz ve yama

  • Güncel imzalı sürüm için eksiksiz bir 0day exploit’in yayımlanması iOS jailbreak scene’inin dikkatini çekti
  • O dönemde Project Zero’da çalışan Brandon Azad, exploit yayımlandıktan sonraki 4 saat içinde zafiyeti tespit edip Apple’a bildirdi
  • Exploit’in yayımlanmasından 6 gün sonra Synacktiv, yeni bir yazı ile iOS 12’deki orijinal fix’in memory leak oluşturduğunu ve bu memory leak’i düzeltme girişiminin orijinal bug’ı geri getirmiş olabileceğini ele aldı
  • Exploit’in yayımlanmasından 9 gün sonra Apple yamayı dağıttı
  • Daha sonra XNU’ya bu bug için bir regresyon testi eklendi
  • Yayımdan 54 gün sonra reverse-engineered sürüm olan “tardy0n”, Odyssey jailbreak’e dahil edildi; hedef yine iOS 13.0~13.5 idi

iOS 14 sonrası değişen exploit ortamı

  • iOS 14, Apple’ın kernel güvenliği stratejisindeki değişimi gösterir
  • iOS 14 öncesinde ilk primitive heap overflow, C++ nesne over-release, type confusion ya da başka bir şey olsun, sonraki target çoğunlukla mach port olurdu
  • iOS 14’teki en büyük değişimlerden biri allocator olan kalloc ve zalloc idi
    • zone map’i birden fazla “kheap” aralığına böldü
    • Kullanıcı kontrollü veri ile kernel nesnelerinin farklı heap’lere girmesini sağlayacak şekilde ayırdı
    • Kernel nesnelerine sequestering uygulanarak belirli bir zone’a ayrılmış virtual address page’in yeniden başlatmaya kadar başka bir zone tarafından yeniden kullanılmaması sağlandı
    • Physical memory serbest bırakılabilir; ancak virtual memory range başka nesneler için yeniden kullanılmadığından kernel nesnesi type confusion’ı fiilen engellenir
  • Guard page, her boot’ta değişen zone allocation başlangıç konumu ve sonraki inceltmelerle birlikte cross-zone saldırılarının güvenilirliği ciddi biçimde düştü
  • Apple, yalnızca tek tek bug’ları kapatmaktan exploit stratejisini engelleme yaklaşımına geçti
    • Exploit kmsg struct’ını corruption target olarak kullanırsa ilgili yapı imzalanır
    • Pipe buffer istikrarlı bir kernel okuma/yazma arayüzü olarak kullanılırsa ilgili pointer’lar PAC kapsamına alınır
    • Alakasız bir nesneyi victim olarak kullanma yöntemi ortaya çıkarsa ilgili nesne tipi hardening’e tabi tutulur
  • Sonuç olarak exploit geliştirmede exploit strategy’nin, ilk bellek corruption 0day’inden daha değerli hale geldiği bir durum oluştu

Açık bilgide kopuş

  • iOS 14 öncesinde açık iOS güvenlik araştırması bilgisinin özel bilgiyeyle neredeyse aynı seviyede olduğu değerlendiriliyordu
  • iOS 14 sonrasında ise istisnalar dışında bilgi paylaşımı fiilen durmuş durumda
  • iOS 19 beta’ya birkaç hafta kalmışken bile iOS 18 veya iOS 17 için halka açık kernel exploit bulunmadığı belirtiliyor
  • Apple güvenlik notlarında zaman zaman sahada istismar edilen zafiyetler yer alsa da, açık bilgiler özel araştırmaları yakalayamıyor
  • tachy0n’un yayımlanmasının üzerinden yalnızca 5 yıl geçmiş olması, iOS kernel exploit alanının ne kadar hızlı değiştiğini gösteriyor

2 yorum

 
ndrgrd 2025-05-26

Apple’ın donanımı harika olabilir ama yazılımı kullanıcıyı tasmalı tutma niyetiyle dolu.
Kendi yapıp derlediğiniz bir uygulamayı yalnızca kendi cihazınızda çalıştırmak isteseniz bile 100 dolarlık bir abonelik gerekiyor.

Küçük ve orta ölçekli açık kaynak uygulamaları kullanıp kendisi derleyerek kullanan bir geliştiriciyseniz,
Apple cihazlarında açık istismar edip jailbreak yaparak sideload etmektense doğrudan Android kullanmak daha rahat.

 
GN⁺ 2025-05-25
Hacker News yorumları
  • 1 trilyon dolarlık bir şirketi alt eden şeyin, Apple’ın özellikle zayıf olduğu basit ve sıkıcı bir iş, yani regresyon testi olması etkileyici
    iOS 12’de jailbreak için kullanılan büyük açıklardan biri olan SockPuppet, Project Zero’dan Ned Williamson tarafından bulunup Apple’a bildirildi; iOS 12.3’te yamalandı ve daha sonra Project Zero hata takip sisteminde kamuya açıldı
    Ama iOS 12.4’te, sanki hiç yamalanmamış gibi yeniden ortaya çıktı; muhtemelen Apple o sürüm için XNU’yu ayrı bir dala fork ederken yamayı uygulayamadı
    Bu, bu tür açıklar için regresyon testi olmadığına dair güçlü bir işaretti; bilinen birkaç 1-day’i otomatikleştirmek bile Pwn’in hedefi doğrudan vurmasına yetebilirdi
    Linux, FreeBSD, OpenWRT, OpenSSH gibi projelerin yeni sürümleri üzerinde geçmiş açıkları sürekli çalıştıran CI çiftliği işleten kaç yer vardır merak ediyorum

    • Regresyon testi, düzeltilen hatanın yeniden ortaya çıkmadığını doğrulayan standart bir QA prosedürüdür
      20 yıl önce üniversitedeyken Mozilla’da gönüllü QA yapmıştım; render/layout ve JavaScript motoru hataları etrafında sürekli büyüyen bir regresyon testi koleksiyonu vardı
      Yeniden üretmek ve düzeltmeyi doğrulamak için minimal test case’ler oluşturduğumuzdan, bunları build pipeline’a koymak da kolaydı
      Hatalar kaçınılmazdır ama zaman ve para harcanarak düzeltilmiş bir hatanın geri gelmesi en kötü senaryodur
      Kaliteye önem veren kurumlar regresyon testine kesinlikle yatırım yapar; ancak birçok kurum QA’e saygı duymaz, ya hiç yapmaz ya da en ucuz dış kaynakçıya devreder
      Apple’ın tarihsel olarak en çok ilgi çeken hata sınıflarından biri olan jailbreak için regresyon testine sahip olmaması gerçekten tuhaf
      Bugün Mozilla çeşitli eleştiriler alabilir ama 2000’lerin başında bile Tinderbox ve Bugzilla gibi araçlarla oldukça sağlam bir QA ve CI/CD yürütüyordu
      DevOps popülerleşip bu yaklaşımı yaygınlaştırdığında, herkesin zaten bunu yaptığını sanmıştım; meğer bu benim yanılgımmış
    • Burada projelere istihbarat kurumları da dahipse, en azından G10 istihbarat kurumlarının ve Rusya, Çin, Kuzey Kore ile sayısız özel grubun bunu yaptığını varsaymak güvenli olur
    • Temel sorun, birçok kurumun güvenlik işlerini ayrı bir akışa ve ayrı bir hata sınıflandırmasına izole etmiş olmasında gibi görünüyor
      Bu, güvenlik ile özellik geliştirmenin ayrılmasının yarattığı Conway Yasası benzeri bir durum
      Build/release süreçleri ve olgun regresyon testi setleri olsa bile, iç organizasyon yapısı nedeniyle bu tür güvenlik konularının onların içine girmemesi kuvvetle muhtemel
    • Adını hatırlamıyorum ama her issue için test case dizini olan bir FOSS projesi görmüştüm
      Kolayca binlercesi vardı; muhtemelen SQLite idi
      Örnek alınacak bir yaklaşım
      Düzeltmeleri backport etmiyorsanız testleri de backport etmeme ihtimaliniz yüksek görünüyor
  • kheap separation, task port mitigations, SSV, SPTM gibi ifadeleri görünce, yabancı bir dilde arkadaşınla gayet iyi sohbet ederken bir anda beyin ameliyatı ya da nükleer fizik anlatımına geçilmiş ve anlayışın uçurumdan düşmüş gibi hissettim
    Bir yüksek fırın revizyonu konuşmasını çevirmeye çalıştığım zaman da benzerdi
    Jailbreak’in artık eskisi kadar canlı olmaması üzücü
    Jailbreak yapılmış iPad’imle pratik pek bir şey yapmadım ama eğlenceliydi; şimdi olsa tethering uygulamaları, UTM ve bir JIT çözümü kurmak isterdim
    SideStore da umut verici görünüyordu ama hesabım bir dönem ücretli Apple Developer hesabı olduğu için süresi dolmayan 10 app ID kalmış durumda; bu yüzden yeni hesap açmadan ya da tekrar para ödemeden UTM gibi uygulamaları kuramıyorum

    • Eski iPhone 4’ümü jailbreak ederek kullanmıştım ve iPhone’u ana cihaz olarak kullanabilmenin fiilen tek yolu buydu
      Onu kaybettikten sonra Android’e döndüm; o sıralarda Android temel işlevler açısından epey yetişmişti
  • Apple’ın bugünlerde jailbreak için 1 milyon dolar ödediğini duydum; bu da serbest piyasa fiyatının alt sınırı olsa gerek

  • Bu doğruysa Apple şaşırtıcı bir strateji kullanmış demektir
    Cihazda root elde etmenin tüm yollarını kapatırsanız, jailbreak geliştiricilerinin ücretsiz bulduğu açıkları Apple yamalayabilir

    • Ama tam olarak öyle değil
      Yazıya göre kapalı topluluklar hâlâ exploit’lere sahip ve Apple bunları yamalıyor
      Görünüşe göre yalnızca açık topluluk ya da bu geliştirici tarafı, bir nedenle artık böyle değil
  • Tüm yazıdaki en sevdiğim cümle şuydu: “iOS 13.0’da o bug’ı unpatch eden kişiye de teşekkür etmek istiyorum. Bu da çok havalı bir hareketti.”

  • “5 yıl sonra nerede olacağımızı hayal bile edemiyorum” denmiş; ben edebiliyorum
    iMessage hâlâ cihaz, hesap ve veri ele geçirmeyi mümkün kılıyor

  • Yazıda tethered mi untethered mı olduğu belirtilmemişti

    • tachy0n bir yerel yetki yükseltme (LPE) olduğu için bu sınıflandırma pek uymuyor
      Bunun dahil edilip dağıtıldığı jailbreak olan unc0ver’ın muhtemelen “semi-untethered” olduğunu hatırlıyorum