AzureScape:ACI 容器中的 K8s 跨账户集群接管研究

0x01 漏洞描述

Azure 容器实例 ( ACI ) 是 Azure 的容器即服务 ( CaaS ) 产品,ACI ) 使客户能够在 Azure 中运行容器而无需管理底层服务器。最近有研究人员发现并向 Microsoft 披露了 ACI 中的安全漏洞。恶意的 Azure 用户可能会利用这些问题逃逸到其他用户的容器上执行代码,窃取部署到平台的客户敏感文件,并可能滥用 ACI 的基础设施进行挖矿。研究人员将该漏洞命名为 Azurescape ——公有云中的跨账户容器逃逸接管漏洞。

https://azure.microsoft.com/en-us/services/container-instances/

Azurescape 允许恶意用户破坏托管 ACI 的多租户 Kubernetes 集群,建立对其他用户容器的完全控制。这篇文章描述了研究过程、漏洞分析,并提出了保护 Kubernetes 的措施,重点是多租户防止类似攻击的缓解措施。

微软在我们披露后不久修补了 ACI,现在还不知道 Azurescape 是否被在野利用。

0x02 Azure 容器实例

Azure 容器实例 ( ACI ) 于 2017 年 7 月发布,是大型云提供商提供的第一个容器即服务 ( CaaS ) 产品。借助 ACI,客户无需管理底层基础结构即可将容器部署到 Azure。ACI 负责扩展、请求路由和调度,为容器提供无服务器体验。

https://azure.microsoft.com/en-us/blog/announcing-azure-container-instances/

Azure 官网对 ACI 的描述如下:" 无需管理虚拟机或学习新工具即可快速开发应用程序,它只是在容器中、在云中运行的程序。"

在集群内部,ACI 托管在客户容器的多租户集群上。刚开始是使用 Kubernetes 集群,在过去的一年里,微软也开始在 Service Fabric 集群上托管 ACI。此处的漏洞会影响 Kubernetes 上的 ACI,本文的其余部分将仅参考该架构。根据我们的测试,我们在平台上部署了数千个容器,在披露时 Kubernetes 托管了大约 37% 的 ACI 新创建的容器。

该图说明了托管在多租户 Kubernetes 群集上的 Azure 容器实例,显示了主节点上的 api 服务器如何与为三个不同客户运行的三个工作节点相关联。 租户边界用红色虚线表示。


图 1. 托管在多租户 Kubernetes 集群上的 ACI。


在 ACI 等多租户环境中,你需要在租户之间实施强边界。在 ACI 中,该边界是节点虚拟机。每个客户容器都在专用的单租户节点上的 Kubernetes pod 中运行。这种 Kubernetes 多租户方法通常称为 node-per-tenant。

0x03 AzureScape 攻击场景

ACI 会防止相邻容器的恶意攻击。由于几乎任何人都可以将容器部署到平台上,因此 ACI 必须确保恶意容器不会进行破坏、泄漏信息、执行代码或以其他方式影响其他客户的容器,这种攻击影响就是跨账户或跨租户攻击。

Azure 容器实例中的跨账户攻击场景图解,展示了恶意客户如何占用工作节点。


图 2. 跨账户攻击场景。


以下部分涵盖了我们对 ACI 中跨账户攻击的研究。我们发现了一种跨租户攻击,恶意的 Azure 用户可以通过这种攻击进行容器逃逸,获取特权 Kubernetes 服务帐户令牌并接管 Kubernetes api-server,从而建立对多租户集群和在其中运行的所有客户容器的完全控制。

0x04 ACI 容器逃逸

CaaS 产品很难研究。用户只暴露了他们的容器环境,并且通过防火墙禁止访问本地网络。为了更好地了解 CaaS 平台如何运行我们的容器,我们创建了 WhoC。WhoC 是一个容器镜像,它可以读取执行它的容器 runtime,基于 Linux 容器中一个很少讨论的设计缺陷,允许它们读取底层主机的容器 runtime。这个想法与 CVE-2019-5736 非常相似,不同之处在于 CVE-2019-5736 是读取 runtime,它 WhoC 是覆盖主机的 runtime。

