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

This commit is contained in:
Translator
2025-08-28 18:02:07 +00:00
parent 0a1ef296cd
commit 0f42ac10be

View File

@@ -4,62 +4,62 @@
## GCP
Se você estiver executando um cluster k8s dentro do GCP, provavelmente desejará que algum aplicativo em execução dentro do cluster tenha acesso ao GCP. Existem 2 maneiras comuns de fazer isso:
Se você está executando um cluster k8s dentro do GCP, provavelmente vai querer que alguma aplicação rodando no cluster tenha acesso ao GCP. 2 maneiras comuns de fazer isso:
### Montando chaves GCP-SA como segredo
### Mounting GCP-SA keys as secret
Uma maneira comum de dar **acesso a um aplicativo kubernetes ao GCP** é:
Uma forma comum de dar acesso a uma aplicação kubernetes ao GCP é:
- Criar uma Conta de Serviço do GCP
- Vincular as permissões desejadas
- Baixar uma chave json da SA criada
- Montá-la como um segredo dentro do pod
- Definir a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para o caminho onde o json está.
- 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 **atacante**, se você comprometer um contêiner dentro de um pod, deve verificar essa **variável** **env** e **arquivos** **json** com credenciais do GCP.
> 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.
### Relacionando json GSA ao segredo KSA
### Relating GSA json to KSA secret
Uma maneira de dar acesso a um GSA a um cluster GKE é vinculá-los desta forma:
Uma forma de dar acesso de um GSA a um GKE cluster é vinculando-os desta forma:
- Criar uma conta de serviço Kubernetes no mesmo namespace que seu cluster GKE usando o seguinte comando:
- Crie um Kubernetes service account no mesmo namespace que seu GKE cluster usando o comando a seguir:
```bash
Copy codekubectl create serviceaccount <service-account-name>
kubectl create serviceaccount <service-account-name>
```
- Crie um Kubernetes Secret que contenha as credenciais da conta de serviço GCP à qual você deseja conceder acesso ao cluster GKE. Você pode fazer isso usando a ferramenta de linha de comando `gcloud`, conforme mostrado no exemplo a seguir:
- 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:
```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
```
- Vincule o Kubernetes Secret à conta de serviço do Kubernetes usando o seguinte comando:
- Vincule o Kubernetes Secret à Kubernetes service account usando o comando a seguir:
```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]
> No **segundo passo**, as **credenciais do GSA foram definidas como segredo do KSA**. Então, se você puder **ler esse segredo** de **dentro** do **cluster GKE**, você pode **escalar para essa conta de serviço GCP**.
> 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**.
### Identidade de Workload GKE
### GKE Workload Identity
Com a Identidade de Workload, podemos configurar uma [conta de serviço Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para agir como uma [conta de serviço Google](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods executando com a conta de serviço Kubernetes serão automaticamente autenticados como a conta de serviço Google ao acessar APIs do Google Cloud.
Com o Workload Identity, podemos configurar a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para atuar como a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). 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 a Identidade de Workload no GCP** ([**passos**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) e criar a SA GCP que você deseja que o k8s impersonifique.
A **primeira série de passos** para habilitar esse comportamento é **habilitar o Workload Identity no GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) e criar a GCP SA que você quer que o k8s assuma.
- **Habilitar a Identidade de Workload** em um novo cluster
- **Enable Workload Identity** on a new cluster
```bash
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)
- **Criar/Atualizar um novo nodepool** (Clusters Autopilot não precisam disso)
```bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- Crie a **Conta de Serviço GCP para se fazer passar** a partir do K8s com permissões GCP:
- Crie a **GCP Service Account to impersonate** a partir do K8s com permissões 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"
```
- **Conecte-se** ao **cluster** e **crie** a **conta de serviço** para usar
- **Conecte-se** ao **cluster** e **crie** a **conta de serviço** a ser usada
```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
```
- **Vincule o GSA com o KSA**
- **Vincular o GSA ao 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 \
@@ -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
```
Verifique o seguinte comando para autenticar, caso necessário:
Verifique o comando a seguir para autenticar, caso necessário:
```bash
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 **anotação `iam.gke.io/gcp-service-account`**, pois isso indica que a SA pode acessar algo no GCP. Outra opção seria tentar abusar de cada KSA no cluster e verificar se ela tem acesso.\
> Do GCP, é sempre interessante enumerar as ligações e saber **qual acesso você está concedendo às SAs dentro do Kubernetes**.
> 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 facilmente **iterar sobre todas as definições de pods** **procurando** por essa **anotação**:
Este é um script para **iterar facilmente por todas as definições de pods** **procurando** por essa **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
@@ -141,9 +141,9 @@ done | grep -B 1 "gcp-service-account"
### Kiam & Kube2IAM (IAM role for Pods) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
Uma maneira (ultrapassada) de dar IAM Roles para Pods é usar um [**Kiam**](https://github.com/uswitch/kiam) ou um [**Kube2IAM**](https://github.com/jtblin/kube2iam) **servidor.** Basicamente, você precisará executar um **daemonset** em seu cluster com um **tipo de IAM role privilegiado**. Este daemonset será o responsável por conceder acesso aos IAM roles para os pods que precisarem.
Uma forma (desatualizada) de atribuir IAM Roles aos Pods é usar um [**Kiam**](https://github.com/uswitch/kiam) ou um [**Kube2IAM**](https://github.com/jtblin/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.
Primeiramente, você precisa configurar **quais roles podem ser acessadas dentro do namespace**, e você faz isso com uma anotação dentro do objeto namespace:
Primeiro de tudo, você precisa configurar **quais roles podem ser acessadas dentro do namespace**, e isso é feito com uma annotation dentro do namespace object:
```yaml:Kiam
kind: Namespace
metadata:
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
```
Uma vez que o namespace está configurado com os papéis IAM que os Pods podem ter, você pode **indicar o papel que deseja em cada definição de pod com algo como**:
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**:
```yaml:Kiam & Kube2iam
kind: Pod
metadata:
@@ -171,12 +171,12 @@ annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> Como um atacante, se você **encontrar essas anotações** em pods ou namespaces ou um servidor kiam/kube2iam em execução (provavelmente no kube-system), você pode **impersonar qualquer r**ole que já está **sendo usada por pods** e mais (se você tiver acesso à conta AWS, enumere os roles).
> 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 r**ole que já é **usado por pods** e mais (se você tiver acesso à conta AWS, enumere os roles).
#### Criar Pod com Role IAM
#### Criar Pod com IAM Role
> [!NOTE]
> O role IAM a ser indicado deve estar na mesma conta AWS que o role kiam/kube2iam e esse role deve ser capaz de acessá-lo.
> 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.
```yaml
echo 'apiVersion: v1
kind: Pod
@@ -192,14 +192,14 @@ image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -
```
### IAM Role para Contas de Serviço K8s via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
### IAM Role para Service Accounts do K8s via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
Esta é a **maneira recomendada pela AWS**.
Esta é a **forma recomendada pela AWS**.
1. Primeiro, você precisa [criar um provedor OIDC para o cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Em seguida, você cria um papel IAM com as permissões que a SA precisará.
3. Crie uma [relação de confiança entre o papel IAM e a SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) nome (ou os namespaces que dão acesso ao papel para todas as SAs do namespace). _A relação de confiança verificará principalmente o nome do provedor OIDC, o nome do namespace e o nome da SA_.
4. Finalmente, **crie uma SA com uma anotação indicando o ARN do papel**, e os pods executando com essa SA terão **acesso ao token do papel**. O **token** é **escrito** dentro de um arquivo e o caminho é especificado em **`AWS_WEB_IDENTITY_TOKEN_FILE`** (padrão: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
1. Antes de tudo você precisa [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Depois você cria um IAM role com as permissões que o SA irá requerer.
3. Crie uma [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (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_.
4. 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`)
```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
```
Para **obter aws usando o token** de `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`, execute:
Para **obter aws usando o token** de `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` execute:
```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]
> Como um atacante, se você puder enumerar um cluster K8s, verifique as **contas de serviço com essa anotação** para **escalar para AWS**. Para fazer isso, basta **exec/create** um **pod** usando uma das **contas de serviço privilegiadas** do IAM e roubar o token.
> 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 **Política de Confiança de um papel** pode estar **mal configurada** e, em vez de dar acesso AssumeRole à conta de serviço esperada, dá acesso a **todas as contas de serviço**. Portanto, se você for capaz de escrever uma anotação em uma conta de serviço controlada, pode acessar o papel.
> À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.
>
> Verifique a **página seguinte para mais informações**:
> Confira a **following page for more information**:
{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}
### Encontrar Pods e SAs com Papéis IAM no Cluster
### Encontrar Pods e SAs com IAM Roles no Cluster
Este é um script para facilmente **iterar sobre todos os pods e definições de sas** **procurando** por essa **anotação**:
Este é um script para facilmente **iterar por todas as definições de pods e SAs** **procurando** por essa **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
A seção anterior tratou de como roubar IAM Roles com pods, mas note que um **Node do** cluster K8s será uma **instância dentro da nuvem**. Isso significa que o Node é altamente provável que **tenha um novo IAM role que você pode roubar** (_note que geralmente todos os nodes de um cluster K8s terão o mesmo IAM role, então pode não valer a pena tentar verificar em cada node_).
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_).
No entanto, há um requisito importante para acessar o endpoint de metadados a partir do node, você precisa estar no node (sessão ssh?) ou pelo menos ter a mesma rede:
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:
```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"}]}}'
```
### Roubar Token de Função IAM
### Steal IAM Role Token
Anteriormente, discutimos como **anexar Funções IAM a Pods** ou até mesmo como **escapar para o Nó para roubar a Função IAM** que a instância tem anexada a ela.
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 **roubar** suas novas **credenciais de função IAM**:
Você pode usar o seguinte script para **steal** suas recém-conquistadas **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,6 +283,19 @@ curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE
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](https://blog.calif.io/p/privilege-escalation-in-eks). 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**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
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:
```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
```
## Referências
- [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)