Konteyner kaçışı: ayrıcalıklı mod ve bağlar
- Çoğu konteyner kaçışı bir çekirdek açığından değil, ayrıcalıklı mod ya da Docker soketi gibi gevşek bırakılmış bir yapılandırmadan doğar.
- Ayrıcalıklı mod, Docker soketi ve ana makine bağları üç klasik kapıdır.
- Savunma yetenek düşürme, salt okunur kök ve kullanıcı ad alanı eşlemesidir.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +60 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
Konteyner kaçışı kulağa egzotik gelir. Uygulamada ise çoğu kaçış, birinin işini kolaylaştırmak için verdiği bir bayraktan doğar. Çekirdek açığına gerek kalmaz. Bu derste kaçışın klasik kapılarını, savunmanın dört ayarını ve çalışma zamanı izlemesini görürsün.
Amaç, yalıtımın nerede inceldiğini görmek ve o noktayı kapatmaktır. Sonunda gerçek bir çalışma zamanı açığının nasıl kapandığını da incelersin. Denemeleri yalnız kendi lab ortamında ya da yazılı iznin olan bir hedefte yaparsın.
Kaçışın klasik kapıları
İlk kapı ayrıcalıklı moddur. Bu mod konteynere tüm yetenekleri verir, ana makinenin cihaz düğümlerini görünür yapar ve varsayılan sistem çağrısı süzgeci ile zorunlu erişim profillerini devre dışı bırakır. Bu modla çalışan bir konteyner ana makineden ayrılmış sayılmaz.
İkinci kapı Docker soketinin konteynere bağlanmasıdır. Soketi gören süreç yeni konteyner açabilir; ana makinenin kök dizinini bağlayan bir konteyner açarsa, ana makinenin dosya sistemine kök olarak erişir. Üçüncü kapı ana makine bağlarıdır: /etc dizinini ya da bir çekirdek arayüzünü konteynere bağlamak yalıtımı o yoldan deler.
Etkinlik 1 / 8
Bu compose dosyasında ana makineyi açan satırı bul.
Paylaşılan ad alanları da benzer etki yapar. Ana makinenin süreç ad alanını paylaşan bir konteyner düğümdeki tüm süreçleri görür. Tehlikeli yeteneklerin verilmesi de aynı kapıya çıkar.
Konteyner varsayılan olarak on dört yetenekle başlar; dosya sahipliği değiştirme, ayrıcalıklı portu bağlama ve süreç sinyali gönderme gibi sınırlı işler bunların arasındadır. Bu varsayılan liste çoğu uygulama için bile fazladır. SYS_ADMIN gibi geniş bir yetenek eklendiğinde ise iş değişir. Bu tek yetenek bağlama, ad alanı ve çekirdek yapılandırması işlemlerinin çoğunu açar ve yalıtımı pratikte zayıflatır.
Şema bu zinciri uçtan uca gösterir. Uygulamadaki bir açık, konteyner içinde kod çalıştırmaya dönüşür. Bunun ardından gevşek bırakılmış bir bayrağın keşfi ana makineye çıkışa dönüşür; ana makineye erişim ise düğümdeki diğer iş yüklerine sıçrar.
Her ok, savunmanın bir öncekini durdurursa zincirin orada kesildiğini de gösterir. MITRE bu davranışı iki teknikle adlandırır: ana makineye kaçış T1611, kötü niyetli konteyner dağıtımı T1610. Kırmızı ve mavi taraf aynı şemayı okur, yalnız amaçları değişir.
Aynı riskleri Kubernetes tarafında farklı adlarla görürsün. Ayrıcalıklı mod pod güvenlik bağlamında privileged alanıdır. Ana makine bağı hostPath birimidir.
Paylaşılan süreç ad alanı hostPID alanıdır. Adlar değişse de kapı aynıdır: hepsi konteyner ile ana makine arasındaki sınırı bir yerinden deler. Pod Güvenlik Standartları'nın temel profili bile bu alanların hepsini yasaklar. Bir pod tanımında bunlardan birini görmek, gözden geçirmede durup gerekçe sormak için yeterlidir.
Etkinlik 2 / 8
Her yapılandırmayı kaçış riskine göre ayır.
Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.
0/6 seçenek yerleşti.
Bulunduğun yeri tanı
Kendi laboratuvarında bulunduğun yeri anlamak ilk adımdır. Kontrol grubu yolundaki konteyner izleri ve ortam işaret dosyası, konteyner içinde olduğunu söyler. Çekirdek sürümü ise ana makineyi yazar, çünkü çekirdek paylaşılır; bu yüzden uname çıktısına bakmak konteyner ile ana makineyi ayırmaz.
Sonraki adım verilmiş yetenekleri saymaktır. Yetenek listesi genişse ve içinde SYS_ADMIN varsa, yalıtım zayıftır. Bu bilgi savunma tarafında hangi bayrağın kaldırılacağını doğrudan söyler.
Etkinlik 3 / 8
Kendi laboratuvarında bir konteynerin içinde olup olmadığını anlamanın basit yolu nedir?
Bu keşif adımları savunma için de değerlidir. Bir konteynerin hangi yeteneklerle ve hangi bağlarla çalıştığını görmek, o iş yükünün gereğinden fazla yetki taşıyıp taşımadığını söyler. Çoğu uygulama ne ayrıcalıklı moda ne de geniş bir yetenek listesine ihtiyaç duyar. Buna rağmen kolaylık olsun diye açık bırakılmış ayarlar denetimlerde sık çıkar.
Yetenekleri saymak için capsh --print komutunu kullanırsın. Komut sürece verilmiş yetenekleri tek tek listeler, yalnız okur ve hiçbir şeyi değiştirmez. Listede beklenenden fazlası varsa, savunma tarafında o yetenekleri düşürmek ilk iştir.
$ capsh --print Current: cap_chown,cap_dac_override,cap_fowner,cap_net_bind_service,cap_sys_admin=ep Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_sys_admin,... Securebits: 00/0x0/1'b0
Etkinlik 4 / 8
Kendi labındaki konteynerde sürece verilmiş yetenekleri listele.
Savunmanın dört ayarı
Savunma dört ayarla kurulur. Tüm yetenekleri düşür ve yalnız gerekeni geri ver. Kök dosya sistemini salt okunur yap. Yetki yükseltmeyi kapat, yani konteyner içinde bir sürecin yeni ayrıcalık kazanmasını engelle.
Kullanıcı ad alanı eşlemesini aç. Bu dördü birlikte, kod çalıştırma elde eden bir saldırganın önündeki adımların çoğunu kapatır. Kullanıcı ad alanı eşlemesi en güçlü katmandır: konteynerdeki kök, ana makinede yetkisiz bir kimliğe karşılık gelir. Kaçış olsa bile eline kök geçmez.
Yetki yükseltmeyi kapatmak ayrı bir ayardır. Bir süreç, üstünde setuid biti olan bir ikiliyi çalıştırarak yeni ayrıcalık kazanabilir. no-new-privileges bayrağı bunu engeller: süreç, çalıştırdığı ikililer yoluyla başladığı yetkiden fazlasını alamaz.
Kubernetes tarafında bu ayar allowPrivilegeEscalation: false alanıdır. Bu ayarları tek tek hatırlamak yerine Pod Güvenlik Standartları'nın kısıtlı profilini uygularsın. Bu profil kök olmama, yetki yükseltmeyi kapatma ve yetenek düşürme gibi maddeleri bir arada zorunlu kılar.
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefaultBu bağlamı satır satır oku. runAsNonRoot ve runAsUser süreci kök olmayan sayısal bir kimlikle başlatır. allowPrivilegeEscalation: false, setuid bitli bir ikilinin sürece yeni ayrıcalık vermesini engeller. readOnlyRootFilesystem kök dosya sistemini salt okunur yapar; yazılması gereken dizin için ayrı bir emptyDir birimi bağlarsın.
capabilities altındaki drop listesine yazılan ALL, varsayılan on dört yeteneğin hepsini düşürür. seccompProfile ise çalışma zamanının varsayılan sistem çağrısı süzgecini açar. Ad alanına kısıtlı profili zorunlu kılan etiketi koyduğunda, bu alanları taşımayan pod kümeye alınmaz.
Etkinlik 5 / 8
Kendi laboratuvarındaki test konteynerini tüm yetenekleri düşürerek ve salt okunur kökle başlat.
Sistem çağrısı süzgeci ve kimlik eşlemesi
Sistem çağrısı süzgeci de eklenir. Çekirdek açıklarının çoğu belirli bir çağrıdan geçer ve uygulaman o çağrıları hiç kullanmaz. Docker'ın varsayılan seccomp profili, üç yüzü aşkın sistem çağrısından yaklaşık kırk dördünü kapatır.
Ayrıcalıklı mod bu profili devre dışı bıraktığı için, ayrıcalıklı bir konteynerde bu koruma da yoktur. Kubernetes tarafında varsayılan profil çoğu kümede kendiliğinden açık gelmez; pod'da seccompProfile alanına RuntimeDefault yazarak açarsın. Uygulamana özel, daha dar bir profil de yazabilirsin, ama önce hangi çağrıları kullandığını ölçmen gerekir.
Etkinlik 6 / 8
seccomp profili ne yapar?
Ad alanı eşlemesi çalışma zamanında da kümede de kurulur. Docker tarafında daemon.json içine "userns-remap": "default" yazılır; çalışma zamanı bir dockremap kullanıcısı oluşturur ve konteynerdeki kimlikleri ana makinede yüksek numaralı ve yetkisiz bir aralığa eşler. Örneğin konteynerdeki sıfır numaralı kullanıcı ana makinede 231072 numaralı kimliğe düşer. Kubernetes tarafında aynı iş pod'da hostUsers: false alanıyla açılır; bu özellik v1.36'dan beri kararlı sürümde gelir ve Linux 6.3 ile uyumlu bir çalışma zamanı ister.
Çok kiracılı ortamlarda bir katman daha eklenir. Kum havuzlu çalışma zamanları, konteyner ile çekirdek arasına ince bir ara katman koyar. Konteynerin yaptığı sistem çağrıları önce bu katmandan geçer, çekirdeğe doğrudan ulaşmaz. Böylece bir çekirdek açığının konteynerden ana makineye sıçraması zorlaşır.
Bedeli küçük bir başarım kaybıdır, bu yüzden her iş yükünde değil, güvenilmeyen kodun koştuğu ortamlarda tercih edilir. Zorunlu erişim profilleri de benzer bir rol oynar: bir sürecin hangi dosyalara ve aygıtlara erişebileceğini çekirdek düzeyinde sınırlar. Bu katmanlar birbirinin yerine geçmez; her biri farklı bir kaçış yolunu kapatır ve bir arada kullanılır.
Etkinlik 7 / 8
Doğru mu, yanlış mı?
Kullanıcı ad alanı eşlemesi açıkken konteyner içindeki kök, ana makinede yetkisiz bir kimliğe karşılık gelir.
İzleme ve gerçek olay
Çalışma zamanı izleme bu noktada devreye girer. Konteyner içinde beklenmedik bir kabuk açılması, paket yöneticisinin çalışması ya da hassas bir dosyanın okunması iyi sinyallerdir. Uygulama neyi çalıştıracağı belli olduğu için, beklenenin dışına çıkan her süreç dikkat çeker. Falco gibi araçlar tam bu iş için kural taşır.
Konteyner içinde terminale bağlı bir kabuk açılması, konteynerden API sunucusuna erişim ya da özel anahtar ve parola araması hazır kurallardır. Kaynak sınırları da unutulmaz. Bellek ve işlemci sınırı olmayan bir konteyner, düğümdeki diğer iş yüklerini aç bırakabilir; bu bir kaçış değildir ama hizmet dışı bırakma etkisi yaratır.
Falco'nun sinyalleri okunabilir bir biçimde gelir. Bir kural tetiklendiğinde çıktı, olayın türünü, çalışan sürecin adını ve yolunu, kullanıcı kimliğini ve komut satırını yazar. Böylece bir alarmı ele aldığında hangi konteynerde, hangi sürecin, hangi komutu çalıştırdığını doğrudan görürsün.
Konteyner çalışırken beklenmedik bir dosyanın yazılması da bir sinyaldir. İmaj değişmez olduğu için, çalışma zamanında dosya sistemine eklenen her yeni ikili şüphelidir. Uyarıları merkezî kayıt sistemine gönderirsen, aynı konteynerden gelen diğer olaylarla tek bir zaman çizelgesinde birleştirirsin.
Bilinen bir olay bu dersi somutlaştırır. 2024 başında açıklanan Leaky Vessels açıklarından CVE-2024-21626, çalışma zamanı runc'ta bir dosya tanıtıcısının sızmasından doğuyordu. Konteynerin çalışma dizini sızmış bir tanıtıcıya ayarlandığında süreç ana makinenin dosya sistemine ulaşabiliyordu.
Açık runc 1.1.12 ile kapandı. Aynı açıklama grubundaki derleme aracı açıkları da ayrı yamalarla giderildi. Daha eski CVE-2019-5736 ise ana makinedeki runc ikilisinin üzerine yazılmasına yol açıyordu. İkisinin de dersi aynı: çalışma zamanını güncel tut ve kaçış denemesini çalışma zamanı izlemesiyle yakala.
Etkinlik 8 / 8
CVE-2024-21626 (Leaky Vessels) gibi bir çalışma zamanı kaçış açığı hangi savunmayla kapatılır?
Derinleş:Neden ayrıcalıklı mod bu kadar geniş
Ayrıcalıklı mod tek bir bayrak gibi görünse de aslında birkaç korumayı birden kaldırır. Önce konteynere Linux yeteneklerinin tümünü verir; varsayılan on dört yerine, ana makine yönetiminin tüm yeteneklerine sahip olursun. Sonra ana makinedeki cihaz düğümlerini konteynere görünür kılar, yani disk bölümlerine ve çekirdek arayüzlerine doğrudan yol açılır.
Üstüne varsayılan seccomp profilini ve zorunlu erişim profillerini kapatır, böylece sistem çağrısı süzgeci devre dışı kalır. Bu üçü bir aradayken konteyner, ana makinede kök çalışan bir süreçten pratikte ayırt edilemez. Bir cihaz düğümünü bağlayıp ana makinenin diskini okumak ya da bir çekirdek modülü yüklemek mümkün olur. Bu yüzden ayrıcalıklı mod, gerçekten donanıma erişmesi gereken çok dar bir grup iş yükü dışında hiç açılmaz; açıldığında da o konteyner ana makineyle aynı güven sınırında sayılır.
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.
- Etkinlik 1· Kaçışın klasik kapıları
- Etkinlik 2· Kaçışın klasik kapıları
- Etkinlik 3· Bulunduğun yeri tanı
- Etkinlik 4· Bulunduğun yeri tanı
- Etkinlik 5· Savunmanın dört ayarı
- Etkinlik 6· Sistem çağrısı süzgeci ve kimlik eşlemesi
- Etkinlik 7· Sistem çağrısı süzgeci ve kimlik eşlemesi
- Etkinlik 8· İzleme ve gerçek olay