https://github.com/twistlock/whoc https://nvd.nist.gov/vuln/detail/CVE-2019-5736 https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736/

通过将 WhoC 部署到 ACI,我们能够检索平台中使用的容器 runtime。不出所料,我们发现了行业标准容器 runtime runC。

https://github.com/opencontainers/runc

让我们措手不及的是 runC 的版本,如图 3 所示。

屏幕截图显示了在 2016 年 10 月 1 日发布的 runC v1.0.0-rc2 上运行的 Azure 容器实例。


图 3. ACI 中使用的容器 runtime。


RunC v1.0.0-rc2 于 2016 年 10 月 1 日发布,并且容易受到至少两个容器逃逸漏洞的攻击。

https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736/

ACI 中存在这个旧版本的 runC,使用这个容器映像对其进行优化并将其部署到 ACI。通过 cve-2019-5736 成功突破容器,并获得了一个在底层主机上以 root 身份运行的反向 shell,主机是一个 Kubernetes 节点(Node)。

https://docs.paloaltonetworks.com/prisma/prisma-cloud/20-12/prisma-cloud-compute-edition-admin/runtime_defense/incident_types/reverse_shell.html

一旦我们发现 ACI 中存在这个旧版本的 runC,我们就采用了当时开发的 PoC 容器映像,对其进行了优化并将其部署到 ACI。 我们成功地突破了容器,并获得了一个在底层主机上以 root 身份运行的反向 shell,结果证明它是一个 Kubernetes 节点。 在这里,我们展示了利用 CVE-2019-5736 来逃避我们的 ACI 容器的过程的屏幕截图。


图 4. 利用 CVE-2019-5736 来逃逸 ACI 容器。


虽然实现了容器逃逸,但仍然在租户边界内—— VM 节点 。CaaS 平台可以抵御复杂的攻击者,这些攻击者可以通过内核漏洞实现权限提升和容器逃逸。

该图显示了租户边界如何防范恶意客户。 虽然我们逃离了我们的容器,但我们仍然在租户边界内——节点 VM。 CaaS 平台旨在抵御复杂的攻击者,这些攻击者拥有内核漏洞,可实现权限提升和容器突破。 恶意容器爆发是一种预期的威胁,通过节点级隔离可以容忍。


图 5. 获取节点权限后任然在 node 中。


0x05 探测 K8s 节点环境

探测扫描节点可以验证当前容器是唯一的客户容器。使用 Kubelet 凭据,我们列出了集群中的 pod 和节点。该集群托管了大约 100 个客户 pod,拥有大约 120 个节点。每个客户都被分配了一个 Kubernetes 命名空间,他们的 pod 在其中运行;我们的容器 ID 是 caas-d98056cf86924d0fad1159XXXXXXXXXX。

https://kubernetes.io/docs/concepts/overview/components/#kubelet

可以看到节点的 Kubelet 允许匿名访问,尝试访问相邻节点上的 Kubelet。所有尝试访问相邻节点的请求都超时了,可能是由于防火墙配置阻止了工作节点之间的通信。

节点在 kubernetes.azure.com/cluster 标签中引用了集群名称,格式如下:CAAS-PROD-< LOCATION >-LINUX-< ID >。


图 6. 集群名称。


我们部署了几个分支容器,它们在不同的 Kubernetes 集群节点上。发现每个集群节点都有唯一的集群 ID,范围在 1-125 之间。这些集群 ID 表明托管了几十个集群节点。

1.Kubernetes 1 day

接下来,我们检查了集群的 Kubernetes 版本。

Azure 容器实例托管在运行 Kubernetes v1.8.4、v1.9.10 或 v1.10.9 的群集上,如下所示。 这些版本于 2017 年 11 月至 2018 年 10 月期间发布,容易受到多个已知漏洞的攻击。


图 7. ACI Kubernetes 版本。


ACI 托管在运行 Kubernetes v1.8.4、v1.9.10 或 v1.10.9 的集群上。这些版本于 2017 年 11 月至 2018 年 10 月期间发布,容易受到多个已知漏洞的攻击。运行较旧的 Kubernetes 版本有较大的风险,但它不一定会导致 ACI 中的安全问题。

