K8S提权漏洞之CVE-2018-1002105

admin 2025年2月13日11:17:24评论19 views字数 6257阅读20分51秒阅读模式

文章首发于:

火线Zone社区(https://zone.huoxian.cn/)

CVE-2018-1002105是k8s的一个提权漏洞,提权中利用高权限websocket通道来窃取凭据文件、利用凭据文件接管apiserver的过程还是比较有趣,这里记录CVE-2018-1002105漏洞原理和利用。

漏洞基础知识

基于角色的访问控制(Role-Based Access Control)

基于角色的访问控制(Role-Based Access Control,即”RBAC”)使rbac.authorization.k8s.io API Group 实现授权决策,允许管理员通过 Kubernetes API 动态配置策略。

要启用 RBAC,请使用 --authorization-mode=RBAC 启动 API Server。

RBAC API 声明了四种 Kubernetes 对象:

Role   #一系列权限的集合,通常是命名空间ClusterRole   #一系列权限的集合,通常是无命名空间RoleBindingClusterRoleBinding

用户可以像使用其他 Kubernetes API 资源一样 (例如通过 kubectl、API 调用等)与这些资源进行交互。例如,命令 kubectl create -f (resource).yml

在 RBAC API 中,一个角色包含了一套表示一组权限的规则。权限以纯粹的累加形式累积(没有” 否定” 的规则)。角色可以由命名空间(namespace)内的Role对象定义,而整个 Kubernetes 集群范围内有效的角色则通过 ClusterRole对象实现。

Role 对象

一个 Role 对象只能用于授予对某一单一命名空间中资源的访问权限。以下示例描述了”default” 命名空间中的一个 Role 对象的定义,用于授予对pod的读访问权限:

kind: RoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:  namespace: default  name: pod-readerrules:- apiGroups: [""]  resources: ["pods"]  verbs: ["get", "watch", "list"]

ClusterRole 对象

ClusterRole 定义可用于授予用户对某一特定命名空间,或者所有命名空间中的secret的读访问权限:

kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1beta1metadata:  # 鉴于 ClusterRole 是集群范围对象,所以这里不需要定义 "namespace" 字段  name: secret-readerrules:- apiGroups: [""]  resources: ["secrets"]  verbs: ["get", "watch", "list"]

RoleBinding 与 ClusterRoleBinding对象

简单的来说就是把role和clusterrole定义的权限和我们的Role进行绑定的,二者的区别也是作用范围的区别:

RoleBinding只会影响到当前namespace下面的资源操作权限,而ClusterRoleBinding会影响到所有的namespace。

角色绑定包含了一组相关主体(即 subject, 包括用户 ——User、用户组 ——Group、或者服务账户 ——Service Account)。

下面示例中定义的 RoleBinding 对象在”default” 命名空间中将”pod-reader” 角色授予用户”jane”。这一授权将允许用户”jane” 从”default” 命名空间中读取 pod。

kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata:  name: read-pods  namespace: defaultsubjects:- kind: User  name: jane  apiGroup: rbac.authorization.k8s.ioroleRef:  kind: Role  name: pod-reader  apiGroup: rbac.authorization.k8s.io

websocket协议

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

例子:

1、客户端:申请协议升级

首先,客户端发起协议升级请求。可以看到,采用的是标准的HTTP报文格式,且只支持GET方法。

GET / HTTP/1.1Host: localhost:8080Origin: http://127.0.0.1:3000Connection: UpgradeUpgrade: websocketSec-WebSocket-Version: 13Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==

重点请求首部意义如下:

- Connection: Upgrade:表示要升级协议- Upgrade: websocket:表示要升级到websocket协议。- Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。- Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。

2、服务端:响应协议升级

服务端返回内容如下,状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。

HTTP/1.1 101 Switching ProtocolsConnection:UpgradeUpgrade: websocketSec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

k8s apiserver代理转发功能

这个洞就是利用了api server具备的代理转发功能,比如kubectl的exec功能就是为进入目标pod的目标容器中执行命令(挂载标准输入和输出、标准错误的情景),kubectl exec访问kube-apiserver的connect接口,kube-apiserver把请求转发至对应节点的kubelet进程

漏洞原理

K8S提权漏洞之CVE-2018-1002105

CVE-2018-1002105漏洞大致原理是与k8s apiserver通信时切换websockect协议,处理出错时但apiserver却保留了这个通道,导致了打通了client到kubelet的通道。

漏洞处是在:
staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go 发现有两处goroutine:

K8S提权漏洞之CVE-2018-1002105

通过注释大概就能看出这是要建立一个proxy通道,而漏洞的点就是无论rawResponseCode会返回多少,都会成功走到这两个Goroutine中,建立起proxy通道。

K8S提权漏洞之CVE-2018-1002105

这里就导致一个问题就是,如果一个正常请求在进行协议切换时,是会返回一个101的返回码,继而建立起一个websocket通道,该websocket通道是建立在原有tcp通道之上的,且在该TCP的生命周期内,其只能用于该websocket通道,而代码中没有对处理出错的异常进行捕获和判断, 无论是否有错都会保留这个TCP通道,造成TCP连接的复用,打通了client到kubelet的通道。

查看发送api 请求的代码在:
pkg/kubelet/server/server.go,其中在InstallDebuggingHandlers方法中注册了exec、attach、portForward等接口:

K8S提权漏洞之CVE-2018-1002105

而如果要构造失败的请求,pkg/kubelet/server/remotecommand/httpstream.go,如果对exec接口的请求参数中不包含stdin、stdout、stderr三个,则可以构造一个错误

K8S提权漏洞之CVE-2018-1002105

回到staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go中建立proxy通道的上方, 发现属于tryUpgrade函数

K8S提权漏洞之CVE-2018-1002105

这里tryUpgrade函数首先调用了IsUpgradeRequest方法进行请求的过滤,满足HTTP请求头中包含 Connection和Upgrade 要求的将返回True。

K8S提权漏洞之CVE-2018-1002105
K8S提权漏洞之CVE-2018-1002105

IsUpgradeRequest返回False的则直接退出tryUpdate函数,而返回True的则继续运行。

所以只需发送给API Server的攻击请求HTTP头中携带Connection/Upgrade Header即可运行到建立proxy的代码处。

环境搭建

而构造的请求需要是认证的用户和满足API server 往后端转发(通过HTTP头检测),且后端kubelet会返回失败,利用错误返回没有被处理导致连接可以继续保持的特性来复用通道打成后面的目的。

前面看了代码如果不包含stdin、stdout、stderr三个则可以构造一个错误。

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

K8S提权漏洞之CVE-2018-1002105

创建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

K8S提权漏洞之CVE-2018-1002105

对其他命名空间越权操作发现提示权限不足:
kubectl --token=password --server=https://192.168.1.22:6443 --insecure-skip-tls-verify get pods -n kube-system

K8S提权漏洞之CVE-2018-1002105

漏洞复现

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目录下的大量敏感凭据:

K8S提权漏洞之CVE-2018-1002105

exp中指定读取的证书文件

K8S提权漏洞之CVE-2018-1002105

利用

K8S提权漏洞之CVE-2018-1002105

这样就拿到了凭据,最后就是创建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: /
K8S提权漏洞之CVE-2018-1002105

host-escape-door 目录为pod挂载宿主机的目录,发现已经可以查看apiserver宿主机的目录:

K8S提权漏洞之CVE-2018-1002105

【火线Zone云安全社区群】

进群可以与技术大佬互相交流

进群有机会免费领取节假日礼品

进群可以免费观看技术分享直播

识别二维码回复【社区群】进群

K8S提权漏洞之CVE-2018-1002105

【火线Zone社区周激励】

2022.5.16~ 2022.5.22公告

K8S提权漏洞之CVE-2018-1002105

【相关精选文章】

K8S提权漏洞之CVE-2018-1002105
K8S提权漏洞之CVE-2018-1002105
K8S提权漏洞之CVE-2018-1002105

火线Zone是[火线安全平台]运营的云安全社区,内容涵盖云计算、云安全、漏洞分析、攻防等热门主题,研究讨论云安全相关技术,助力所有云上用户实现全面的安全防护。欢迎具备分享和探索精神的云上用户加入火线Zone社区,共建一个云安全优质社区!

如需转载火线Zone公众号内的文章请联系火线小助手:hxanquan(微信)

K8S提权漏洞之CVE-2018-1002105

//  火线Zone//

微信号 : huoxian_zone

K8S提权漏洞之CVE-2018-1002105

点击阅读原文,加入社区,共建一个有技术氛围的优质社区!

原文始发于微信公众号(火线Zone):K8S提权漏洞之CVE-2018-1002105

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月13日11:17:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   K8S提权漏洞之CVE-2018-1002105https://cn-sec.com/archives/1059257.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息