Typical workflow:
Get cluster access (internal, RCE on container, etc)
⬇ ️ Check for serviceaccount and its privileges
⬇️
Scan for services and try to get other accounts
🔄 ⬇️
Get admin account or an account with sufficient privileges
⬇️
Execute code on cluster/steal data
By default on every container a "default service account" is mounted to dir /run/secrets/kubernetes.io/serviceaccount
By default it has no value, but admins may give it permissions, e.g Helm, Dashboard
Check if account can get secrets, so you can compromise other accounts with different privileges
How to distinguish account token/keys?
ServiceAccount can be in forms:
- CA certificate + JWT token (usually on containers,
Webhook
authn) - CA certificate + client certificate + client key (usually in
.yaml
files as base64encoded strings,TLS
authn)
With any form you can query apiserver
by http with, e.g curl
. See notes for second form usage examples, and k8s docs for Webhook
.
-
/.dockerenv
-
/entrypoint.sh
,/app-entrypoint.sh
-
strange hostname looking like hex string
de605c442545
-
PID 1 process is application process or small init system like
dumb-init
-
cat /proc/self/cgroup
shows that we are in cgroupExample:
root@de605c442545:/# cat /proc/self/cgroup 12:cpuset:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 11:freezer:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 10:perf_event:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 9:rdma:/ 8:net_cls,net_prio:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 7:pids:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 6:memory:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 5:hugetlb:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 4:blkio:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 3:cpu,cpuacct:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 2:devices:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 1:name=systemd:/docker/de605c4425454a410b82da7b6ceeb73ba15ed4adab5c3ae10602b648ba296225 0::/system.slice/docker.service
-
docker signs if it is CRI
-
hostname like
<app-name>-[a-f0-9]{10}-[a-z0-9]{5}
Example:
tomcat-55c4cc5fcd-7l6x4
-
cat /proc/self/cgroup
shows that we are in k8sExample:
11:pids:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 10:cpuset:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 9:blkio:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 8:memory:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 7:hugetlb:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 6:perf_event:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 5:freezer:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 4:cpu,cpuacct:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 3:net_cls,net_prio:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 2:devices:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd 1:name=systemd:/kubepods/burstable/pode060d9f5-4f41-4153-8daf-4a7ee2a7eaad/4088e78945f24d32ca3e1b09f097704c9c92e70f525a553fef8da2e6c7f333fd
But, there is an option that k8s controls cgroups via systemd
-
run
mount
Example:
... cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory) ... tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime)
Prerequisite:
- dns queries are not blocked
- no network policy blocking connection
Given no TLS was configured you can use Helm as an admin
- Download helm cli
$ wget https://get.helm.sh/helm-v2.14.3-linux-amd64.tar.gz
$ tar xfv helm-v2.14.3-linux-amd64.tar.gz
- Use helm
$ ./helm --host tiller-deploy.kube-system.svc.cluster.local:44134
If cluster-admin
role is given to dashboard serviceaccount and --enable-skip-login
is set, then you can bypass login in kubernetes dashboard
- Just go to
https://kubernetes-dashboard.kubernetes-dashboard.svc.cluster.local
- Click
skip
button
Never forget about typical container(Docker) attacks:
- Mounted
docker.sock
=> root on node - Mounted
/var/lib/kubelet/
dir => kubelet account (get secrets, pods by name) - Pod running on master => direct exposure to
etcd