eBPF Linux çekirdeği güvenli açığının keşfi ve düzeltilme yöntemi
(bughunters.google.com)- Google, eBPF verifier içindeki CVE-2023-2163 açığını Buzzer ile keşfetti ve bu açığın yerel ayrıcalık yükseltme ile konteyner kaçışı için kötüye kullanılabileceğini doğruladı
- eBPF, çekirdek işlevlerini çalışma zamanında genişletir; ancak yüksek ayrıcalıklarla rastgele bytecode çalıştırdığı için yükleme öncesi verifier doğrulaması güvenliğin temel unsurlarından biridir
- Sorun, verifier içindeki path pruning mekanizmasının gerçekte farklı bir yürütme yolunu, daha önce güvenli olduğu görülen bir yolla eşdeğer sanmasından kaynaklandı
- Exploit, verifier'ın 0 olarak gördüğü bir register'ın çalışma zamanında farklı bir değere sahip olmasından yararlanarak eBPF stack pointer'ını bozuyor ve bu da rastgele okuma/yazma ile KASLR atlatmaya yol açıyor
- Yama, precise register'ı etkileyen imprecise register'ları da precise olarak işaretleme yaklaşımını benimsiyor; aynı pointer arithmetic fuzzing stratejisinde sonrasında ek bir sorun bulunmadı
eBPF verifier'ın genişlettiği çekirdek saldırı yüzeyi
- eBPF, karmaşık çekirdek modüllerine gerek kalmadan Linux çekirdeği işlevlerini çalışma zamanında genişletmeye imkân veren bir teknolojidir
- eBPF programları özel bytecode ile yazılır ve belirli bir olay gerçekleştiğinde çalıştırılmadan önce güvenlik doğrulamasından geçer
- Tipik bir örnek, belirli bir syscall çağrısında çalışan eBPF programıdır
- Yapı gereği yüksek ayrıcalık seviyesinde rastgele kod çalıştırabildiğinden çekirdeğin saldırı yüzeyi önemli ölçüde büyür
- Programlar yüklenmeden önce verifier'dan geçmelidir ve verifier, eBPF'nin güvenlik varsayımlarının karşılanıp karşılanmadığını denetler
- Verifier açıkları kötüye kullanıldığında sonuç genellikle yerel ayrıcalık yükseltme ya da konteyner ortamlarında konteyner kaçışı olur
Buzzer ve pointer arithmetic fuzzing
- Google, eBPF verifier kodunu otomatik olarak denetlemek için Buzzer'ı geliştirdi
- Buzzer, sözdizimsel olarak geçerli çok sayıda eBPF programı üreten bir fuzzer'dır ve mantıksal hataları tetiklemeye yönelik stratejiler tanımlayabilir
- CVE-2023-2163'ün bulunmasında pointer arithmetic strategy kullanıldı
- Register'ları rastgele değerlerle başlatan bir header üretir
- Rastgele arithmetic ve jump komutu dizileri üretir
- Rastgele bir register seçip eBPF map element pointer'ı ile toplama işlemi yapar
- İlgili element'e sihirli bir değer yazar
- Kullanıcı alanında yazılan değer gözlemlenmezse sınır dışı yazma gerçekleşmiş olabilir
- Buzzer'ın bulduğu CVE-2023-2163, eBPF path pruning mantığında yer alıyordu ve hem konteyner kaçışı hem de yerel ayrıcalık yükseltme için kullanılabilen bir exploit'e dönüştü
path pruning ve precise tracking hatası
- eBPF verifier, programın güvenli biçimde çalışıp çalışamayacağını doğrulamak için olası yürütme yollarını simüle eder
- Koşullu dallanmada register değerleri kesin değilse verifier, mümkün olan tüm durumları izlemeye çalışır
- Koşullu sıçramalar arttıkça yürütme yolu sayısı üstel olarak büyür ve bu da programın çekirdeğe yüklenme performansını olumsuz etkiler
- Bunu azaltmak için eBPF geliştiricileri path pruning yaklaşımını tanıttı
- Verifier, belirli bir durumla eşdeğer başka bir durumun daha önce güvenli biçimde
exitkomutuna ulaştığını garanti edebiliyorsa, o yolu daha fazla keşfetmez
- Verifier, belirli bir durumla eşdeğer başka bir durumun daha önce güvenli biçimde
- Daha verimli pruning için precise tracking kavramı da birlikte kullanılır
- Bir register pointer arithmetic işlemine katıldığında veya helper fonksiyona sabit olarak geçirildiğinde precise olarak işaretlenir
- Verifier, o register'ın dâhil olduğu tüm durumları keşfetmek zorundadır
- CVE-2023-2163'te, r6'nın doğruluğunu etkileyen r9 doğru şekilde işaretlenmemişti
- Verifier, r9'un r6'nın preciseness'ına katkıda bulunmadığını varsaydı
- Önceki yolda r6 ile güvenli biçimde exit'e ulaşılabildiğine karar verdikten sonra kalan durumları eşdeğer sayıp pruning uyguladı
- Çalışma zamanında ise 1:2:4:6 yolu yürütüldü ve 6. adımda verifier'ın 0 sandığı r6, gerçekte farklı bir değere sahipken pointer arithmetic işleminde kullanılabildi
Rastgele okuma/yazma ve KASLR atlatma
- Exploit kodu Google security research repository'sinde yayımlandı
- Exploit geliştirmede @chompie ve @_manfp çalışmalarından önemli ölçüde yararlanıldı
- Genel akış, önce rastgele okuma/yazma elde edip ardından süreç credential'larını bularak uid ile
fs_structpointer'ını yamalayıp ayrıcalıkları yükseltme şeklindedir - İlk aşamada bozulmuş register değeri 1'e dönüştürülür
- El ile yapılan analizde çalışma zamanındaki r6 değeri
0x400idi ve verifier bunu 0 olarak görüyordu r6 >>= 10komutuyla istenen 1 değeri üretildi
- El ile yapılan analizde çalışma zamanındaki r6 değeri
- Ardından
bpf_skb_load_bytes_relativehelper fonksiyonu kullanılarak eBPF stack içindeki pointer bozuldu- Verifier,
lendeğerini 8 olarak değerlendirirken gerçek çalışma zamanında r6 nedeniylelen9 oldu - Sonuç olarak 8 byte yerine 9 byte yazıldı ve stack offset
-32değerinin ilk byte'ı bozuldu
- Verifier,
- Bozulmuş stack pointer'ı manipüle ederek eBPF map pointer'ı sızdırılabiliyor
- Kullanıcı alanında R2 ve R3 değerleri okunuyor ve R2'nin
0xBACAolup olmadığı kontrol ediliyor - Bu koşul sağlanıyorsa R3, sızdırılan map pointer değeri oluyor
- eBPF map pointer sızıntısı, KASLR atlatılmış bir duruma götürüyor
- Kullanıcı alanında R2 ve R3 değerleri okunuyor ve R2'nin
- Aynı stratejiyle tek bir byte yerine ardışık stack alanının tamamı istenen pointer ile ezilirse rastgele okuma/yazma mümkün oluyor
- Verifier açısından bu bir BPF stack manipülasyonu gibi görünse de gerçekte çekirdek belleği okunup yazılabiliyor
map leak'ten root shell'e
- Map pointer sızıntısından sonra exploit, büyük ölçüde Chompie'nin exploit'inden farklı değil ve bazı kod bölümleri oradan alınmış
- Kabaca süreç şu şekilde işliyor
- kstrtab içinde
init_pid_nsdizgesi tekrar tekrar aranıyor - Bu dizgeye başvuran ksymtab sembolü bulunarak init_pid_ns yapısının adresi elde ediliyor
- Radix tree üzerinde dolaşılıp
commalanı çalışan exploit ikilisinin adıyla eşleşen entry aranıyor- Konteyner içinde çalışırken PID güvenilir bir sezgisel olmadığından yalnızca PID kullanılmıyor
- uid 0 olacak şekilde yamanıyor ve
fs_structpointer'ı değiştiriliyor fs_struct, PID 1'in işaret ettiği pointer ile aynı değere yamanırsa, konteyner içinde çalışırken host dosya sistemi görülebiliyorsystem("/bin/bash")çalıştırılarak root shell elde ediliyor
- kstrtab içinde
- GitHub'da yayımlanan kod, yalnızca belirli Linux sürümlerinde konteyner kaçışı olarak çalışıyor
- Ubuntu ve bazı dağıtımlarda üzerine yazılan veri yapısının offset'i farklı olduğundan yalnızca yerel ayrıcalık yükseltme olarak çalışıyor
- Bunun her Linux dağıtımında çalışması için kodda uyarlama yapmak gerekiyor
Yama ve sonraki doğrulama
- CVE-2023-2163'ün kök neden analizi ve yama bilgileri kernel mailing list'te görülebilir
- Düzeltme, precise register'ı etkileyen işlemlerdeki imprecise register'ları da precise olarak işaretleme yaklaşımına dayanıyor
- Bu düzeltmenin eBPF verifier performansını etkileyip etkilemediği net değil
- Aynı pointer arithmetic fuzzing stratejisi çalıştırılmaya devam edildi ancak ek bir sorun bulunmadı
- Buzzer geliştirilmeye devam ediyor ve açık kaynak topluluğunun katkıları GitHub repo üzerinden kabul ediliyor
1 yorum
Hacker News yorumları
eBPF’nin en yaygın kullanıldığı platformlarda, en başta yetkisiz kod eBPF programı yükleyemediği için doğrulayıcı hatalarının etkisi çoğu zaman büyük olmuyor.
Bu tür hatalar nihayetinde root → ring0 zafiyeti; bu önemsiz demek değil, ama sunucu tarafı iş yüklerinde genelde kabul edilebilir bir ödün.
Özellikle eBPF’nin kernel içi yerel yetki yükseltme geçmişi, kernel’in geneliyle karşılaştırıldığında oldukça iyi sayılır; mevcut eBPF ortamında doğrulayıcının en büyük değeri de hatalı eBPF programlarıyla kernel’i kazara çökertmeyi zorlaştırması.
Sıradan yüklenebilir kernel modülleri için bu söz gülünç derecede geçerli değil.
Bu durumda çoğu platformda yetki gerekmiyor.
Ayrıca yetkisiz kullanıcı namespace’leri varsa kişi kendi kendine “root” olabildiğinden root → ring0 da daha az kısıtlayıcı bir sorun haline geliyor.
Dağıtımlar bunu açıp çoğunlukla tekrar kapattığından beri çıkan eBPF hatası PoC’lerinde sürekli gördüğümüz akış bu.
Cilium gibi araçlar büyüdükçe, cap_bpf’ye sahip konteyner ortamlarına giriş sağlayan saldırı yolları giderek daha gerçekçi hale geliyor.
“Uno no es ninguno” kelime kelime “Bir, hiç değildir değil”, yani “One is not none” ifadesine daha yakın.
İspanyolcada çift olumsuzluğun gerçekte çift olumsuzluk olmadığı durumlar yaygındır.
Örneğin “burada hiçbir şey yok” ifadesi “no hay nada aquí” diye söylenir; kelime kelime çevrilirse “burada hiçbir şeyin olmadığı doğru değil” gibi görünür.
Royal Spanish Academy de bunu şöyle açıklar:
https://www.rae.es/espanol-al-dia/doble-negacion-no-vino-nad...
Sözde “çift olumsuzluk”, İspanyolca ve diğer Roman dillerinde belirli durumlarda zorunlu olan olumsuzluk uyumundan kaynaklanır; bunun sonucunda zarf olan no ile olumsuz anlam taşıyan başka unsurlar aynı cümlede birlikte görünür.
Bu iki “olumsuzluk” aynı anda bulunduğu için cümlenin olumsuz anlamı ortadan kalkmaz.
Eskiden eBPF’yi denediğimde, ihtiyacım olan işi yapmak için ifade gücü yetersizdi.
Sınırlı bir esneklik elde etmek uğruna kernel alanı karmaşıklığını artırmanın gerçekten haklı olup olmadığını sorguluyorum.
Paket filtreleme amacıyla kullanılmasını anlıyorum, ama sandboxing gibi başka amaçlarda kullanılması daha az ikna edici görünüyor.
Kernel’in seçeneği eBPF ya da hiçbir şey değil; eBPF ya da ona benzer başka bir şey.
Kendiniz çok kullanmıyor olabilirsiniz, ama bütün gün kullanan insanlar var.
FAANG mühendislerinin, tek seferlik kullanımlar hariç, her sunucuda her zaman bu programlardan onlarcasını, hatta belki yüzlercesini çalıştırdıklarını söylediklerini hatırlıyorum.
FAANG ayrıca özel kernel geliştiricileri de istihdam ediyor; dolayısıyla kullandıkları bu karmaşıklığı finanse etmiş oluyorlar.
Ben de eBPF ile sorun çözdüm.
eBPF olmadan kernel uzmanı olmayan biri için fiilen çözülemez olan sorunlardı; sık sık gerekmiyor ama gerektiğinde yerine geçecek bir şey yok.
Bazı durumlarda kernel uzmanı için bile seçim eBPF kullanmak ile özel kernel yamasını sonsuza dek sürdürmek arasında.
Güvenli olmadığını biliyorum, ama bunlarla uğraşan insanlar büyük gücün sorumluluk getirdiğinin farkında.
“Uno no es ninguno” ifadesinin “One is not none” olarak çevrilmesi doğru gibi görünüyor.
https://bughunters.google.com/blog/6303226026131456/a-deep-d...
Ama öyle değil, “One is none” diye çevrilmiş.
Ben dahil yabancı dil konuşanların zorlandığı, kötü şöhretli çift olumsuzluk meselesi bu.
https://spanish.stackexchange.com/questions/26777/how-does-d...
Kelime kelime bakınca öyle, ama İspanyolcada tuhaf biçimde çift olumsuzluk genelde sadece olumsuzluk olarak kullanılıyor.
“one ain't nothin'” gibi çevirmek belki daha doğru olabilir.
Bizim ülkede “pantolonun içindeki kirpi” diye bir deyim var.
Ne kadar kullanışlı olursa olsun, bunun güvenli ve dikkatli yazılmış gibi göründüğünü sanmıyorum.