Linux’un yeni mseal syscall’una derinlemesine bakış
(blog.trailofbits.com)- Linux 6.10’daki mseal, çalışan sanal bellek bölgelerini mühürleyerek kod yürütme yetkisi elde eden bir saldırganın sonradan VMA izinlerini veya yerleşimini değiştirmesini zorlaştıran, exploit azaltmaya yönelik bir syscall’dur
mseal(start, len, flags), sayfa hizalı VMA aralığınaVM_SEALEDekler; çekirdekmprotect,munmap,mmap(MAP_FIXED),mremapve bazı yıkıcımadviseyollarında değişiklikleri reddeder- Mevcut
memfd_createveyamemfd_secretRAM tabanlı anonim dosyalar ve gizli bellek erişim denetimine daha yakınken, mseal uzak saldırganın kod yürütmesinden sonra izin değiştirme, unmapping ve remapping işlemlerini engellemeye odaklanır - Başlıca savunma hedefleri,
mprotect(PROT_EXEC)ile NX’i atlatıp shellcode çalıştıran saldırılar vemunmap/remapping ile bellekte delik açtıktan sonra burayı saldırgan verisiyle dolduran yalnızca veri odaklı exploit’lerdir - glibc 2.41 sonrasında entegre edilmesi mümkün olsa da, yığın ve heap çalışma zamanında büyüyüp küçüldüğü için otomatik mühürleme hedefi değildir; bu nedenle geliştiricilerin uygulama zamanı ve kapsamını dikkatle belirlemesi gerekir
mseal’in sağladığı koruma
- Bellek mühürleme (memory sealing), çalışan bir VMA aralığını sonraki tehlikeli değişikliklere karşı neredeyse değişmez hale getiren bir özelliktir
- Saldırgan kod yürütme primitive’i elde etse bile, mühürlenmiş VMA’da sanal bellek işlemleriyle izinleri değiştirmesi veya yerleşimi lehine manipüle etmesi zorlaşır
- Chrome Security ekibi, Linux tabanlı ChromeOS’te V8 CFI stratejisini desteklemek için bu syscall’u kullanıma aldı
- Birkaç tartışma ve yeniden yazımın ardından Linux çekirdeğine girdi; glibc entegrasyonu sayesinde kullanım alanı tarayıcı dışına genişleyebilir
memfd ailesinden farkı
memfd_createvememfd_secret, dosya mühürleme (file sealing) ailesine daha yakındır- RAM tabanlı anonim dosyalar oluşturabilir
memfd_secret, yalnızca dosya tanımlayıcısına sahip süreçlerin ilgili bellek bölgesine erişmesini sağlar- Hassas bellek içi verileri koruyan “secure enclave” tarzı kullanıcı alanı eşlemelerinde kullanılabilir
mseal, hassas bilgi sızdırmayı hedefleyen yerel saldırganlardan çok, kod yürütmeyi hedefleyen uzak saldırganlara karşı exploit azaltmaya göre tasarlanmış bir syscall’dur
Çekirdek içindeki çalışma şekli
- syscall imzası
int mseal(unsigned long start, size_t len, unsigned long flags)şeklindedirstartvelen, mühürlenecek geçerli VMA’nın başlangıcını ve uzunluğunu belirtirlendoğru şekilde sayfa hizalı olmalıdır- Şu anda
flagskullanılmaz ve0olmalıdır
- Linux 6.12 itibarıyla uygulama
do_msealçağırırcurrent->mm, çağıran sürecin tüm sanal bellek adres alanı olanmm_struct’ı gösterirmm_structiçindekimmap,mmapile oluşturulmuş bitişik bellek bölgeleri olanvm_area_structlistesini içerir- Yığın veya VDSO gibi bölgeler de tek bir VMA olarak temsil edilebilir
do_mseal, aralığın bitiş adresini hesapladıktan sonrammap_write_lock_killableile bellek bölgesini kilitlercheck_mm_seal, hedef aralıktaki her VMA üzerinde dolaşır ve önce sınır geçerliliğini kontrol eder- Aralığın ortasında ayrılmamış bellek varsa
-ENOMEMdöndürür - Bu ilk kontrol, hata durumunda yalnızca bazı VMA’ların mühürlenmesi gibi bir durumu önlemek içindir
- Aralığın ortasında ayrılmamış bellek varsa
apply_mm_seal, aynı aralığı yeniden dolaşır vemseal_fixuparacılığıyla hedef VMA’yaVM_SEALEDbayrağını ekler
Mühürleme sonrasında yasaklanan bellek işlemleri
- Çekirdek yama seti,
VM_SEALEDkontrolünümm/madvise.c,mm/mmap.c,mm/mprotect.c,mm/mremap.c,mm/mseal.cdosyalarına ekler mprotectvepkey_mprotect, içeridemprotect_fixupüzerindencan_modify_vmaçağırır; VMA mühürlüyse-EPERMdöndürülür- Mühürlenmiş VMA’da şu işlemlere izin verilmez
mprotect,pkey_mprotectüzerinden izin bitlerini değiştirmemunmapile unmappingmmap(MAP_FIXED)ile mühürlenmiş eşlemeyi değiştirilebilir ve mühürsüz bir eşlemeyle değiştirmemremapile boyutu büyütme veya küçültmemremap(MREMAP_MAYMOVE | MREMAP_FIXED)ile yeni hedefe taşıma- Bazı yıkıcı bayraklarla
madvisekullanımı
mremaptaşımasında hem kaynak hem hedef VMA için mühür kontrolü uygulanırMREMAP_DONTUNMAPyoksa kaynak VMA unmap edilir; bu durumda damunmapmühür kontrolünden geçmesi gerekir- Linux 6.10 ve üzerinde
msealdoğrudan syscall çağrısıyla kullanılabilir; örnek wrapper syscall numarası olarak462veflags=0kullanır
NX’i güçlendirerek shellcode yürütmeyi engelleme
- Saldırgan, ROP gibi kod yeniden kullanım teknikleri olsa bile shellcode yürütmeyi tercih edebilir
- Çalıştırılamaz yığına veya heap’e shellcode serpiştirir
- Zafiyet üzerinden ilk ROP zincirini çalıştırır
mprotect(PROT_EXEC)ile shellcode’un bulunduğu bölgenin NX bitini kapatır- O bölgeye atlayıp shellcode’u çalıştırır
- CVE-2018-7445 kullanan MikroTik RouterOS SMB daemon saldırısı bu akışa bir örnektir
- Soket tabanlı shellcode’u çalıştırılamaz heap’e serpiştirir
- Stack overflow ile oluşturulan ROP zinciri heap bellek izinlerini değiştirir
- Ardından shellcode’u çalıştırır
- İlgili VMA
msealile mühürlenirse,mprotectçağrısı sırasındacan_modify_vmakontrolü izin değişikliğini engeller - Örnek kodda, yığındaki shellcode sayfası mühürlenmezse
mprotect(PROT_READ|PROT_WRITE|PROT_EXEC)sonrasında yürütme mümkündür - Aynı sayfa
msealile mühürlenirsemprotectgerçek izinleri değiştiremez ve shellcode yürütülürken segmentation fault oluşur
Yığın ve heap’e uygulamadaki kısıtlar
- glibc 2.41 sonrasında
msealdevreye alındığında dinamik yükleyicinin önceden belirlenmiş bir VMA kümesine mühür uygulaması planlanıyor - Mevcut planda yığın ve heap otomatik olarak mühürlenmez
- Yığın ve heap çalışma sırasında genişleyebildiği için otomatik mühürleme uygulamanın davranışını bozabilir
- Heap ayırıcısı alanı geri kazanmak için
brksyscall’unu çağırabilir - Bu yolda
arch_unmapvedo_vmi_unmapüzerinden küçülme gerçekleşebilir - Mühürlü durumda bu tür unmapping işlemlerine izin verilmez; bu da dinamik bellek ayırmayı bozabilir
- Heap ayırıcısı alanı geri kazanmak için
- Geliştiriciler, uygulama bağlamına göre hangi zamanda ve hangi bölgelerde mühürleme yapılabileceğine karar vermelidir
- Örnek makro, güvenilmeyen veri bulunabilecek seçilmiş yığın frame’lerinin sayfalarını tekrar tekrar mühürler
- Zaten mühürlenmiş bir VMA’ya yeniden
msealçağrılırsa no-op olarak işlenir ve hata oluşmaz - Otomatik yığın genişletme veya stack splitting gibi özellikler için ayrıca dikkat gerekir
- Saldırgan shellcode’da ısrar ederse yeni bir çalıştırılabilir bölgeyi
mmapedip okunabilir bir bölgeden payload’u kopyalayabilir; ancak süreç daha zahmetli hale gelir
Unmapping tabanlı yalnızca veri odaklı exploit’leri azaltma
mprotectengeli, mühürlenmiş bölgenin yazılabilir duruma geçirilmesini de engeller; bu, değiştirildiğinde exploit primitive’ini güçlendirebilecek veri değişkenlerini korumaya yardımcı olur- Chrome bakımcıları, bozuk bir pointer’ı unmapping/remapping syscall’larına vererek bellekte delik açma ve burayı saldırgan denetimindeki veriyle yeniden doldurma tekniğini dikkate aldı
- Bu yöntem, yığın dönüş adresi veya fonksiyon pointer’ı gibi kontrol akışı geçişlerini doğrudan değiştirmediği için forward-edge ve backward-edge CFI garantilerini atlatabilir
- JIT derleyicileri uygulayan tarayıcılarda bu teknik özellikle kullanışlı olabilir
- V8’in Turbofan’ı RW ile RX arasında geçiş yapan bölgeler oluşturabilir
- Saldırgan, JIT derleme sürecinde hot-path JavaScript ile çalıştırılabilir kod ürettirebilir
- Unmap edilen bölgeyi doldurup kritik verilerin üzerine yazarak kod yürütmeye giden bir değişiklik oluşturabilir
- Kontrol akışını doğrudan ele geçirmeyi veya pointer sızıntısını gerektirmeden, bellekteki belirli verileri manipüle ederek kontrol akışını etkileyen yalnızca veri odaklı exploit’tir
mseal, unmapping ve remapping işlemlerini engelleyerek bu tür hole-punching senaryolarını önleyebilir
House of Muney örneği
- House of Muney, kullanıcı alanı heap exploit’lerinde benzer bir unmapping tabanlı teknik kullanır
- Qualys, eski bir Qmail hatasına yönelik gerçek exploit içinde bu tekniği kullandı
- Bu teknik, büyük allocation chunk’larının
M_MAP_THRESHOLDdeğerinden büyük olduğundamallocvefree’nin sırasıyla doğrudanmmapvemunmapçağırması özelliğine dayanır - Büyük chunk’larda arada freelist cache bulunmadığından exploit süreci basitleşir
- Allocation chunk’ının üst kısmındaki boyut metadata’sı başka bir sayfa boyutuna dönüştürülüp
freeedilirse, chunk’a bitişik bellek bölgesi içinmunmapgerçekleşebilir - Dulin’in örneği, keyfi
munmapile.gnu.hashve.dynsymbölgelerini hedefler- Ardından daha büyük bir
mmapchunk’ı ile yeniden doldurur - Henüz çözülmemiş bir PLT girişinin üzerine yazmayı mümkün kılar
- GOT overwrite tarzı saldırıyı yeniden canlandırır
- Ardından daha büyük bir
- Kısaltılmış PoC, iki büyük chunk ayırır;
top[-1]içindeki size alanını manipüle ettikten sonrafree(top)ile bitişik bölgeye kadar unmap eder ve daha büyük bir allocation ileXverisini yeniden doldurur - Bu teknik CFI olsa bile çalışabilir ve önceden ASLR sızıntısı gerektirmez
- glibc’nin
msealentegrasyonunda planlanan VMA kümesi mühürlemesinin, eşlenmiş ikili kodu ve dinamik kütüphaneleri unmapping/remapping hilelerinden koruyarak bu saldırıyı otomatik olarak azaltması bekleniyor - Ek güçlendirme isteyen geliştiriciler, program ömrü boyunca genişlemeyecek veya unmap edilmeyecek
mmapallocation’larını seçici olarak mühürleyebilir
Gelecekteki kullanım alanı
mseal, Linux çekirdeğinde görece yeni bir azaltma özelliğidir ve henüz ele alınmamış başka kullanım örnekleri olabilir- glibc entegrasyonu tamamlanıp olgunlaştıkça syscall’un gereksinimlerine uygun iyileştirmeler devam edebilir
- Şu anda kullanılmayan
flagsparametresi için de gelecekte somut kullanım alanları netleşebilir - Gerçek yazılıma uygularken mühürlenecek belleğin ömrü ve değişiklik ihtiyacı birlikte değerlendirilmelidir
1 yorum
Hacker News yorumları
Çekirdek posta listesindeki hararetli tartışmada hangi itirazlar ve kaygılar vardı, içeriden biri özetlese iyi olurdu
Posta listesinin kendisi bazen fazla sert olabildiği için uzak duruyorum; baş ağrım zaten yeterince kötü
Mekanizmanın kendisi makul görünüyor ama böyle bir özelliğin çekirdekte hâlihazırda olmaması şaşırtıcı
Önerilen flag’lerden biri seal’ları yok saymayı mümkün kılıyordu; Linus da “bir yerde
munmapyapmayı engelleyip başka bir yerde seal’ı yok saymak mı istiyorsunuz?” tarzında tepki verdiSonrasında “bir kez seal edildiyse seal edilmiştir” ve “bazı yerlerin seal’a uyup rastgele başka yerlerin uymaması” gibi bir şeyin olamayacağını daha sert biçimde söyledi
Daha sonra seal’ların yok sayılamayacağını ve pazarlık konusu olmadığını, flag ile seal’ı yok sayma önerisi daha gelirse ignore list’e koyacağını bile söyledi
Theo de Raadt, Jeff Xu’ya gönderdiği e-postada
mimmutable()ilemseal()yaklaşımlarının farklı olsa da ikisinin de saldırganlara karşı belleği seal ederek Linux uygulamalarını daha güvenli hâle getirmeyi amaçladığı söylenince, “Sanırım mseal’i yalnızca Chrome için yapıyorsunuz” diye yanıt verdiBunun genel olarak uygulamalara pek uymayacağını düşündü; gerekçe olarak da fazla karmaşık olmasını ve
mimmutable()deneyimine göre uygulamaların bunu doğrudan ele almayıpexecve(), libc başlatması veld.sotarafından üstlenilmesini gösterdiTheo ve Linus’a katıldı;
mimmutable()ın temel fikrinin iyi olduğunu ama bunun bölünme ve uygulanma biçiminin berbat olduğunu düşündüBu sistem çağrısının nasıl kullanılacağını merak ediyorum
Chrome bunu istiyor, ama saldırgan farklı flag’lerle yeniden map edebileceği için seal edilmiş sayfalar unmap edilemiyor
O zaman çalışma zamanında ayrılan sayfalarda, bunları prosesin tüm ömrü boyunca tutmayı düşünmüyorsanız, pratikte kullanılamaz değil mi?
Öyleyse JS sandbox belleği gibi çok cazip hedeflere uygulamak zor olmaz mı?
Bu tür işler ayrı bir proseste çalıştırılıp bellek seal edildikten sonra iş bitince prosesin öldürülmesiyle mi çözülüyor, merak ediyorum
Chrome’un bellek ve proses yönetimini iyi bilmediğim için bunun neden sorun olmadığından emin değilim; bu özelliğin çoğu program için yararlı olmadığı sıkça söyleniyorsa sebebi de bu mu, merak ediyorum
Chrome’un bunu yaygın biçimde kullandığını biliyorum; namespace’ler üzerinden izolasyon gibi nedenlerle de ayrı proses zaten gerektiğinden yön bu olabilir
mseal()sonrası tartışma, 20 Ekim 2023: https://lwn.net/Articles/948129/mseal()yaklaşıyor, 19 Ocak 2024: https://lwn.net/Articles/958438/GNU C Library için bellek seal etme, 12 Haziran 2024: https://lwn.net/Articles/978010/
Modern x86_64 mimarisinde güvenli programlama ve bilgi işlemeye yardımcı olacak çok sayıda özellik varken işletim sisteminin böyle çağrılar uygulamak zorunda kalması üzücü
Eski miraslar ve düşünce biçimleri, bugünün dünyasına ve bilgisine uymayan paradigmalar üzerine kurulmuş eski sistemleri yamamaya çalışma tutumunun bilişimin ilerlemesini yavaşlattığını ve kelimenin tam anlamıyla milyarlarca insanı riske attığını düşünüyorum
Elbette bu değişikliğin doğru yönde bir adım olmadığını söylemeye çalışmıyorum; ama işletim sisteminin çalışması gerektiğine dair mevcut ideali bir kenara bırakıp bugünkü sistemleri, bilgiyi ve insanların sistemlerden ne istediğini dikkate alınca, bugün geliştiricilere ve kullanıcılara yüklenen yük ve risklerden arınmış sistemler hayal edebiliriz
Mimari hataların var olduğu doğru, fakat yazılım mevcut özelliklerden bile doğru dürüst yararlanmıyor; bu yüzden mimari hataları öne sürerek itiraz etmek asıl noktayı kaçırıyor
Temel sallantılıysa daha ucuz ihlal yöntemleri her zaman var olacaktır
msealın neden gerekçelendirilemediğini düşündüğünüzü ve onun yerine neyin daha iyi olacağını da merak ediyorumLD_PRELOADhilesiylemsealsistem çağrısının üzerine yazmak ya da onu devre dışı bırakmak mümkün mü?mseal, Linux’un mevcut bellek koruma yöntemlerinden farklı olarak, bellekteki hassas sırları çıkarmaya çalışan yerel saldırganlardan çok uzaktan kod çalıştırma saldırılarını hafifletmeye odaklanan bir sistem çağrısıdır.Uzak saldırgan yerel ortamı değiştirebiliyorsa, zaten sisteme sızmış sayılmalıdır.
LD_PRELOADile olmaz.Etkili olması için içe aktarılmış bir fonksiyon olması gerekir; ham sistem çağrıları bu şekilde yakalanamaz.
Linux sistem çağrılarını yakalama tartışması: https://stackoverflow.com/questions/69859/how-could-i-interc...
msealçalışıyormuş gibi yapan yamalı bir çekirdeği doğrudan oluşturmak, özelliği “devre dışı bırakmanın” en kolay yolu olabilir.Ancak
msealkullanan bir program bunun gerçekten çalışıp çalışmadığını doğrulayabileceği için, bozulmuş çekirdeğin uygulandıktan sonra uygulamanın kontrollerini engellemek üzeremsealı gizlice devre dışı bırakacak bir yönteme de ihtiyacı olur.Örneğin yürütülebilir dosyayı
ptraceile izleyip sistem çağrılarını gözetleyerekmseal(2)çağrısını atlatabilirsiniz.Bu sistem çağrısı, “saldırganın süreç başlatılmadan önce zaten erişim elde ettiği” tehdit modeli için tasarlanmış değildir.
msealçağrı sarmalayıcısının üzerine yazılabilir, ama sistem çağrısının kendisinin üzerine yazılamaz.Araştırınca, preload tabanlı sistem çağrısı üzerine yazma yöntemlerinin hepsinin sarmalayıcıyı değiştirdiği görülüyor; doğrudan sistem çağrısı yapılırsa üzerine yazılamayacak gibi.
Teknik olarak
syscallfonksiyonunun kendisinin üzerine yazmak mümkün olabilir.Meta: Yazıdaki
mseal()prototipinin düzenlenmesi gerekiyor.İlk argüman
unsigned start addrolarak gösterilmiş; muhtemelen doğrusuunsigned long start_addrolmalı.int mseal(unsigned long start, size_t len, unsigned long flags)“Memory Sealing ‘Mseal’ System Call Merged for Linux 6.10” (2024) https://news.ycombinator.com/item?id=40474510#40474551 başlığında CPython’ın
mseal()sistem çağrısını nasıl desteklemesi gerektiği daha önce tartışılmıştı.OpenBSD’de uzun zamandır bulunan bir özellik: https://man.openbsd.org/mimmutable.2
Bu kadar bariz bir özelliğin Linux’a neden ancak şimdi girdiğini merak ediyorum.
mimmutableözelliği, 10 Nisan 2023’te yayımlanan OpenBSD 7.3 ile tanıtıldı; yani “uzun zamandır” denemez.Buna karşılık Linux ve FreeBSD’de
memfd_createuzun zamandır vardı; OpenBSD’de ise anonim dosyalar olmadığı içinshm_open’a dayanıyor.