回顾过去的 Kubernetes 漏洞,寻找可以通过被控节点提升权限或访问其他节点的漏洞,目标锁定: CVE-2018-1002102。

https://github.com/kubernetes/kubernetes/issues/85867

2.Kubernetes CVE-2018-1002102

当为 kubectl exec < pod > < cmd >命令提供服务时,api-server 会将请求推迟到适当的 Kubelet 的 / exec 端点。

CVE-2018-1002102 是 api-server 与 Kubelets 通信中存在的漏洞,可以实现重定向。通过将 api-server 的请求重定向到另一个节点的 Kubelet,恶意 Kubelet 命令可以在集群中传播。图 8 展示了漏洞的基本流程:

CVE-2018-1002102 的基本流程:1 ) 由 api-server 服务的命令,2 ) api-server 将请求延迟到适当的端点,3 ) 302 重定向,4 ) 通过集群传播。


图 8. CVE-2018-1002102 利用流程。


漏洞利用前提条件:

存在漏洞的 api-server 版本:

获取一个节点权限:

通过 api-server 可以使被控节点与其他节点通信。例如,可以通过向被控节点上的 pod 发出 kubectl exec 来完成。

事实证明,ACI 满足第三个前提条件。ACI 支持通过镜像 kubectl exec 的 az container exec 命令在上传的容器上执行命令。

az container exec --name --exec-command

创建一个利用 CVE-2018-1002102 的自定义 Kubelet 映像,将传入的 exec 请求重定向到其他节点上的 pod。为了最大限度地发挥作用,将其配置为以 api-server pod 为目标,最后,运行 az container exec my-ctr --exec-command /bin/bash,希望能在 api-server 容器上建立一个 shell。

执行命令失败了,经过一些调试,我们注意到重定向操作仅在目标容器托管在同一节点上时才有效。这有效地消除了攻击,因为我们无法传播到其他节点。分析 CVE-2018-1002102 的补丁,确实对该漏洞的修复。

https://github.com/kubernetes/kubernetes/pull/66516

重新检查到达节点的 exec 请求,请求可能会从 api-server IP 到达,如图 8 所示。但是,求来自在默认命名空间中运行的 "bridge" pod。

重新检查到达节点的 exec 请求后发现,这些请求源自在默认命名空间中运行的被称为 " 网桥 " 的 pod,如图所示。


图 9.az container exec 会话期间的 Kubelet 连接。


我们发现 ACI 将 exec 请求的处理从 api-server 转移到自定义服务。这可能是通过将 az 容器 exec 命令路由到桥接容器而不是 api-server 来实现的。

我们发现 ACI 将 exec 请求的处理从 api-server 转移到自定义服务。 这可能是通过将 az 容器 exec 命令路由到桥接容器而不是 api-server 来实现的。


图 10. Bridge Pod 处理 ACI 中的执行程序。


网桥镜像标签是 master_20201125.1,表明它是在 CVE-2018-1002102 之后更新的。从其最近的构建时间和拒绝重定向 exec 请求来看,似乎 CVE-2018-1002102 的补丁已经 patch 到了 bridge 上。微软应该是注意到了此漏洞影响了他们的自定义网桥 Pod 并进行了修补。

值得一提的是,CVE-2018-1002102 也可以在其他情况下被利用,例如,当客户端要求恶意 Kubelet 检索容器日志(例如 kubectl 日志)时。这实际上与 ACI 相关,其中此功能是通过 az container logs 命令实现的。但与 exec 请求一样,ACI 将日志检索的处理推迟到适当的 log-fetch 专用 pod 上 。与 Bridge Pod 一样,CVE-2018-1002102 的修复程序也被移植到 log-fetch Pod,以防止被利用。

0x06 获取集群管理员权限

CVE-2018-1002102 不在讨论范围内,但我们在调试漏洞利用时确实注意到了一些奇怪的事情。到达节点的 exec 请求包含一个 Authorization Header 头,其中包含一个 Kubernetes 服务帐户令牌,如图 11 所示。

