Son 0day jailbreak: Tachy0n
(blog.siguza.net)- 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_listioiçindeki Lightspeed yarış koşuluydu;kalloc.16nesnesini 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
racoonortamı 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;IOBufferMemoryDescriptorveIOAcceleratorFamily2kullanarak 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_listiosyscall’ındadır - Kernel, gönderilen I/O işlerini izlemek için
aio_lio_contextyapısını ayırır- Yapıda
io_waiter,io_issued,io_completedalanları bulunur
- Yapıda
- Gerçek işler ayrı bir thread’de yürütülür ve tüm I/O tamamlandığında
do_aio_completionilgili 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 == 0gibi görünmesi sağlanır lio_listiobunu 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 olankalloc.16iç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
racoonortamı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_contextiçin ilk free tetiklenir- Boyutu 1 veya 2 olan OOL mach ports descriptor içeren mach message spray edilir
- İlk entry
MACH_PORT_NULLbırakılarakkalloc.16içine girmesi veio_issued’ın 0 gibi görünmesi sağlanır - İkinci free ile OOL mach ports dizisi serbest bırakılır
kalloc.16iç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
mmapvemlockile 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
__DATAsegmentine veri yerleştirmekti - Uygun target bulunamadığından uygulama yolunda A10/A11 desteği gerçekleşmedi
- Plan, paylaşımlı bellek pointer’ını leak etmek veya kernel
racoonyolu 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ınOSUnserializeXMLspray’i kullanılamıyordu - Bunun yerine
RootDomainUserClient::secureSleepSystemOptionsiçindekiOSUnserializeXMLç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
__DATAiçindeki sysctl global’i, bilinen adreste bir veri deposu gibi kullanılabiliyordu - Spice’ta
vm.swapfileprefixseçildi
- Kernel slide biliniyorsa kernel
- Kernel slide elde etmek için panicall’ın CVE-2018-4413’ü kullanıldı
sysctl_procargsxbilgi sızıntısı,kernel_mapiç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_procargsxengellendiğ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 üzerindenOSDataunserialize eden racer thread’lerini çalıştırır - Varsayılan değerler 4 freerer ve 16 racer’dır; ayarlanabilir
- Alt katman,
- IOSurface üzerinden unserialize edilen veri, birden fazla
OSDataentry içeren birOSDictionaryidiio_issued’a karşılık gelen konum 0 olmalıydı0x41414141,0x69696969gibi magic value’lar ve key değerik, overlap tespitinde kullanıldı
- Yarıştan sonra tüm
OSDatadeğ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
kdeğeri farklıysa başka birOSDatanesnesinin aynı backing buffer’ı gösterdiği bir overlap oluştuğuna karar verilir
- Koddaki
maybe_reyoinkveoverlapfonksiyonları bu çakışma bilgisini oluşturup üst katmana aktarır - Üst katman, çakışan
OSDatanesnelerini kullanarak fake mach port oluşturur- Bir
OSDatafree edilir - OOL port descriptor içeren mach message spray edilir
- Diğer
OSDatafree edilir - Fake task port pointer’ı içeren yeni
OSDataolarak yeniden ayrılır
- Bir
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
IOSurfaceRootservice 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
IOMemoryDescriptoridi_rangesalanı birIOVirtualRangedizisidir; tek birIOVirtualRangetam olarakkalloc.16içine sığar- Ancak sıradan
IOMemoryDescriptor, yalnızca bir range varsa heap allocation yerine_singleRangekullanır
IOBufferMemoryDescriptorise istisnai olarak tek bir range içinIONew(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ıncaIOAccelContext2elde 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
IOAccelContext2açılır ve çakışan ikiOSDataalınır- Bir
OSDatafree edilir - Önceden açılmış
IOAccelSharedUserClient2,IOConnectAddClient()ile bağlanır - Kalan
OSDataokunur; ilk 8 byte’ın page-aligned kernel pointer, sonraki 8 byte’ın ise0x8000olup olmadığı kontrol edilir - Koşul sağlanmazsa
IOAccelContext2kapatılıp tekrar denenir
pageable memory, fake port, zone_require
- Memory descriptor process’e map edilip kernel adresi öğrenildikten sonra da belleğin
kIOMemoryPageableolarak 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ğıranIOAccelContext2::submit_data_buffersexternal 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 == 0x100olacak 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_requirebypass’ı da gerekiyordu- O dönemde
zone_require,zone_mapdışındaki page’lere izin veriyor ve page’in ilk0x20byte’ı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
- O dönemde
- 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
- Bu analiz How to unc0ver a 0-day in 4 hours or less yazısında özetlendi
- 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
kallocvezallocidi- 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
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.
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
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ış
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
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
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
Bir aracıdan mı geçmek gerekiyor, yoksa birinci seviye müşteri desteğinde kaybolmayacak herkese açık bir e-posta gibi bir şey var mı bilmiyorum
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
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
Bunun dahil edilip dağıtıldığı jailbreak olan unc0ver’ın muhtemelen “semi-untethered” olduğunu hatırlıyorum