---
title: "Konteyner Kaçışı: Docker Socket, Privileged Mode ve Hareket Eden Sınır"
description: "Bir konteyner; bir namespace, bir cgroup ve bir dizi capability'den oluşur — bir sınır değildir. Bunlardan biri sana verildiğinde host yalnızca bir mount uzağındadır. Docker socket ve --privileged canlı olarak gösteriliyor, modern sistemlerin çoktan kaldırdığı cgroup v1 numarası da ayrıca anlatılıyor."
date: 2026-11-06
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/tr/yazilar/container-escape-docker-socket/
---

## Konteyner bir sınır değildir

Bir konteyner üç şeyden oluşur: bir **namespace** (process, mount, network ve geri kalanı; *ne görebildiğini* yalıtır), bir **cgroup** (*ne kullanabildiğini* sınırlar) ve bir dizi **capability** (*ne yapmana izin verildiğini* belirler). Bunların hiçbiri tek başına bir güvenlik sınırı değildir — hepsi kaynak yalıtımı için tasarlanmış ilkel yapılardır. Host ile konteyner aynı çekirdeği paylaşır ve bu paylaşım, kaçışın neden mümkün olduğunun da temelidir. Namespace'ler görünürlüğü, cgroup'lar tüketimi, capability'ler ise yetkiyi sınırlar; ama üçü de aynı çekirdeğin üzerinde çalışır. Dolayısıyla koruma, konteynerin ne kadar iyi yapılandırıldığına bağlıdır, sihirli bir yalıtım katmanına değil.

Bu yüzden her konteyner kaçışı aynı biçimi izler: konteynere verilen ve aslında verilmemesi gereken tek şeyi bul, sonra onu host'un dosya sistemine ya da process alanına ulaşmak için kullan. Burada en yaygın iki yolu gerçek bir Docker daemon'ı üzerinde gösteriyorum; ardından artık geçerli olmayan üçüncü bir yolu, neden öldüğünü anlatarak kapatıyorum.

Bu laboratuvardaki host hakkında bir not: Mac'imde Docker, bir Linux VM'inin (OrbStack) içinde çalışıyor; bu yüzden bu çıktılardaki "host" aslında o VM'dir. Bu makinelerin normalde çalıştığı yer olan bir Linux sunucusunda ise aynı adımlar doğrudan fiziksel makineye iner. Yani mekanizma değişmez, yalnızca hedefin kim olduğu değişir.

## Docker socket: host'un anahtarları, içeride mount edilmiş

`/var/run/docker.sock`, Docker daemon'ının kontrol düzlemidir. Host'ta root ile eşdeğerdir, çünkü onunla konuşabilen herkes host üzerinde istediği ayrıcalıkta konteyner oluşturabilir. Bu socket'i bir konteynerin içine mount etmek, en yaygın kaçış tetikleyicisidir:

```bash
$ docker run -d --name web -v /var/run/docker.sock:/var/run/docker.sock alpine sleep 3600
```

Konteynerin içinde socket hazırdır ve yanıt verir:

```bash
$ ls -la /var/run/docker.sock
srw-rw----  1 root root 0 Nov  3 07:38 /var/run/docker.sock

$ docker -H unix:///var/run/docker.sock ps
CONTAINER ID   IMAGE     COMMAND        CREATED        STATUS        NAMES
0da093ef619f   alpine    "sleep 3600"   1 second ago   Up 1 second   web
```

O `ps` çıktısı bile host seviyesinde bilgi gösteriyor; konteyner artık kendi küçük dünyasından değil, daemon'ın gözünden konuşuyor. Kaçış da bariz bir sonraki adım oluyor: socket'i kullanarak host'un root dosya sistemini içine mount eden *yeni* bir konteyner başlat, sonra onun içine `chroot` yap. Burada dikkat edilmesi gereken nokta, kaçışın ilk konteynerin içinden değil, onun adına konuşan daemon üzerinden gerçekleşmesidir:

```bash
$ docker -H unix:///var/run/docker.sock run --rm --privileged -v /:/host alpine \
    sh -c "cat /host/etc/hostname; head -1 /host/etc/os-release"
[host] hostname: orbstack
[host] os: PRETTY_NAME="OrbStack"
```

Hiçbir zaman kendisine verilmeyen host `/etc/hostname` dosyasını bir konteynerin içinden okumak, işin bütün özetidir. `cat` komutunu `cat /host/etc/shadow` ile değiştir ya da `root` sahipli bir dosya yaz; konteyner sınırı artık yoktur. Socket anahtardı, geri kalan her şey onu takip etti. Bu yüzden Docker socket'i "sadece bir dosya" gibi düşünmek yanıltıcıdır: o dosya, host'un tüm kontrolünü temsil eder. Okuma izni bile bilgi sızdırabilir; yazma izni ise doğrudan host üzerinde kod çalıştırma demektir.

