The Docker Rodeo
在此引导式展示中了解各种 Docker 漏洞。
以下内容均来自TryHackMe
前提设置
/etc/docker/daemon.json
{
"insecure-registries" : ["docker-rodeo.thm:5000","docker-rodeo.thm:7000"]
}
Docker注册表
在我们开始利用 Docker 注册表之前,我们不仅需要首先了解我们如何与它们交互,还需要了解为什么它们对我们渗透测试人员来说如此有利可图。
Docker 注册表从根本上用于存储和提供已发布的 Docker 映像以供使用。使用存储库,Docker 映像的创建者可以在其应用程序的多个版本之间切换,并轻松地与其他人共享。
存在诸如DockerHub之类的公共注册表,但是,许多使用Docker的组织将托管自己的“私有”注册表。
以 RustScan DockerHub 注册表为例。开发人员为每个版本的 RustScan 创建了一个“标签”。由于这是公开的,任何人都可以通过下载他们要使用的标签的图像来轻松切换他们想要使用的 RustScan 版本。
与Docker注册表交互
与我们将要进行渗透测试的任何系统一样,我们需要枚举正在运行的服务以了解任何潜在的入口点。
Nmap 不仅能够发现 Docker 注册表,还能够发现 API 版本 - 这对于我们如何与之交互非常重要。
nmap -sV
Docker 注册表是一个 JSON 端点,因此我们不能像普通网站那样简单地与它进行交互 - 我们将不得不查询它。虽然这可以通过终端或浏览器完成
发现仓库
我们需要发送请求以列出在注册表上注册的所有存储库。
http://docker-rodeo.thm:5000/v2/_catalog
在开始分析存储库之前,我们需要两条关键信息:
- 存储库名称
- 已发布的任何存储库标记
我们目前有存储库名称(cmnatic/myapp1),现在我们只需要列出所有已发布的标签。每个存储库至少有一个标记。此标签是“latest”标签,但可以有许多标签,所有标签都有不同的代码,例如,主要软件版本或两个标签,用于“生产”和“开发”。
发送请求以查询所有已发布的标记。对于我们的应用程序,我们的请求如下所示:
http://docker-rodeo.thm:5000/v2/repository/name/tags/list
获取数据
有了关于存储库的这两个重要信息,我们可以枚举清单文件的特定存储库。 此清单文件包含有关应用程序的各种信息,例如大小、图层和其他信息。我将通过以下请求获取“notsecure”标签的清单文件 :
http://docker-rodeo.thm:5000/v2/cmnatic/myapp1/manifests/notsecure
注意响应 - 特别是“history”键;尽管有点难以阅读,但我们有一个在image构建阶段执行的命令以明文形式存储
逆向Dockers镜像
我们将继续执行任务 3 中概述的上一个漏洞。“滥用 Docker 注册表”。
正如我们所发现的,我们能够查询 Docker 注册表和其中包含的数据,而无需进行身份验证。
我们不仅可以查询 Docker 注册表,而且 Docker 的一个基本功能是能够下载这些存储库供他人自己使用。这称为图像;诸如潜水之类的工具,用于对我们下载的这些图像进行逆向工程。
如果不公正,当我们使用它来运行容器时,Dive 充当我们和 Docker 之间的中间人。Dive 在每个阶段监控并重新组装每个层的创建方式和容器文件系统。
我们将从一个例子开始。让我们从易受攻击的存储库中下载一个 Docker 映像并开始深入研究。
4.1. 从官方 GitHub 安装 Dive
4.2. 下载我们将要反编译的 Docker 镜像
docker pull docker-rodeo.thm:5000/dive/example
4.3. 找到我们在步骤 2 中下载的仓库镜像的IMAGE_ID:
-
4.3.1. 运行并查找我们下载的仓库的名称
docker imagesdocker-rodeo.thm:5000/dive/example
-
4.3.2. “IMAGE_ID”是第三列中的值:
在这种情况下,它对我来说是“398736241322”。
4.4 通过跑步开始dive,并提供我们要反编译的图像的“IMAGE_ID”。例如:
dive 398736241322
使用dive
dive一开始有点不知所措,但是,它很快就有意义了。我们有四种不同的观点,我们只对这三种观点感兴趣:
-
图层(红色)
此窗口显示了 docker 容器所经历的各个层和阶段
-
当前图层内容(以绿色显示)
此窗口显示容器文件系统在选定层的内容
-
图层详细信息(红色)
显示其他信息,例如图层的 ID 以及该图层在 Dockerfile 中执行的任何命令。
使用“向上”和“向下”箭头键在当前窗口中导航数据。
您可以使用“Tab”键在Windows之间切换。
上传恶意docker镜像
继续利用任务 3 中易受攻击的 Docker 注册表。“滥用 Docker 注册表”,我们可以将自己的映像上传到包含恶意代码的存储库。存储库可以具有所有者希望的标签数量或数量。但是,每个存储库都保证有一个“最新”标签。此标记是最新上传的图片的副本。push
发出docker pull or docker run命令时,Docker 将首先尝试在主机上查找映像的副本(即 cmnatic/myapp1),然后继续检查是否对从中提取的 Docker 注册表进行了任何更改。如果有更改,Docker 会将更新后的镜像下载到主机上,然后继续执行。
如果没有适当的身份验证,我们可以将自己的映像上传到目标的注册表。这样,下次所有者运行docker pull or docker run命令时,他们的主机将下载并执行我们的恶意映像,因为它将是 Docker 的新版本。
通过暴露的docker守护程序RCE
确认漏洞。
伟大!看起来它是开放的,我们将使用该命令开始与公开的 Docker 守护程序进行交互。curl
确认我们可以访问 Docker 守护进程:
curl http://10.10.199.129:2375/version
请注意,我们收到的回复将提供有关主机的各种数据
我们将使用 “-H” 开关来指定实例以列出正在运行的容器,从而执行
我们的第一个 Docker 命令
docker -H tcp://10.10.199.129:2375 ps
docker逃逸
寻找暴露的 Docker 套接字 有了我们在“漏洞 #4:通过暴露的 Docker 守护程序的 RCE”中了解到的有关 Docker 套接字
的知识,我们可以在容器中查找此文件的暴露情况,并确认当前用户是否有权使用 .groups
挂载主机卷
在这个房间的实例中,我已经将“高山”映像下载到您正在利用的容器中。在 THM 会议室中,您很可能必须先将此映像上传到容器,然后才能执行它,因为实例不会通过互联网连接进行部署。
现在我们已经确认可以执行 Docker 命令,让我们将主机目录挂载到一个新容器,然后连接到该容器以显示主机操作系统上的所有数据!
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
我们本质上是将主机 “/” 目录挂载到新容器中的 “/mnt” 目录,然后通过 shell 进行连接。
共享命名空间
我们在这里只会对命名空间感兴趣,毕竟,它们是它的核心。命名空间本质上是将系统资源(如进程、文件和内存)与其他命名空间隔离开来。
在 Linux 上运行的每个进程都将被分配两件事:
- 命名空间
- 进程标识符 (PID)
命名空间是实现容器化的方式!进程只能“看到”同一命名空间中的进程 - 理论上没有冲突。以Docker为例,每个新容器都将作为新的命名空间运行,尽管该容器可能运行多个应用程序(进而运行进程)。
我们可以在操作系统上使用进程 #1 的命名空间来提升我们的权限。虽然容器被设计为使用这些命名空间来隔离另一个命名空间,但它们可以与主机进程重合,而不是与主机进程隔离。这给了我们一个逃生的好机会!
此漏洞通常依赖于对容器的根权限,以便容器向主机上的命名空间公开。
使用以下漏洞利用: 执行以下操作:
nsenter --target 1 --mount sh
回顾
让我们回顾一下我们利用的漏洞。我们不仅了解了容器化技术,还了解了这些容器如何仅仅是主机操作系统的抽象。
10.1. 最小特权原则:
虽然这是整个 InfoSec 的首要主题,但我们将把它与 Docker 联系起来…
还记得 Docker 镜像吗?除非另有说明,否则这些映像中的命令将以 root 身份执行。假设您为 Web 服务器创建了一个 Docker 映像,在这种情况下,该服务将以 root 身份运行。如果攻击者设法利用 Web 服务器,他们现在将拥有容器的根权限,并且可能能够使用我们在任务 10 和 11 中概述的技术。
10.2. 码头工人安全 101:
Seccomp 或“安全计算”是 Linux 内核的一项安全功能,允许我们通过确定容器可以进行的系统调用来限制容器的功能。 Docker 对容器使用安全配置文件。 例如,我们可以拒绝容器执行诸如使用 mount 命名空间(有关此漏洞的演示,请参阅任务 10)或 Linux 系统调用 y 等操作的能力。
10.3. 保护您的守护进程:
在以后安装的 Docker 引擎中,运行注册表依赖于在 Web 服务器后面实现自签名 SSL 证书,然后必须在将与注册表交互的每台设备上分发和信任这些证书。对于想要设置快速环境的开发人员来说,这是相当麻烦的 - 这与 Docker 的全部观点背道而驰。
判断是否在容器中
列出正在运行的进程
容器由于其隔离性质,与虚拟机等相比,通常运行的进程很少。我们可以简单地用于打印正在运行的进程。请注意,在下面的屏幕截图中,正在运行的进程很少?ps aux
寻找 .dockerenv
容器允许使用“.dockerenv”文件从主机操作系统提供环境变量。此文件位于“/”目录中,并且将存在于容器中 - 即使未提供环境变量
那些讨厌的cgroups。
请注意我们如何在任务 10 中使用“cgroups”。Cgroups由容器化软件(如LXC或Docker)使用。让我们通过导航到“/proc/1”然后对“cgroups”文件进行分类来查找它们…值得一提的是,“cgroups”文件包含包含单词“docker”的路径
提供环境变量
那些讨厌的cgroups。
请注意我们如何在任务 10 中使用“cgroups”。Cgroups由容器化软件(如LXC或Docker)使用。让我们通过导航到“/proc/1”然后对“cgroups”文件进行分类来查找它们…值得一提的是,“cgroups”文件包含包含单词“docker”的路径
[外链图片转存中…(img-Lp3MsxQb-1674301800155)]