CVE-2019-18276 Gun Bash条件竞争漏洞

最近在看K8S方面的漏洞,分析漏洞就看到了一个Gun Bash条件竞争的漏洞,漏洞的原因和复现比较简单,但是其中包含一些条件竞争TOCTOU和一些linux文件基础知识方面的东西,感觉值得记录一下,因此有了这篇文章:

1. 漏洞描述

Bash是一款为GNU计划而编写的、运行于类Unix操作系统中的Shell(命令语言解释器)。它能够从标准输入设备或文件中读取、执行命令。

Bash 5.0 patch 11及之前版本中的shell.c文件的disable_priv_mode存在安全漏洞。攻击者可利用该漏洞获取权限。

注意这个漏洞是发生在Bash组件上的漏洞,虽然很多的操作系统都会自带这个Bash程序,但是不能以次就说是操作系统的漏洞。并且经过分析这个漏洞实际作用有限,下面就是对于这个漏洞的完整分析

2. 漏洞分析

定位到漏洞发生时候的commit,查找漏洞的点在于shell.c的disable_priv_mode()函数,这里地方离程序的入口很近,是Bash刚执行时的权限检查函数,按照bash的设计者的设计思路,bash是由哪个用户启动的那么进入bash后就拥有哪个用户的权限,但是Linux文件权限权限中又有一个SetUID标志位:

  • SetUID标志位
SetUID:当一个可执行程序具有SetUID权限,用户执行这个程序时,将以这个程序所有者的身份执行。前提是这个文件是可执行文件,可就是具有x权限(属组必须先设置相应的x权限)。
操作系统中很多的命令都默认设置了SetUID标志位,以方便非root用户操作系统某些功能。例/usr/bin/sudo,/usr/bin/passwd等。虽然设置了SetUID标志位后,其他用户也可以调用本来只有root用户才能调用的passwd命令。

理解了SetUID标志位,但是在实际中,虽然root用户和普通用户都可以调用root指令,但是在普通用户在执行passwd指令后只能修改自己的密码,root用户执行passwd指令后可以修改root账户的密码,这里又涉及了Linux进程权限的内容了:

  • linux进程凭证(权限)
    进程的信任凭据放在进程描述符的几个字段中,这些字段包含:
字段名称 字段说明
ruid (实际用户id) 进程是由哪个用户启动的
euid (有效用户id) 有效用户ID 当前进程是以哪个用户ID来运行的 一般情况下与UID 相同,但在可执行程序设置 SUID 权限后未可执行程序所有者的ID, 操作系统是通过EUID来判断权限的
suid (保存用户id) 有效用户UID的一个副本
fuid (文件系统用户id) 决定文件系统的访问权限

还是以passwd命令举例,如果以root权限运行,那么ruid,euid,suid和fuid都是root权限的id,但是以普通权限用户运行,那么ruid,euid,suid和fuid都将是0;但是如果以普通用户运行passwd命令,那么ruid将会是普通用户的用户ID。
那如果以普通用户的权限运行带有SetUID标志位的bash(虽然系统默认的bash是不带SetUID标志位的),那么将会是以何种权限呢?答案是普通用户的权限。因为在bash开始时候会获取当前进程的创建者的id(ruid),并以实际创建者的权限运行。权限改变的过程中会使用到setuid函数。

  • setuid 函数
    看看对官方对 setuid()函数的解释:

CVE-2019-18276 Gun Bash条件竞争漏洞_第1张图片

个人理解呢,代码在调用setuid()函数的时候可以调用可以简单分析为如下情形:

  1. 以root权限启动进程,可以设置 ruid、uuid、suid、fsuid为函数参数uid的值;
  2. 以普通用户权限启动所属者为root且包含s标志位的进程, 可以设置 ruid、uuid、suid、fsuid为函数参数uid的值
  3. 以普通用户权限启动所属者为自己的进程/不含s标志位的进程 传入uid必须等于 UID或SUID 可以设置 EUID 和 FSUID 这个当没说,因为EUID肯定是等于 UID的
  4. 以普通用户权限启动所属者为其他普通用户的进程 setuid()传入的 uid 必须等于 UID或SUID, 可以设置 EUID 和 FSUID为传入参数uid的值

这里看看bash开头的实现,在disable_priv_mode()函数中直接调用setuid (current_user.uid),如果bash的所有者为root且含有setuid标志位,那么ruid、uuid、suid、fsuid都将会被修改为uid,此种情况下不存在越权。当bash的所有者为其他用户且含有setuid标志位,那么setuid函数执行后会将euid设置为传入的参数uid,不会改变suid的值。

然而问题就在这个地方,bash中在disable_priv_mode()函数中调用setuid (current_user.uid)的目的当bash带有setuid标志位且拥有者为其他用户时调整bash的权限为启动bash的用户(通过函数名称disable_priv_mode也可以验证这个调用setuid()函数的目的是这样),但是在改变euid后并没有修改suid,后续函数可以再次调用setuid()函数将特权恢复,因此此处存在一个权限控制不当漏洞。

利用过程

这个漏洞的利用就比较奇葩了,创建A,B两个用户,然后将bash的拥有者设置为B然后设置bash可执行程序setuid标志位,这样A用户在调用bash可执行程序之后,在bash进程自身调用玩setuid函数之后,如果程序再次调用setuid并传入B用户的UID,bash进程将恢复进程EUID权限为B用户权限,A用户至此可以访问到一些A用户本不能访问的资源,调用setuid()的源代码如下:

#include 
#include 
#include 

void __attribute((constructor)) initLibrary(void) {
        printf("Escape lib is initialized\n");
        printf("[LO] uid:%d | euid:%d\n", getuid(), geteuid());
        setuid($euid);
        printf("[LO] uid:%d | euid:%d\n\n\n", getuid(), geteuid());
}

具体测试细节可参考:https://github.com/M-ensimag/...

总结

最近在做Linux和虚拟化相关的漏洞研究,发现除了传统内存破坏性漏洞以外还有挺多这种逻辑性的漏洞,自己也有搭建syzkaller来fuzz内核,但是觉得syzkaller无论怎么修改应该都不能大量的产出漏洞了,反倒是这种不能fuzz出来的逻辑漏洞可能还有点戏。准备以后每周都写点类似的东西,和大家一起成长!

参考资料

  1. 漏洞验证POC:https://github.com/M-ensimag/...
  2. 初探文件路径条件竞争:http://whip1ash.cn/2021/06/16...

你可能感兴趣的:(CVE-2019-18276 Gun Bash条件竞争漏洞)