## `--privileged`: diski doğrudan mount et

Konteyner privileged ise socket'e ihtiyacın bile yoktur. `--privileged` hem `CAP_SYS_ADMIN` verir **hem de** device cgroup kısıtlamalarını kaldırır; bu ikisi birlikte "host block device'larını mount edebilirsin" anlamına gelir. Diskleri listele, host root'u mount et, işlem tamam:

```bash
$ docker run --rm --privileged alpine sh -c 'cat /proc/partitions'
 254        0     420164 vda
 254       16  482798608 vdb
 254       17  482797568 vdb1

$ docker run --rm --privileged alpine sh -c 'mkdir -p /mnt/host && mount /dev/vdb1 /mnt/host && ls /mnt/host'
docker
guest-state
k8s
scon
version
```

`/dev/vdb1` host'un ana bölümüdür ve `ls /mnt/host` onu listeler — yani daemon'ın kendi root dosya sistemini. Akılda tutulması gereken nüans şu: `--cap-add=SYS_ADMIN` *tek başına* **yeterli değildir**. Mount başarısız olur, çünkü ayrıcalıklı olmayan bir konteyner ilgili device node'a da sahip değildir:

```bash
$ docker run --rm --cap-add=SYS_ADMIN alpine sh -c 'mount /dev/vdb1 /mnt/host'
mount: mounting /dev/vdb1 on /mnt/host failed: No such file or directory
```

Yani gerçek malzeme *capability + device erişimi* bileşimidir. `--privileged` ikisini aynı anda verir; tam da bu yüzden kaçınılması gereken bayraktır. Tek tek `--cap-add` ve `--device` vermek, ihtiyaç duyulanı verirken gereksiz yüzeyi kapatmanın daha kontrollü yoludur. Kural basit: bir konteyner yalnızca gerçekten ihtiyaç duyduğu capability'ye ve yalnızca gerçekten erişmesi gereken aygıta sahip olmalıdır.

## Modern sistemlerin çoktan kaldırdığı numara

Üçüncü klasik kaçış, cgroup v1 `release_agent` mekanizmasıydı: bir cgroup mount et, `notify_on_release` ayarla, `release_agent` değerini host'taki bir script'e yönlendir ve sonra tetikle. Bu sistemde ise mekanizma daha başlamadan ortadan kalkmış durumda:

```bash
$ cat /proc/self/cgroup
0::/
```

Tek bir `0::/` çıktısı **cgroup v2** anlamına gelir ve cgroup v2 `release_agent` mekanizmasını tamamen kaldırmıştır. Bu, konteyner modelinin zamanla nasıl sıkılaştığını gösteren iyi bir örnektir: cgroup v1'e karşı çalışan bir kaçışın artık bir arayüzü yoktur. Ders "cgroup v1 kullanmak tehlikeliydi" değil; konteynerin sunduğu *arayüz yüzeyi* bir saldırganın yaşadığı yerdir ve o yüzeyi küçültmek tek gerçek savunmadır. Bir zamanlar çalışan bir teknik, arayüz kaldırıldığında kendiliğinden etkisiz hale gelir; yama yazmak zorunda kalmazsın, çünkü saldırının tutunacağı yüzey artık yoktur.

## Tespit ve düzeltme

| Kaçış | Gerektirdiği | Tespit | Kontrol |
| --- | --- | --- | --- |
| Docker socket | mount edilmiş `/var/run/docker.sock` | pod içinden beklenmedik konteyner oluşturma | socket'i asla mount etme; delegated builder servisi kullan |
| Privileged mount | `--privileged` (SYS_ADMIN + devices) | konteynerden host block device `mount`'u | `--privileged`'ı kaldır; yalnızca gerekli `--cap-add` + `--device` ver |
| cgroup release_agent | cgroup v1 | tarihsel | cgroup v2 arayüzü kaldırır |

