Skip to content

Cet outil n'est ni affilié à The Linux Foundation, à la Cloud Native Computing Foundation (CNCF) ou au projet Kubernetes, ni approuvé ni sponsorisé par eux. Kubernetes et K8s sont des marques déposées de The Linux Foundation. EKS, GKE, AKS et les autres noms sont des marques de leurs propriétaires respectifs.

Escalade de privilèges RBAC Kubernetes : la détecter

Repérer l'escalade de privilèges RBAC Kubernetes dans les logs d'audit : bindings cluster-admin, verbes escalate, bind, impersonate, accès anonyme.

Publié le 6 min de lecture

TL;DR. Dans le log d'audit, une escalade RBAC est une écriture : un create, update ou patch sur clusterrolebindings, rolebindings, clusterroles ou roles. Les dangereuses lient cluster-admin, accordent les verbes escalate, bind ou impersonate ou des wildcards *, ou nomment system:anonymous / system:unauthenticated. Il vous faut les corps de requête (niveau Request) pour voir le roleRef et les subjects. Regardez aussi le champ impersonatedUser, et souvenez-vous que l'IAM cloud peut donner accès au cluster sans aucun objet RBAC Kubernetes.

Un nouveau binding vers cluster-admin est le signe le plus fiable d'une intrusion Kubernetes sérieuse : c'est ainsi qu'un attaquant transforme un token emprunté en identité permanente. ATT&CK le suit sous T1098.006, Additional Container Cluster Roles.

RBAC en un tableau

Le RBAC repose sur quatre types d'objets :

ObjetPortéeAccorde
RoleNamespaceDes règles (verbes sur des ressources) dans un namespace
ClusterRoleClusterDes règles sur des ressources de cluster, ou réutilisables entre namespaces
RoleBindingNamespaceUn Role ou ClusterRole à des sujets, dans un namespace
ClusterRoleBindingClusterUn ClusterRole à des sujets, partout

Le roleRef du binding nomme le rôle ; subjects liste les utilisateurs, groupes ou comptes de service. Les deux sont dans le corps de la requête : une politique qui journalise les écritures RBAC en Metadata vous dit « un ClusterRoleBinding nommé node-health-admin a été créé » et rien sur ce qu'il accorde.

Les verbes de prévention de l'escalade

Kubernetes bloque l'escalade naïve : la documentation RBAC explique qu'on ne peut créer ou modifier un rôle qu'avec des permissions qu'on détient déjà, et ne lier qu'un rôle dont on détient déjà les permissions. Trois verbes lèvent ces contrôles :

  • escalate sur roles ou clusterroles : écrire un rôle avec plus de permissions qu'on n'en a.
  • bind sur roles ou clusterroles : lier un rôle qu'on ne détient pas, y compris cluster-admin.
  • impersonate sur users, groups ou service accounts : agir en tant qu'un autre.

Les bonnes pratiques RBAC classent les trois parmi les risques d'élévation de privilèges, avec les wildcards *, qui couvrent aussi tout type de ressource ajouté à l'avenir. Un rôle qui en accorde un à une personne ou à un workload est un chemin vers cluster-admin.

Détections

L'analyseur de logs d'audit contrôle les écritures RBAC avec ces règles :

RègleSévéritéLogique
Binding vers cluster-admin crééCritique(Cluster)RoleBinding avec roleRef.name: cluster-admin
Accès accordé à des utilisateurs anonymes / non authentifiésCritiqueBinding avec le sujet system:anonymous ou system:unauthenticated
Rôle accordant escalate / bind / impersonate ou des wildcardsÉlevée(Cluster)Role écrit avec ces verbes ou des verbes/ressources *
Requête effectuée en usurpant une autre identitéMoyenneimpersonatedUser présent
Requête anonyme autoriséeÉlevéesystem:anonymous autorisé au-delà des endpoints de santé, de version et de découverte OIDC
Binding de rôle cluster-wide crééFaibleTout ClusterRoleBinding écrit par une identité non système

La règle de faible sévérité existe à cause du trou de journalisation évoqué plus haut : quand le corps manque, un nouveau ClusterRoleBinding mérite quand même un coup d'œil, et l'annotation authorization.k8s.io/reason des requêtes suivantes le nommera dès qu'il servira.

Lire un binding malveillant

Un exemple fictif, journalisé au niveau RequestResponse :

