*本文原创作者:yolandamo,本文属于FreeBuf原创奖励计划,未经许可禁止转载
前言
IBM下有两款大型关系型数据库,分别是Informix和DB2。早在2001年和2003年两款数据库就被爆出存在符号链攻击提权的问题。尤其2003年Snosoft一口气发布了DB2数个关键工具存在符号链接攻击提权的问题。
符号链攻击虽然是一种本地攻击,但符号链攻击会造成非常严重的安全问题。例如:替换SSH公钥文件、操作系统敏感信息泄露甚至直接夺取操作系统root权限。
经过十余年的发展IBM本应该消灭了符号链攻击问题。但根据我们安华金和攻防实验室研究,发现各种“姿势”的IBM数据库符号链接漏洞。我们发现的典型漏洞分别是CVE-2017-1508、CVE-2018-1799、CVE-2018-1780、CVE-2018-1781和CVE-2018-1784。
一、符号链接简介
符号链接最早出现在4.2的BSD版本中,发展至今日,基本所有的主流操作系统都在某种程度上支持符号链接。符号链接(也称为软链接)是用来让用户创建指向另一个文件或目录的文件或目录。例如管理员可以创建一个名为test的符号链指向系统文件/etc/hosts文件。管理员访问test,等于访问/etc/hosts文件。
符号链接文件实际上是一种文件系统中的特殊小文件。他们的inode(简单可以认为是linux系统中文件的唯一编号)被标为符号链接类型。他们的实际文件内容其实是文件路径。当内核解析路径名时,如果遇到符号链接文件,他会读取符号链接中的文件路径,跟随一层一层的符号链接文件路径,最终到达文件。从而获得到目标文件的路径。
整个符号链接流程如下图所示:第一步访问符号链接test.txt。第二步从符号链接中读取到文件的路径。第三步基于第二步拿到的路径访问下一个软链接或文件。第四步经过层层抽丝剥茧最终获得真实路径。第五步在获得真实路径后根据真实路径跳转到上级目录。第六步再从上层目录跳入下层目录访问到要访问的真实文件。
二、符号链接攻击
符号链接攻击的本质就是欺骗程序访问不应该访问的文件。最常见的是用来获取敏感信息。有时候经过精妙的设计可以突破权限的限制,最终达到覆盖文件或夺取更高权限的效果。
假设有如下一段代码,此代码是用来在主目录中查看该用户是否有.optconfig文件。如果文件存在,程序将打开该文件并读入配置条目。大多数程序员会认为只要parse_opt_file()的文件解析功能是安全的,这种行为就是安全可靠的:
void start_processing(char *username)
{
char *homedir;
char tmpbuf[PATH_MAX];
int f;
homedir=get_users_homedir(username);
if (homedir)
{
snprintf(tmpbuf, sizeof(tmpbuf),
"%s/.optconfig", homedir);
if ((f=open(tmpbuf, O_RDONLY))>=0)
{
parse_opt_file(tmpbuf);
close(f);
}
free(homedir);
}
...
但如果攻击者在. Optconfig上做一些手脚。使用命令建立到/etc/shadow(存操作系统密码的文件) 的软链接(ln -s /etc/shadow ~/.optconfig)。此时攻击者在使用上述程序。程序会去解析. Optconfig文件。由于. Optconfig是一个符号链接,所以最终文件解析的是存有密码hash值的/etc/shadow文件。攻击者很容易通过这种手段拿到密码的hash值。威胁整个操作系统的安全。
三、提权C位-粘滞位
一次完美的符号链接攻击,除了必须有符号链接外,更重要的是要有提权的渠道。利用程序的粘滞位,是常见的提权方法。粘滞位加符号链接有机会造成一次本地提权攻击。
要解释清楚懂粘滞位。首先简单介绍几个linux权限。SUID 是 Set User ID,作用是让程序执行者具有该程序所有者的权限;如果没有设置,程序执行者具有执行用户的权限。SUID 位可以通过 chmod u+s 设置。
举个例子,在Linux中,所有账号的密码记录在 /etc/shadow文件中,并且只有root可以读写这个文件。那么,一个普通用户通过命令 passwd 修改自己密码的过程中肯定就需要写 /etc/shadow这个文件,之所以能这么做是因为 /usr/bin/passwd 设置了SUID位:
-rwsr-xr-x. 1 root root 30768 Feb 22 2012 /usr/bin/passwd
Linux 内核主要是根据 EUID(effective user id) 和 EGID(effective group id) 来确定进程对资源的访问权限。 如果进程对应的程序没有设置 SUID 或 SGID 位,则 euid=uid egid=gid,分别是执行这个程序的用户的 uid 和 gid;反之,则 euid 和 egid 变为程序所有者的 uid和 gid。
因此如果能拦截有粘滞位的程序在降低权限之前,实施越权访问或操作,就很有可能最终获得root权限或以root身份进行越权操作。
四、经典案例剖析(CVE-2017-1508)
理解了粘滞位和符号链接攻击的原理后,咱们剖析一个informix的符号链接漏洞,来深入理解下这类漏洞的利用“姿势”。CVE-2017-1508是一个标准的,通过低权限用户越权非法写文件,最终夺取操作系统root权限例子。
Informix中使用oninit程序可以启动数据库。Oninit启动数据库的时候会打开一个固定名称为/tmp/jvp.log的文件。如果在这个被我们用符号链替换,就可能造成一次符号链接攻击。更关键的是这个程序有粘滞位(下图中权限S)。粘滞位是为了帮助程序在启动时可以使用root权限完成一些操作。
除了上述两点外最重要的是oninit在访问/tmp/jvp.log时。并未进行降权处理。于是基于粘滞位、符号链接攻击和未及时降权。我们可以对oninit实施符号链接攻击。
根据逆向结果,可以看到访问/tmp/jvp.log需要开启环境变量GLSLOG = “on”。同时在打开fopen的时候未做符号链接文件的判断。通过符号链接攻击我们可以以root权限读取或写入任意文件。
五、符号链接攻击提权
至此符号链接攻击主要还停留在创建、读取和替换文件内容阶段。虽然符号链接可以通过读取/etc/shadow获得hash值。再尝试通过字典来破解root密码,但这种方法极不稳定又麻烦。这里我给大家演示一种稳定提权到root的方法。
Linux 系统有一个特点,为了方便的给基础库或函快速修改的机会。所以在加载器上开了一个后门。加载器除了加载一个程序的动态库外还会额外加载系统文件/etc/ld.so.preload 中的动态库。而如果在额外加载动态库中写入和系统函数同名的函数。又会因为ld.so.preload 比其他库早加载,最终导致ld.so.preload中库的同名函数函数覆盖真正系统上的同名函数。
由于可以覆盖同名库函数,所以我们就可以实现自己想要实现的任意功能。有粘滞位的程序基本都会使用geteuid获取当前euid值。我们只要替换掉系统的geteuid。就可以在系统中实现自己想要执行的任意代码。
如下例利用geteuid实现一个永久的bash_shell:
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("$BACKDOORPATH", 0, 0);
chmod("$BACKDOORPATH", 04777);
printf("Dbsec finishes making root shell.\n");
}
return old_geteuid();
六、符号链接攻击的防护手段
从符号链接漏洞到最终获得root shell。经历了两个关键点:1.存在粘滞位。2.访问文件前,未检查是否是符号链接。
第一点:粘滞位的问题主要看是否存在必要性如果无必要性就不要使用。SUID尤其是 SUID Root 程序是存在很大风险的。如果程序确实需要设置 SUID 位,代码中不再需要高权限时尽快通过调用 setuid() 降低权限,也就是使 euid=uid egid=gid。尽量缩短,危险代码的时间。
第二点:在需要写文件时,应该先判断要写的文件是否是已经存在的符号链接或硬链接文件。如果使用open() 函数,oflag 设置成O_NOFOLLOW。可以保证如果是符号链接文件,函数就会返回失败。防止被符号链接攻击。如果使用fopen()函数就必须在使用之前利用lstat() 判断文件的类型。防止被符号链接攻击。
做到上述两点检查,可以完全避免符号链接的攻击。至少也能把符号链接攻击限定在一个非常有限的范围内。
*本文原创作者:yolandamo,本文属于FreeBuf原创奖励计划,未经许可禁止转载