到达节点的 exec 请求包含一个 Authorization 标头,其中包含一个 Kubernetes 服务帐户令牌,如图所示。


图 11. Bridge 发送带有服务帐户令牌的 "exec" 请求。


在这里找到令牌令人惊讶。如前所述,集群中的 Kubelet 被配置为允许匿名访问,因此请求不需要通过令牌进行身份验证,也许是旧版本中遗留的令牌。

Kubernetes 服务帐户令牌是未加密的 JSON Web 令牌 ( JWT ) ,因此它们是可解码的。如下所示,接收到的令牌是 "bridge" 服务帐户的服务帐户令牌。考虑到来自桥接 Pod 的请求,这是有道理的。

https://jwt.io/

收到的令牌是 " 网桥 " 服务帐户的服务帐户令牌。 考虑到来自桥接 Pod 的请求,这是有道理的。


图 12. 已解码的桥接服务帐户令牌。


如果你要运行 Kubernetes,一定要注意要将服务帐户令牌发送给谁:任何收到令牌的人都可以自由使用它并伪装成其所有者。令牌窃取者很可能对他们被盗令牌的权限非常感兴趣。api-server 公开了两个允许客户端查询其权限的 API,SelfSubjectAccessReview 和 SelfSubjectRulesReview。kubectl 提供了 kubectl auth can-i 作为访问这些 API 的便捷方式。

以下是默认命名空间中 "Bridge" 令牌的权限:

这显示了默认命名空间中 " 桥 " 令牌的权限。


图 13. 桥接令牌权限——默认命名空间。


查看其他命名空间,权限是一致的,表明它们是集群范围的(而不是命名空间范围的)。以下是 kube-system 命名空间中令牌的权限。尝试确定允许我们在多租户集群中传播的权限:

这显示了 kube-system 命名空间中令牌的权限,包括 pods/exec 权限,表明令牌可用于在集群中的任何 Pod 上执行命令。


图 14. 桥接令牌权限 - kube-system 命名空间。


经验丰富的 Kubernetes 安全人员可能已经确定了 pods/exec 权限,这表明该令牌可用于在集群中的任何 pod 上执行命令——包括 api-server pod!图 15 显示了在 api-server 容器上打开 shell 的令牌:

这显示了在 api-server 容器上打开 shell 的令牌。


图 15. 使用网桥的令牌在 api-server 上弹出一个 shell。


我们刚刚完成了一次跨账户攻击。通过在 api 服务器上执行代码,我们现在是集群管理员了,可以完全控制多租户集群和其中的所有客户容器 : )

0x07 Azurescape 攻击总结

让我们总结一下恶意 Azure 客户可能通过托管 ACI 的多租户 Kubernetes 集群获得管理权限的步骤:

将利用 CVE-2019-5736 的镜像部署到 ACI。恶意镜像在执行时在底层节点上实现逃逸代码执行。

在节点上,监听 Kubelet 端口 10250 上的流量,并等待在 Authorization 标头中包含 JWT 令牌的请求。

发出 az container exec 以在上传的容器上运行命令。桥接 Pod 将向受感染节点上的 Kubelet 发送 exec 请求。

在节点上,从请求的 Authorization 标头中提取桥接令牌,并使用它在 api-server 上获取 shell。

以下视频演示了该攻击:

https://youtu.be/YfZBwKP18CQ

恶意 Azure 用户可以破坏托管 ACI 的多租户 Kubernetes 集群。作为集群管理员,攻击者可以在其他客户容器中执行命令、泄露部署到平台的敏感信息和私有映像,或者部署加密挖矿程序。

0x08 获取 Cluster 权限的另一种方法:Bridge SSRF

某些功能仅在 Kubernetes 上受支持,例如 gitRepo volume。如果 ACI 容器使用了此类功能,并且它部署在 Kubernetes 集群上,这意味着其他容器可能会控制 Kubernetes。私有虚拟网络中的容器就是这种情况。

https://docs.microsoft.com/en-us/azure/container-instances/container-instances-volume-gitrepo

https://docs.microsoft.com/en-us/azure/container-instances/container-instances-vnet