Kontroller temiz bir hiyerarşi oluşturur. Platform seviyesinde **Pod Security Standards** ("restricted" profili) privileged mode'u, host mount'larını ve capability escalation'ı engeller. Runtime seviyesinde **seccomp** ve **AppArmor**, konteynerin çağırabileceği syscall'ları sınırlar; böylece beklenmedik bir `mount` başarılı olmadan ölür. Alışkanlık seviyesinde ise iki basit kural vardır: Docker socket'ini bir konteynere asla mount etme ve yazılı bir gerekçen olmadan `--privileged` çalıştırma. Konteyner kaçışı sonuçta bir zafiyet sorunundan çok bir yanlış yapılandırma sorunudur; bu yüzden düzeltmeler de çoğu zaman bir yamadan değil, verilen iznin geri alınmasından ibarettir.

## Özet

İki canlı kaçış, bir kullanımdan kalkmış arayüz: Docker socket root ile eşdeğerdir ve asla dağıtılmamalıdır; `--privileged` host'a yalnızca bir disk mount uzağındadır; cgroup v1 `release_agent` numarası ise cgroup v2 onu kaldırdığı için artık yoktur. Ortak nokta şudur: her kaçış *verilmiş* bir capability, mount ya da socket ile başlar — bu yüzden düzeltme, onları vermeyi bırakmaktır. Yüzeyi daraltmak, tek tek saldırıları kovalamaktan daha kalıcı bir savunma sağlar.

<figure class="diagram">
<svg viewBox="0 0 760 220" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Container escape yolları: mount edilmiş Docker socket veya CAP_SYS_ADMIN ve cihaz erişimine sahip privileged bir container host root dosya sistemine ulaşırken, cgroup v2 eski release_agent arayüzünü kaldırır.">
<defs><marker id="ce1" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker></defs>
<text x="0" y="16" class="dg-muted">CONTAINER ESCAPE · VERİLEN YETKİLERDEN HOST ROOT ERİŞİMİNE</text>
<rect x="0" y="40" width="220" height="120" rx="8" class="dg-box" />
<text x="14" y="66" class="dg-label">1 · Container bağlamı</text>
<text x="14" y="88" class="dg-mono dg-compact">docker.sock VEYA --privileged</text>
<text x="14" y="108" class="dg-muted">paylaşılan host çekirdeği</text>
<line x1="220" y1="100" x2="262" y2="100" class="dg-line-crit" marker-end="url(#ce1)" />
<rect x="266" y="40" width="230" height="120" rx="8" class="dg-box-accent" />
<text x="280" y="66" class="dg-label">2 · Sınır geçişi</text>
<text x="280" y="88" class="dg-mono dg-compact">-v /:/host | mount /dev/vdb1</text>
<text x="280" y="108" class="dg-muted">daemon API veya blok cihaz</text>
<line x1="496" y1="100" x2="538" y2="100" class="dg-line-crit" marker-end="url(#ce1)" />
<rect x="542" y="40" width="218" height="120" rx="8" class="dg-box-danger" />
<text x="556" y="66" class="dg-label">3 · Host ele geçirme</text>
<text x="556" y="88" class="dg-mono dg-compact">chroot /host · /mnt/host</text>
<text x="556" y="108" class="dg-crit">host root dosya sistemi</text>
</svg>
<figcaption>Her kaçış açıkça verilmiş bir host arayüzünü kullanır: ya Docker daemon soketi ya da <code>--privileged</code> altındaki blok cihaz erişimi.</figcaption>
</figure>

## Kanıt ve sınırlar

Docker socket ve `--privileged` kaçış adımları, yerel bir OrbStack Linux sanal makinesi üzerinde çalışan izole bir Docker daemon ortamında yürütülmüştür. Bu nedenle mount edilen kök dosya sistemi (`/dev/vdb1`) ve `/etc/os-release` çıktıları, üretim ortamındaki bir küme düğümünü değil yerel daemon VM'ini yansıtır. Negatif kontroller (cihaz erişimi olmadan `--cap-add=SYS_ADMIN` hatası ve cgroup v2'yi doğrulayan `0::/` çıktısı) aynı çekirdek üzerinde doğrulanmış, test konteynerleri de doğrulamadan hemen sonra kaldırılmıştır.

## Referanslar

- **Docker, Security:** [docs.docker.com/engine/security/security](https://docs.docker.com/engine/security/security/)
- **Kubernetes, Pod Security Standards:** [kubernetes.io/docs/concepts/security/pod-security-standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
- **Linux man-pages, cgroups (7):** [man7.org/linux/man-pages/man7/cgroups.7.html](https://man7.org/linux/man-pages/man7/cgroups.7.html)
- **MITRE ATT&CK, Container Escape (T1611):** [attack.mitre.org/techniques/T1611](https://attack.mitre.org/techniques/T1611/)
- **Trail of Bits, Understanding Docker Container Escapes:** [blog.trailofbits.com](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/)
