通过攻击命名管道实现权限升级是我研究Windows Docker时发现的,它需要安装 Docker Desktop for Windows,我在其中发现很多Docker进程。由于其中一些进程是保密的,因此我进行了进一步探索。分析发现,这些进程使用命名管道进行通信,其中一种方法是将API调用从低权限用户转发到高级权限服务。了解API是发现漏洞的关键。
本文,我们将详细介绍在Docker Desktop for Windows中发现的六个权限升级漏洞的详细信息,以及CyberArk开发的名为“PipeViewer”的新工具,该工具可以帮助我们扫描具有低权限的Windows命名管道。在第一部分中,我们将重点关注一切是如何开始的,并展示导致我们在Windows上完全升级权限的特定漏洞。
在这篇文章中,我们将分享在Windows Docker Desktop中寻找以下漏洞的过程:
完全权限升级—— CVE-2022-25365
为什么有这么多进程?
对于很多研究Docker的人来说,他们会很兴奋地听到可以在Windows中运行Docker,而不是像以前那样只能在Linux中运行。
先从安装Docker Desktop for Windows开始,这是一个用c#编写的软件,由Docker制作,用于支持Windows环境中的Docker。它为使用者提供了创建、编辑、停止、更改配置等方法。安装完成后(从Docker的网站上),我们看到它使用了很多进程。
Docker Desktop进程列表
通过查看进程列表,我们看到了两个服务,Dockerd和com. Docker.service,这似乎值得仔细研究,因为这两个服务使用SYSTEM权限运行,并与许多其他资源通信。我们认为有必要检查一下我们是否可以根据我们的需要来影响它们。但在此之前,让我们先了解一下这两个服务之间的关系。
Desktop (Docker Desktop.exe) 的主要进程是一个低权限的GUI应用程序。它与服务com. Docker.service通信,后者将一些请求转发给 Dockerd服务,最终 Dockerd与容器通信。
配有服务的Docker Desktop图释
在每一项漏洞研究中,我们都必须了解具有不同权限的实体之间的通信方式,因为这是在不同进程之间找到EoP漏洞渠道的好地方。此时,我们有必要了解 Docker Desktop和com. Docker.service以及其他后台进程之间的通信。简而言之就是管道,很多管道。
通过对Docker Desktop代码及其依赖关系库的简单浏览,我们可以看到它使用的是Windows命名管道。
Docker.Core.dll中的类PipeNames
命名管道是服务器管道和一个或多个客户端管道之间的一种通信方式。任何进程都可以访问命名管道,使它们成为相关或无关进程之间的一种简单通信形式。
我们搜索了更多的命名管道,但覆盖其余的进程代码是耗时且不必要的,特别是因为其中一些不是用C#编写的。因此,我们通过IO Ninja(一个多功能终端仿真器、嗅探器和协议分析器)监控每个创建的命名管道。当我们开始捕获命名管道并运行 Docker Desktop时,我们收到了很多信息(如下图所示)。
使用IO Ninja监视Docker管道
如果我们仔细观察,便可以看到有一个名为“服务器文件打开(Server file opened)”的事件,这意味着出现在“process:”字段中的进程创建了管道。
在上述分析之后,我们将所有命名管道映射到一个表,或者至少映射到其中的大多数管道(大约40个管道),这样我们就可以看到每个命名管道的起源并检查其权限。
在许多管道中,我们将其缩小到大约三个,这些管道是由高级权限服务创建的,看起来很有被利用的可能性。最后,我们将重点放在一个名为 DockerBackendV2的特定管道上。
PipeViewer——命名管道查看器工具
在进行这项研究时,我们构建了一个工具,可以帮助我们查看系统上当前的所有命名管道及其权限,我们将这个工具命名为“PipeViewer”。
PipeViewer允许我们过滤所有正在运行的Docker命名管道。这样,我们就可以看到,作为低权限Docker用户组的一部分,由于我们对命名管道具有读写权限,这意味着我们可以与该命名管道通信。
PipeViewer显示我们对命名管道具有读写权限
在其他情况下,当你希望检查是否存在可与之通信的易受攻击的命名管道时,此工具可以提供帮助。现在让我们回过头来研究我们命名的管道。
公开 DockerBackendV2未记录API
命名管道dockerBackendV2是由服务com.docker.service创建的。通过仔细研究Docker。在com.docker.service.exe中的服务类中,我们看到它启动了dockerBackendV2服务器。
启动dockerBackendV2服务器
虽然该服务的主要代码似乎不太有趣,但它使用了有趣的库:Docker.Core.dll和 Docker.Backend.dll。
Docker库
如果我们查看Docker.Core.dll中的类BackendAPIPipeResolver,似乎命名管道DockerBackendV2是作为HTTP REST API实现的,这为我们提供了可以与之通信的第一条线索。
通过Http创建 DockerBacnendV2命名管道
在Docker.Backend.dll中,我们发现在命名空间Docker.Backend.HttpAPI下有7个类(基于4.11.1版本)。每个类都有一个路由前缀和子路由,每个路由都是一个REST API方法,将由com.docker.service.exe以SYSTEM权限执行。我们映射了REST API方法以获得更好的概述,通过这些方法,我们看到了主路由、调用方式及其用法。
Docker用户组的问题
幸运的是,我们可以从低权限用户调用任何REST API方法。然而,用户必须是 Docker用户组的一部分(正如我们之前在PipeViewer中看到的那样)。
这个组没有特殊的权限,它的存在只是为了让用户能够使用Docker,但实际上,它拥有很高的权限。
拥有此组允许低权限用户创建装载到主机“C:\Windows”的 Docker,即使用户无权访问该路径。
他们将能够访问 Docker,并从 Docker访问受保护的路径,如下所示:
通过这种方式,你可以轻松地将权限提升到SYSTEM级别。Docker已经意识到了这个问题,正如他们在回复所提到的:
“这是WindowsDocker的一个已知问题。不幸的是,这超出了我们的控制范围(这是一个操作系统功能),但我们在https://docs.docker.com/desktop/windows/permission-requirements/#windows-containers上记录了它,并为管理员提供了一个标志,如果他们愿意,可以在安装时禁用WindowsDocker。”
请注意,该回复是在研究人员人员报告漏洞后创建的,他们从4.11版本中添加了一个新标志(-no-windows-containers),可以阻止创建WindowsDocker,但其仍然是 Docker用户组的一部分。但是我们发现的漏洞仍然绕过了它,因为我们能够直接与API通信。
现在,我们需要查看API调用表搜索第一个可能被滥用的API方法。在那里,我们得到了第一条线索。
从移动数据文件夹API到完全权限升级
引起我注意的一个比较特殊的方法是HyperVController类(Docker.Backend.HttpAPI命名空间)下移动数据文件夹,它定义了hyperv控制器。从名称可以理解该方法将目录文件移动到不同的位置。我们已经知道它是以高权限执行的。唯一剩下的就是调用它,看看我们是否可以将文件移动到受保护的位置。但我们如何调用该方法?
Docker.Backend.HttpAPI命名空间HyperVController类下的移动数据文件夹API
搜索不久后,Docker.CoreBackendAPI命名空间下的一个名为ServiceAPIClient的类(位于 Docker.Core.dll内)显示了如何调用该方法。因为这是C#代码,我们可以看到它是如何被调用的,所以我们可以使用相同的类并调用此方法。
从Docker.Core.BackendAPI.ServiceAPIClient (Docker.Core.dll)调用 “hyperv/move-data-folder” API
现在我们已经了解了这个方法的作用。
移动数据文件夹API逻辑
move-datafolder方法接收两个参数:old\source目录和new\target目录。然后它将所有内容从旧目录移动到新目录,但有一些例外:
根目录和子目录(如果有)必须有一个名为 DockerDesktop.vhdx的文件。
从根目录中,只会移动名为 DockerDesktop.vhdx的文件,但从子目录中,会移动所有文件。
我们使用自己创建的两个目录执行了该方法,并绘制了控制流的工作原理图。
MoveDataFolder逻辑流程图
你可能还记得服务com.docker.service以SYSTEM权限运行,下面就让我们看看其中的原理。
下一步是再次调用move-data-folder函数,但这次是在C:\Windows这样的特权目标位置。当然,它是有效的,但令我们惊讶的是,它没有继承C:\Windows的权限,这意味着我们可以控制复制的文件,这比我们预期的要好。
控制复制的文件
根据微软的说法,这背后的原因是,将文件移动到同一卷中的不同目录(在本例中为NTFS),保留了原始权限。在本例中,move-data-folder方法使用file.move将文件移动到同一卷中的不同位置,从而允许我们根据需要编辑文件。我们可以通过查看Procmon中的SetRenameInformationFile操作看到这一点。
使用SetRenameInformationFile重命名文件
这样一来,一切就很容易了。我们只需要更改文件名,就可以使用一个简单的已知DLL劫持来获取SYSTEM shell。但是,即使我们完全控制了文件,它也位于受保护的位置,Windows文件保护(WFP)阻止我们重命名它。
Windows文件保护(WFP)防止重命名文件
绕过Windows文件保护障碍
为了绕过这个限制,我们决定使用连接目录和对象管理器符号链接的组合。我们没有直接将文件移动到C:\Windows,因为这会阻止我们更改文件的名称,而是使用了一个代理目录并将文件移动到一个连接目录,我们将其命名为“Jumper”。该目录有一个从 DockerDesktop.vhdx到DLL的链接,我们可以稍后利用该链接进行攻击。例如,一个已知的易受攻击的PrinterSpooler DLL劫持二进制路径C:\Windows\system32\ualapi.dll。通过这种方式,我们可以控制文件名,并将其移动到连接目录,然后将其重定向到具有我们选择的名称的路径。
最后一步是调用move-data-folder,其中C:\tmp\ABC作为包含恶意文件 DockerDesktop.vhdx的旧目录参数,C:\tmp\Jumper作为新目录参数,这是一个带有对象管理器符号链接的连接目录,指向C:\Windows\system32\ualapi.dll(已知的dll劫持路径)。服务将C:\tmp\ABC\ DockerDesktop.vhdx移动到C:\tmp\jumper\ DockerDesktop.vhdx,该文件重定向到\RPC Control\ DockerDesktop.vhdx,然后转到\??\C:\windows\system32\ualapi.dll。
漏洞利用图示
我们可以通过使用Procmon看到上面描述的过程(图16)。它调用SetRenameInformationFile来移动DockerDesktop文件。vhdx到连接目录C:\tmp\jumper,重定向到对象管理器符号链接(C:\Windows\System32\ualapi.dll),但Procmon没有正确处理它,也没有显示文件名目的地。您可以看到Detail列下的FileName是空的(在列表的末尾)。
利用过程的Procmon日志
在不重启计算机的情况下触发漏洞
现在我们只需要重新启动计算机,以便打印机后台处理程序服务加载被劫持的DLL并获得SYSTEM shell。但在真正的攻击中,重新启动计算机不是一个现实的场景,因为攻击者知道大多数人会注意到他们的计算机何时重新启动,而重新启动计算机可能需要很长时间。我记得读过Yarden Shafir和Alex Ionescu写的一篇名为“Faxing Your Way to SYSTEM”的文章,其中提到在一个低权限用户的特权服务中触发DLL劫持。我们使用了此技术在不重新启动计算机的情况下触发恶意DLL劫持。
Docker在Docker Desktop 4.5版本中发布了此问题的修复程序,并将其分配给CVE-2022-23774。然而,它只阻止将文件直接移动到受保护的位置,攻击者仍然可以绕过,因为它使用了非权限连接目录,间接地(使用对象管理器符号链接)指向权限位置。研究人员通知了Docker,他们在4.5.1版本发布了一个完整的修复程序,并将其分配给CVE-2022-25365。
总结
研究人员检查发现Docker使用了一个带有REST API的命名管道,允许攻击者从低权限用户调用其方法,并由特权服务执行操作。在这项研究中,我们还开发了一个名为“PipeViewer”的新开源工具,以帮助扫描Windows命名的管道并显示其权限。
参考及来源:https://www.cyberark.com/resources/threat-research-blog/breaking-docker-named-pipes-systematically-docker-desktop-privilege-escalation-part-1