{
  "verb": "create",
  "user": { "username": "system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard" },
  "sourceIPs": ["203.0.113.45"],
  "userAgent": "kubectl/v1.30.2 (linux/amd64) kubernetes/3968350",
  "objectRef": { "resource": "clusterrolebindings", "name": "node-health-admin",
                 "apiGroup": "rbac.authorization.k8s.io" },
  "requestObject": {
    "kind": "ClusterRoleBinding",
    "metadata": { "name": "node-health-admin" },
    "roleRef": { "apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "cluster-admin" },
    "subjects": [ { "kind": "ServiceAccount", "name": "node-health", "namespace": "kube-system" } ]
  },
  "responseStatus": { "code": 201 }
}

Trois choses sautent aux yeux : un compte de service qui crée des objets RBAC, depuis une adresse Internet, avec kubectl ; un nom choisi pour ressembler à un composant système ; et un nouveau compte de service dans kube-system comme sujet. L'étape suivante de l'attaquant est généralement une TokenRequest pour ce compte de service, qui lui donne un identifiant indépendant de celui volé au départ. Le déroulé fictif suit exactement cette séquence.

Impersonation

Un appelant qui détient la permission impersonate peut envoyer les en-têtes Impersonate-User, Impersonate-Group (et apparentés) ; la requête est alors autorisée en tant que l'identité usurpée. L'événement d'audit garde les deux : user est l'appelant réel, impersonatedUser l'identité revendiquée. Rapportez toujours les deux, et vérifiez qui détient impersonate : dans la plupart des clusters, cette liste devrait être très courte.

kubectl --as=<user> et --as-group utilisent ces en-têtes ; certains proxys d'accès et dashboards aussi, c'est pourquoi l'analyseur signale l'impersonation en sévérité moyenne et vous laisse juger.

Accès anonyme

Sauf s'il est désactivé, une requête sans identifiants est authentifiée comme system:anonymous, dans le groupe system:unauthenticated, comme le décrit la documentation sur l'authentification. Les endpoints de santé et de découverte sont souvent ouverts. Un binding qui accorde davantage à ces sujets signifie que quiconque peut joindre l'API server dispose de ces droits. Voir l'entrée de glossaire system:anonymous pour les réglages qui le contrôlent.

Chemins d'escalade sans écriture RBAC

Toute escalade n'apparaît pas comme un binding :

  • IAM cloud. Sur EKS, les access entries et la ConfigMap aws-auth font correspondre des principaux IAM à des identités Kubernetes ; associer une politique d'accès admin à une access entry se fait dans l'API AWS (CloudTrail), pas dans le log d'audit Kubernetes. GKE accorde l'accès au cluster via des rôles IAM Google Cloud, AKS via des attributions de rôles Azure RBAC quand Azure RBAC pour Kubernetes est activé. Vérifiez ces changements dans les logs du plan de contrôle cloud.
  • Agrégation de ClusterRoles. Un ClusterRole dont les labels correspondent à une aggregationRule voit ses règles fusionnées dans le rôle agrégé. Ajouter un tel rôle étend discrètement tous les bindings du rôle agrégé.
  • Création de workloads. Qui peut créer des pods dans un namespace peut monter n'importe quel compte de service de ce namespace et agir en son nom. Voir secrets et vol de tokens.
  • Certificats. La permission d'approuver des CertificateSigningRequests permet d'émettre des certificats clients pour des identités arbitraires.
  • Webhooks d'admission. Contrôler la configuration des webhooks permet de voir ou de réécrire les objets à leur création.

Requêtes de chasse

Lignes JSON brutes, bindings vers cluster-admin :

jq -c 'select((.objectRef.resource == "clusterrolebindings" or .objectRef.resource == "rolebindings")
              and (.verb == "create" or .verb == "update" or .verb == "patch")
              and .requestObject.roleRef.name == "cluster-admin")
       | {t: .requestReceivedTimestamp, user: .user.username, ip: .sourceIPs,
          name: .objectRef.name, subjects: .requestObject.subjects}' audit.log

EKS, CloudWatch Logs Insights, toutes les écritures RBAC :

fields @timestamp, user.username, verb, objectRef.resource, objectRef.name
| filter @logStream like "kube-apiserver-audit"
| filter objectRef.apiGroup = "rbac.authorization.k8s.io"
| filter verb in ["create", "update", "patch", "delete"]
| sort @timestamp desc

Remédiation

  1. Sauvegardez les objets en cause (kubectl get clusterrolebinding <name> -o yaml) comme preuves, puis supprimez-les.
  2. Supprimez puis recréez tout compte de service qui a reçu cluster-admin, pour que les tokens émis pour lui cessent de fonctionner.
  3. Passez en revue chaque binding vers cluster-admin et vers des rôles capables d'écrire du RBAC, de lire des secrets ou de faire des exec dans des pods.
  4. Retirez escalate, bind, impersonate et les wildcards des rôles qui n'en ont pas strictement besoin.
  5. Désactivez l'authentification anonyme ou limitez-la aux endpoints de santé.

Articles liés

Articles liés

Détecter le crypto-minage dans Kubernetes via les logs d'audit : images et arguments de mineurs, registries inhabituels, persistance CronJob et DaemonSet.
Repérer une évasion de conteneur en préparation dans les logs d'audit Kubernetes : pods privilégiés, hostPID, hostPath sur /, DaemonSets dans kube-system.
Détecter le vol de secrets et de tokens de compte de service Kubernetes dans les logs d'audit : list globaux, rafales, TokenRequest, IP publiques, can-i.

Cet outil n'est ni affilié à The Linux Foundation, à la Cloud Native Computing Foundation (CNCF) ou au projet Kubernetes, ni approuvé ni sponsorisé par eux. Kubernetes et K8s sont des marques déposées de The Linux Foundation. EKS, GKE, AKS et les autres noms sont des marques de leurs propriétaires respectifs.