我们发现的第二个问题是网桥 Pod 中的服务器端请求伪造 ( SSRF ) 漏洞。

当桥接 Pod 为 az 容器 exec< ctr > < cmd >命令提供服务时,它会向相应的 Kubelet 的 /exec 端点发送请求。桥接器根据 Kubelet 的 /exec 端点的 API 规范构造请求,生成以下 URL:

https://< nodeIP >:10250/exec///?command=&error=1&input=1&output=1&tty=1

Bridge 必须以某种方式填充 中缺少的参数,< nodeIP >的值是从客户 pod 的 status.hostIP 字段中检索到的。发现这一点非常有趣,因为节点有权更新其 pod 的状态(例如,为了将其 pod 的 status.state 字段更新为 Running、Terminated 等)。

我尝试使用受感染节点的凭据更改 pod 的 status.hostIP 字段,确实起作用了,但在一两秒钟后,api-server 将 hostIP 字段更正为其原始值。尽管更改没有持续存在,但我们可以重复更新此字段。

我们编写了一个小脚本来反复更新 pod 的状态,并使用它来将 status.hostIP 字段设置为 1.1.1.1。然后发送了一个 az container exec 命令。命令执行失败,验证网桥将 exec 请求发送到 1.1.1.1 而不是真实节点 IP。我们开始考虑哪些特制的 hostIP 可以诱使网桥在其他 pod 上执行命令。

简单地将 pod 的 status.hostIP 设置为另一个节点的 IP 也没有成功。Kubelets 只接受指向它们托管的容器的请求,即使 Bridge 将 exec 请求发送到另一个 Kubelet 的 IP,URL 仍将指向我们的命名空间、pod 名称和容器名称。

然后我们意识到 api-server 实际上并没有验证 status.hostIP 值是否是一个有效的 IP,并且会接受任何字符串——包括 URL 组件。经过几次尝试,我们想出了一个 hostIP 值,它会诱使网桥在 api-server 容器而不是我们的容器上执行命令:

:10250/exec/kube-system//?command=&error=1&input=1&output=1&tty=1#

此 hostIP 值将导致网桥将 exec 请求发送到以下 URL:

https:// :10250/exec/kube-system//?command=&error=1&input=1&output=1&tty=1 # :10250/exec///?command=&error=1&input=1&output=1&tty=1

我们将 pod 的 status.hostIP 设置为此值并通过 az container exec 执行命令。攻击成功了!我们拿到的不是容器的 shell,而是 api-server 容器的 shell。完整的攻击可以在以下视频中看到:

https://youtu.be/7Alea_9oZgU

欺骗 Bridge 打开 api-server 上的 shell。

这里的影响与之前的攻击完全相同——对多租户集群的完全管理控制。

0x09 研究总结

跨账户漏洞是公有云的 " 噩梦 "。Azurescape 证明它们比我们想象的更真实。云提供商在保护他们的平台方面投入了大量资金,但不可避免地会存在未知的 0 day 漏洞并使用户面临风险。云用户应该对云安全采取深度防御方法,以确保控制和检测漏洞,无论威胁来自外部还是来自平台本身。

我们是设计师、工程师、梦想者,是您扬帆出海的私人顾问专家


相关内容:
[亚马逊开店深圳办事处地址在哪里]
[亚马逊开店深圳办事处地址在哪里]
亚马逊开店深圳办事处地址揭秘:一站式开店服务,轻松拥抱财富!各位亲爱的创业者们,你们好!今天要给大家带来一个好消息——亚马逊开店深圳办事处地址终于揭开了神秘面纱!在这里,
亚马逊开店卖翡翠怎么样?
亚马逊开店卖翡翠怎么样?
亚马逊开店卖翡翠:珠宝行业的巨大商机等你来挖掘!在炎热的夏季,一杯清凉的饮料、一本好书和一个精美的翡翠饰品,想必是很多人的首选。翡翠作为中国传统文化中的瑰宝之一,以其晶莹

TG客服:@SSjiejie — 官方频道:@SSwangluo

三生网络 © 2009-2023 超15年出海经验,跨境项目专家