İçeriğe geç
Sedat Özdemir
Yazılar

container-security

Konteyner İzolasyonunun İllüzyonu: docker.sock Üzerinden Host'u Ele Geçirmek

Konteynerlar bizi kurtaracak sandık ama aslında saldırganlara yepyeni bir oyun alanı açtık. İzolasyonun bittiği ve host sistemin başladığı o ince çizgiyi konuşuyoruz.

Sedat Özdemir
· 3 dk okuma

curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/create -d '{"Image":"ubuntu", "HostConfig":{"Binds":["/:/host"]}}' komutunu çalıştırdığında geri dönüş geliyorsa, geçmiş olsun; artık o makinenin sahibi sen değilsin, saldırgan.

Selamlar dostlar, bugün konteyner dünyasının o çok güvenilen ama aslında pamuk ipliğine bağlı olan 'izolasyon' kavramını masaya yatırıyoruz. Çoğu kişi Docker veya Kubernetes kullandığında uygulamasının sihirli bir fanus içinde olduğunu sanıyor. Spoiler: Değil. Eğer doğru yapılandırmadıysan, o fanus aslında saldırganın içeri girmesi için tasarlanmış bir Truva atı.

Docker Socket: Krallığın Anahtarı

Bir Red Team operasyonunda bir konteynerın içine sızdığımızda ilk baktığımız şey /var/run/docker.sock dosyasının orada olup olmadığıdır. Neden mi? Çünkü bu dosya Docker Daemon ile konuşan Unix soketidir. Eğer bir geliştirici, izleme (monitoring) araçları çalışsın veya CI/CD süreçleri kolaylaşsın diye bu soketi konteynerın içine mount ettiyse, bize root yetkilerini altın tepside sunmuş demektir.

Senaryoyu düşün: testCompany bünyesinde bir mikroservis yazdın. Konteynerın içinde düşük yetkili bir kullanıcıyla çalışıyorsun. Harika! Ama birisi gelip o soketi içeri bağladı. Saldırgan şu komutla host sistemin tüm dosya sistemini kendi üzerine mount eden yeni bir konteyner ayağa kaldırabilir:

# Zararsızlaştırılmış (Defanged) Örnek: Host dosya sistemini ele geçirme
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/create \
-H "Content-Type: application/json" \
-d '{
  "Image": "alpine",
  "Cmd": ["/usr/bin/tail", "-f", "/dev/null"],
  "HostConfig": {
    "Binds": ["/:/host_root"]
  }
}'

Buradan sonra chroot /host_root dediğin an, ana işletim sistemindesin. /etc/shadow elinde, SSH anahtarları elinde. İzolasyon? O sadece bir kelime olarak kaldı.

Privileged Mod ve Capabilities: Yetki Zehirlenmesi

Bazı arkadaşlar "Abi uygulama çalışmıyor, privileged: true yapalım düzeliyor" diyor. Bu, 'evde sigorta atıyor diye ana şalteri devre dışı bırakmakla' aynı şey. Privileged bir konteyner, host üzerindeki tüm capabilities (yeteneklere) sahip olur.

Linux çekirdeğinde root yetkisi aslında parçalara bölünmüştür. Buna capabilities diyoruz. Mesela CAP_SYS_ADMIN yetkisi, neredeyse tam root yetkisine eşittir. Eğer bir saldırganın sızdığı konteynerda bu yetki varsa, cgroups üzerinden kaçış (escape) tekniklerini kullanarak saniyeler içinde host shell'ini alabilir.

Şu kontrolü mutlaka yapın:

# Konteyner içindeki yetenekleri listeleme
getcap /proc/self/status
# Veya manuel kontrol
cat /proc/self/status | grep CapEff

Eğer CapEff değeri 0000003fffffffff gibi bir şeyse, o konteyner her şeyi yapabilir demektir.

İmaj Güvenliği: İçeride Kim Var?

testCompany için bir uygulama geliştirirken FROM python:3.9 yazıp geçiyoruz. Peki o imajın içinde ne var? 400 tane gereksiz paket, 10 tane kritik seviyede (CVE) açık barındıran kütüphane... Saldırgan içeri girdiğinde kullanabileceği curl, wget, netcat,hatta gcc gibi araçları hazır buluyor.

Saldırganın işini kolaylaştırmayın. Distroless imajlar kullanın. İçinde shell bile olmayan bir imajda, saldırgan exploit payload'unu nasıl indirecek? Nasıl çalıştıracak?

# Kötü Uygulama
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl netcat python3

# İyi Uygulama (Distroless / Multi-stage)
FROM python:3.9-slim AS build
COPY . /app
# ... build işlemleri ...

FROM gcr.io/distroless/python3
COPY --from=build /app /app
WORKDIR /app
CMD ["main.py"]

Savunma Hattını Nasıl Kurarız?

  1. Rootless Docker: Mümkünse Docker Daemon'ın kendisini bile root olmayan bir kullanıcıyla çalıştırın.
  2. ReadOnly File System: Konteynerın kendi dosya sistemine yazmasına izin vermeyin. docker run --read-only. Eğer bir yere yazması gerekiyorsa sadece o dizini tmpfs olarak bağlayın.
  3. User Namespaces: Host üzerindeki root (UID 0) ile konteyner içindeki root aynı olmamalı. userns-remap özelliğini aktif ederek konteyner içindeki root'u, host üzerinde yetkisiz bir kullanıcıya eşleyin.
  4. Seccomp ve AppArmor: Uygulamanızın hangi sistem çağrılarını (syscall) yapacağını kısıtlayın. Bir web API neden mount() sistem çağrısını kullansın ki? Eğer kullanmıyorsa, Seccomp ile bunu engelleyin.

Gece Rahat Uyumanızı Sağlayacak Bir Check-list

  • Konteyner içinde root kullanıcıyla mı çalışıyor? (Lütfen USER appuser ekleyin).
  • docker.sock mount edilmiş mi? (Edildiyse nedenini 3 kere sorgulayın).
  • İmajlar her gün scan ediliyor mu? (Trivy veya Grype gibi araçlar hayat kurtarır).
  • K8s kullanıyorsan PodSecurityPolicy veya yeni adıyla Pod Security Admission kuralları devrede mi?

Unutmayın dostlar, siber güvenlikte 'güvenli sistem' yoktur, 'saldırılması çok maliyetli sistem' vardır. Konteynerın etrafına ördüğünüz duvarlar ne kadar teknik ve katmanlı olursa, Red Team olarak bizim işimiz o kadar zorlaşır. Ve dürüst olayım, bizim işimizin zorlaşması, sizin prod ortamınızın ayakta kalması demektir.

Sistemlerinizi sıkılaştırın, loglarınızı izleyin ve asla varsayılan ayarlara güvenmeyin. Görüşmek üzere!

İlgili yazılar