Docker Escape
Last updated
Last updated
Nedir?
Bir kullanıcının veya sürecin sahip olduğu özel izinlerdir. Bu izinler, tipik kullanıcı izinlerinin ötesinde, özel işlemler için ayrıcalıklar sağlar.
Örnek
Birkaç tane örnek yetenek ve açıklamasını alt tarafta bulabilirsiniz:
CAP_CHOWN
: Dosya sahipliğini değiştirme yeteneği.
CAP_NET_BIND_SERVICE
: 1024'den düşük portlara bağlama yeteneği.
CAP_SYS_CHROOT
: Chroot sistem çağrısını kullanma yeteneği.
Keşif
Tamam ben yetenekleri anladım, bunları nasıl bulurum?
Bunun için capsh kullanabiliriz. capsh, linux yeteneklerini incelemek ve yönetmek için kullanılan bir araçtır.
Eğer capsh zafiyet aldığımız makinede yüklü ise direkt capsh --print
yazarak bütün sahip olduğu yetenekleri keşfedebiliriz.
Eğer yüklü değil ise cat /proc/$PID/status
dizini altında bütün işlemler hakkında bilgiler yer almakta bu kısımdan yetenekler hakkında bilgi alabiliriz.
Üstteki resimde bir işlemin yeteneklerini görebilirsiniz. Bunu okunabilir bir halde görmek için yine capsh kullanıyoruz. capsh --decode=blob
yazarsak ve yukarıda mesela CapEff kısmındaki yeri blob kısmına yazarsak bize yine üstteki gibi bir çıktı verecektir.
Buradaki CapEff CapInh kısımlarına biraz açıklama getirelim.
CapInh (Inherited Capabilities):Ebeveyn süreçten geçen yetenekleri belirler.
CapPrm (Permitted Capabilities): Bir sürecin sahip olabileceği maksimum yetenek kümesini tanımlar. En fazla bu kadar yeteneğe sahip olabilirsin gibi.
CapEff (Effective Capabilities): Bir sürecin her an kullandığı gerçek yetenekleri temsil eder.
CapBnd (Bounding Capabilities): Bir sürecin sahip olabileceği en üst düzey kapasiteleri sınırlayan değerleri temsil eder.
CapAmb (Ambient Capabilities): Bu, genellikle işlem tarafından kullanılabilecek olan, ancak şu anda doğrudan kullanılmayan yetenekler kümesidir.
Dikkat Etmemiz Gereken Örnek Yetenekler
SYS_MODULE
: Yeteneği, kernel modüllerini yükleyip kaldırma yetkisini içerir.
SYS_PTRACE
: Yeteneği, ptrace() adlı sistem çağrısını kullanma izni sağlar. Bu sistem çağrısı, bir işlemin yürütülmesini izlemesine ve kontrol etmesine olanak tanır.
SYS_ADMIN
: Yeteneği, bir Linux işletim sisteminde kernel düzeyinde çok çeşitli ayrıcalıkları içeren bir yetenektir.
Tabiki bu kadar az değil bir çok yetenek var dikkat edilmesi gereken. Burada birkaç tane örnek vermek istedim.
Nedir?
Linux tabanlı bir dosya sistemi teknolojisidir. İki veya daha fazla dosya sisteminin birleştirilmesine izin verir.
UpperDir ve LowerDir adı verilen iki ana dizinden oluşur. UpperDir, birleştirilen dosya sisteminde yapılan değişiklikleri içerirken, LowerDir temel dosya sistemini temsil eder. OverlayFS, bu dizinleri birleştirerek birleşik bir dosya sistemini oluşturur ve üst dizinde yapılan değişiklikleri alt dizine uygular.
Bu kısımda asıl öğretmek istediğim Docker'ı çalıştıran Host'un bir path yardımı ile konteyner'daki dosyalara erişebilmesidir.
Uygulama
Alt tarafta yavuzlar adında bir konteyner açıyorum ve bir dosyaya yazı yazıyorum.
Burada docker inspect yavuzlar
kullanarak UpperDir i alıyorum. Ardından cat ile o oluşturduğum dosyayı okumaya çalıyorum ve komut çalışıyor. Burada host makinede olduğumuza dikkat edelim.
Burada docker inspect yavuzlar
yazarak UpperDir pathini almayı başardık. Peki bunu docker escape yaparken nasıl elde edeceğiz. Bunun için alttaki komutu kullanacağız.
Nedir?
Bir işletim sistemi çekirdeği özelliğidir ve bir sistemdeki çeşitli sistem kaynaklarını izole etmek için kullanılır. Alt tarafa namespace'ler var.
PID
User
Mount
Net
UTS
IPC
CGroup
Nedir?
Linux işletim sisteminde kaynak yönetimi ve sınırlama sağlayan bir mekanizmadır. CGroups, sistem üzerinde çalışan işlemlere kaynak tahsisi yapmak, kaynakları izlemek ve sınırlamak için kullanılır. Bu sayede sistem yöneticileri, işlem gruplarına öncelik ve sınırlar belirleyerek sistem kaynaklarını etkin bir şekilde kullanabilirler.
Kullanıldığı Yerler
Kaynak Sınırlama (Resource Limitation)
Öncelik Belirleme (Prioritization)
İzleme ve İstatistikler (Monitoring and Statistics)
Önceden Tanımlanmış Profiller (Pre-defined Profiles)
Nedir?
Linux çekirdeğine eklenen güvenlik altyapısını ifade eder. Bu modüller, çekirdek seviyesinde güvenlik politikalarını uygulayarak sistem güvenliğini artırmaya yardımcı olur.İki yaygın LSM örneği şunlardır: AppArmor ve SELinux.
AppArmor
Hem uygulama hem de işletim sistemi düzeyinde güvenlik politikalarını uygular. Profil kullanarak bu işlemleri gerçekleştirir. Documentation
SELinux
Kullanıcıların, süreçlerin ve nesnelerin arasındaki erişimi daha ayrıntılı bir şekilde kontrol eder.
Not
Docker, enforcement modunda varsayılan bir Linux Security Module (LSM) profili etkinleştirir, bu da çoğunlukla bir konteynerın hassas /proc ve /sys girişlerine erişimini kısıtlamaya hizmet eder.
Bu profil aynı zamanda mount çağrısını da engeller.
Nedir?
Docker konteynerların kısıtlı izolasyon ortamından kaçarak (escape ederek) host sistemine erişim sağlamaya çalışan bir güvenlik açığı veya saldırı türünü ifade eder.
Docker İçinde Olup Olmadığımızı Nasıl Anlarız?
cat /proc/1/cgroup
yazdığımızda içerisinde docker görürsek büyük ihtimalle docker içerisindeyizdir.
Docker'a özgü işlemlere bakılabilir.
Bundan sonra artık örnekler üzerinde anlatacağım.
Ilerideki öğrenmek için bilmemiz gereken bir kaç terimi alt tarafta açıklayacağım.
RunC Nedir?
Konteynerleri çalıştırmaya yönelik görevleri ele almak üzere kullanılır - bir konteyner oluşturma, mevcut bir konteynere bir işlem bağlama (docker exec) ve benzeri işlemler.
/proc Nedir?
Linux işletim sistemlerinde kullanılan bir sanal dosya sistemidir. Bu sanal dosyalar, sistemde çalışan işlemlerin bilgilerinden, donanım kaynaklarının durumundan, ağ istatistiklerinden ve diğer çeşitli sistem bilgilerinden sorumlu olan çekirdek modülü tarafından düzenli olarak güncellenir.
/proc/self Nedir?
Çalışan process’in dizinine işaret eden sembolik bir bağlantıdır.
/proc/self/exe Nedir?
Process’in çalıştırdığı yürütülebilir dosyaya işaret eden sembolik bir bağlantıdır.
/proc/self/fd Nedir?
Process tarafından açılan dosya tanımlayıcılarını (file descriptors) içeren bir dizindir.
file descriptors: Bir sürecin açık dosyalar veya giriş/çıkış kaynaklarına olan erişimini belirtir. Dosya açma, okuma veya yazma gibi işlemler yapılabilir.
Şimdi zafiyetin mantığına geçebiliriz
Exploit kodu çalıştığında, ilk olarak /bin/bash binary'sini #!/proc/self/exe ile değiştirir. Bu, eğer docker exec id bash
komutu yazılırsa, runC konteynerine giriş yapacak ve bash'ı çalıştıracaktır. Ancak bash binary'si, #!/proc/self/exe olarak değiştirildiği için bu çalışan işlemin yürütülebilir dosyasını gösterdiğinden dolayı, exploit kodu gidip hostta runC'yi başlatacaktır.
Tam bu anda kodun devamında runC işleminin PID sini ve File Descriptor'u yakalayacaktır. Bu fd'ı da overwrite fonksiyonuna yolluyor. Bu fonksiyon ise runC binarysini değiştirip içine reverse shell ekleyecektir.
Bunları yaptıktan sonra konteyner içinde nc -lvnp 4444
gibi dinlemeye alırsak host tekrardan docker exec id herhangi-komut
çalıştırdığında konteyner'a host shell'i gelmiş olacaktır.
Bu başlık altında kullanıcı tarafından oluşan zafiyetlere örnek vereceğiz.
Docker Socket: Docker konteyner’larda kullanıcı modu ve kernel modu arasında iletişimi sağlar.
Zafiyetli Makina: docker run -it -v /var/run/docker.sock:/var/run/docker.sock exposedsocket
Buradaki sıkıntı host'un docker.sock dosyası konteyner içine eklenmiş yani konteyner içindekiler bu docker.sock u kullanarak docker ile etkileşime geçebilir.
Bu sıkıntıyı exploit etmek için curl
ve socat
tool'larını kullanacağız. Ilk olarak ls /var/run/
yazarak gerçekten docker.sock dosyası var mı yok mu bakalım.
Şimdi bu exploitte amacımız kendi konteyner'ımızdan çıkıp kendimiz için yüksek yetkide başka konteyner açmak ve oraya geçmektir. Ilk olarak zafiyetli konteyner oluşturmak için bir json yazalım. Bu json ile yeni konteyner'ı oluşturacağız.
Bu alttaki makine --privileged ile başlatılmış ve görüldüğü üzere host'un dosyaları konteyner içine eklenmiş. Aslında direkt bu makineye geçtiğimizde host dosyalarına erişebildiğimiz için direkt escape işlemini tamamlamış oluyoruz.
curl -XGET --unix-socket /var/run/docker.sock http://localhost/containers/json
: Komutu ile hostta çalışan bütün konteyner'ları listeleyebiliyoruz. Burada --unix-socket 'e kullanmasını istediğimiz socket'i veriyoruz.
curl -XPOST -H "Content-Type: application/json" --unix-socket /var/run/docker.sock -d "$(cat container.json)" http://localhost/containers/create
: Komutu ile üstte yazdığımız json'u kullanarak zafiyetli konteyner'ı oluşturuyoruz. Alttaki gibi bir çıktı alacaksınız. Burada ilk Id kısmı işimize yaracaktır.
curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/<CID>/start
: Komutu ile CID kısmına yukarıdaki Id nin ilk 4 karakterini yazarak oluşturduğumuz konteyner'ı çalıştırıyoruz.
Bütün bunları yaptıktan sonra zafiyetli çalışan bir konteyner'ımız var. Şimdi yapmamız gereken buna bağlanmak. Bunun için socat kullanacağız.
socat - UNIX-CONNECT:/var/run/docker.sock
: Yazarak docker.sock a bağlanıyoruz. Bu bağlantının içinde alttaki http requestini yazarak kendimize TCP bağlantısı alıyoruz.
Shell geldiğinde cd /host/home
gidersek host'un dosyalarını bulabilirsiniz.
Bu örnekte bir konteyner'a aşırı yetenek eklendiğinde çıkan sorunlardan birini işleyeceğiz. Burada fazladan SYS_MODULE yeteneği eklenmiş ve bunun ne işe yaradığını üst tarafta anlattık.
Zafiyetli Makina: docker run -it –rm –cap-add SYS_MODULE sysmodule
Buradaki sıkıntı yine dediğim gibi SYS_MODULE yeteneğinin konteyner'a eklenmiş olmasıdır.
Requirements
build-essential
linux-headers-$(uname -r)
net-tools
netcat
nano (optional)
kmod
Exploit
Bu exploit'i yaparken bize iki adet dosya lazım. Biri revshell.c diğeri de Makefile
Altta revshell.c nin içeriğini görebilirsiniz
Bu da bizim makefile dosyamız olacaktır.
Zafiyetli makineye girdiğimiz de ilk olarak kendi ip'mizi ifconfig
ile alıp revshell.c nin içerisine atmaktır. Arından zafiyetli makinede başka bir shell alıp orada nc -lvnp 4444
ile dinlemeye başlamaktır.
Bundan sonra gidip ilk olarak make
atacağız ki modülümüz derlensin. Arından insmod revshell.ko
yazarak modülümüzü yüklüyoruz. Bunların hepsini doğru yapılırsa dinleme yaptığımız yere shell gelmiş olacaktır.
core_pattern: Linux işletim sistemi çekirdeğinde çökmelerde oluşan çekirdek döküm dosyalarının adını ve konumunu belirleyen bir sistem ayarıdır.
Bu zafiyet core_pattern dosyası sayeside vardır. Bu zafiyet için ya makinede privilege escalation yapmamız gerekiyor. Ya da düzgün apparmor'un kapalı olması gerekiyor
Requirements
net-tools
nano (optional)
netcat
kmod
gcc
Exploit
Zafiyetli Makina: docker run -it --rm --cap-add=SYS_ADMIN --secuity-opt apparmor:unconfined corepattern bash
Burada ilk olarak crash.c diye bir c kodumuz olucak. Bu kodun tek amacı bize Segmantation Fault vermesi olucak. Bu kod sayesinde bizim core_pattern tetiklenecek.
Birde shell.sh kodumuz olucak bu bildiğimiz basit bir reverse shell kodu. Alttaki gibidir.
Son olarak escape.sh kodumuz olucak bu kod sayesinde kaçma işlemini yapacağız.
Şimdi kodu anlatalım. Yukarıda anlattığımız overlay pathini ilk satırda alıyoruz. Bu path yardımı ile host'un bizim shell.sh dosyamızı çalıştırmasını sağlayacağız.
Host'un proc'larını mount etmek için yeni klasör oluşturuyoruz ve oraya mount ediyoruz. Bu şekilde hostun proc dosyalarını konteyner içinde görebilir, değiştirebiliriz.
Bu dosyaların içinden /sys/kernel da bizim core_pattern dosyamız bulunmakta bu dosyanın içine echo "|$overlay/shell.sh" > core_pattern
ile shell.sh dosyasının path'ini belirtiyoruz. Arından 6 saniye bekleyip /crash dosyasını çalıştırıyoruz.
/crash dosyası Segmantation Fault hatası verince core_pattern dosyası okunuyor, okunurken bizim path'imi ve başıdaki | ı görüp o dosyayı çalıştırıyor. Bu da bize host'a bağlantı veriyor.
Tabiki escape.sh'ı çalıştırmadan önce nc -lvnp 4444
ile dinlemeye almalıyız.