AzureScape:ACI容器中的K8s跨账户集群接管研究 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

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

h1apwn 技术 2021-09-23 11:45:00
274204
收藏

导语:Azure 容器实例(ACI) 是 Azure 的容器即服务 (CaaS) 产品,ACI)使客户能够在 Azure 中运行容器而无需管理底层服务器。最近有研究人员发现并向 Microsoft 披露了 ACI 中的安全漏洞。

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 服务器如何与为三个不同客户运行的三个工作节点相关联。 租户边界用红色虚线表示。

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

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

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

0x03 AzureScape 攻击场景

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

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

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 容器实例。 

屏幕截图显示了在 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 容器的过程的屏幕截图。

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

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

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

该图显示了租户边界如何防范恶意客户。 虽然我们逃离了我们的容器,但我们仍然在租户边界内——节点 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 >。

节点在 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 月期间发布,容易受到多个已知漏洞的攻击。

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) 通过集群传播。 

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,如图所示。 

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

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

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

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

word-image-21.png

图 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 服务帐户令牌,如图所示。

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

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

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

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

https://jwt.io/

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

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

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

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

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

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

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

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

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

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

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

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

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

这显示了在 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/< customer-namespace >/< customer-pod >/< customer-ctr >?command=< url-encoded-cmd >&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 容器而不是我们的容器上执行命令:

< apiserver-nodeIP > :10250/exec/kube-system/< apiserver-pod >/< apiserver-container >?command=< url-encoded-command >&error=1&input=1&output=1&tty=1#

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

https:// < apiserver-nodeIP >:10250/exec/kube-system/< apiserver-pod-name >/< apiserver-ctr >?command=< url-encoded-command >&error=1&input=1&output=1&tty=1 # :10250/exec/< customer-namespace >/< customer-pod-name >/< customer-ctr-name >?command=< 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漏洞并使用户面临风险。云用户应该对云安全采取深度防御方法,以确保控制和检测漏洞,无论威胁来自外部还是来自平台本身。

本文翻译自:https://unit42.paloaltonetworks.com/azure-container-instances/如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务