0x00 漏洞背景
2019年10月14日, sudo 官方在发布了 CVE-2019-14287 的漏洞预警。
sudo 是所有 unix操作系统(BSD, MacOS, GNU/Linux) 基本集成的一个用户权限控制/切换程序。允许管理员控制服务器下用户能够切换的用户权限。
CVE-2019-14287 是管理员在配置文件中用了 ALL 关键词后造成的。但默认的 sudo 配置文件不受影响。
360CERT判断漏洞等级为低危,影响面有限。
但由于是配置文件的开放性。还请服务管理/运维人员及时做好自查自检工作。
在你的Linux机器上运行一下命令。
sudo -V | grep 'Sudo version'
即可查看是否受到该版本(低于1.8.29)的影响。
0x01 漏洞详情
当 /etc/sudoers 文件存在如下形式的配置会导致漏洞的产生
user_name ALL=(ALL, !root) /usr/bin/vim
这种*=(ALL, *)
形式的配置,简单来说就是对 可以被切换到的用户进行了 ALL(所有用户) 和其他用户的剔除操作。
例如上面的含义就是:
允许 user_name
用户以非 root 权限之外的所有用户权限运行 vim
这样的配置应该会出现在比较少的特殊情况。
一般情况下,大多数Linux发行版的Runas规范(/etc /sudoers)都如下图所示,其中定义的ALL关键字将允许admin或sudo组中的用户以目标系统中的任意用户身份来运行命令
alice myhost = (ALL) /usr/bin/id
除了以任意有效用户的身份运行id命令之外,我们还能够以任意用户ID来运行该命令,此时需要使用#uid语句:
sudo -u#1234 id -u
该命令将返回“1234”。但是,sudo可以使用setresuid(2)和setreuid(2)这两个系统调用来在命令运行之前修改用户ID,并将用户ID修改为-1(或未签名的等价用户ID-4294967295):
sudo -u#-1 id -u
或sudo -u#4294967295 id -u
上述命令运行之后,将返回“0”。这是因为sudo命令本身已经在以用户ID“0”运行了,所以当sudo尝试将用户ID修改为“-1”时,不会发生任何变化。
但是,sudo日志条目中记录下的命令运行用户的ID为“4294967295”,而并非root用户(或用户ID为“0”),除此之外,因为用户ID是通过-u选项指定的,并且不会在密码数据库中存储,所以PAM会话模块也不会运行。
如果sudoer条目允许用户以任意用户身份运行命令(非root),那么攻击者就可以利用该漏洞来绕过这种限制了。比如说,我们有下列sudoer条目:
bob myhost = (ALL, !root) /usr/bin/vi
用户bob能够以除了root之外的其他任意用户身份来运行命令vi,但由于该漏洞的存在,bob实际上能够通过下列命令来以root权限运行vi命令,并绕过目标系统中的安全策略:
sudo -u#-1 vi
只有当包含了ALL关键词的sudoer条目存在于Runas规范中时,该漏洞才存在。比如说,如果规范中包含下列sudoer条目的话,目标系统是不会受到该漏洞影响的:
alice myhost = /usr/bin/id
在上述例子中,alice只能够以root权限运行id命令,任何以不同身份用户运行命令的尝试都将被拒绝。
0x02 源码分析
void exec_cmnd(struct command_details *details, struct command_status *cstat, int errfd)
{
debug_decl(exec_cmnd, SUDO_DEBUG_EXEC)
restore_signals();
if (exec_setup(details, NULL, -1) == true) {
/* headed for execve() */
sudo_debug_execve(SUDO_DEBUG_INFO, details->command,
details->argv, details->envp);
sudo_execve(details->command, details->argv, details->envp,
ISSET(details->flags, CD_NOEXEC));
cstat->type = CMD_ERRNO;
cstat->val = errno;
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to exec %s: %s",
details->command, strerror(errno));
}
debug_return;
}
其中exec_setup,SUD0_DEBUG_EXEC,可以执行组ID,及创建掩码
bool exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
{
bool rval = false;
debug_decl(exec_setup, SUDO_DEBUG_EXEC);
unlimit_nproc();
#ifdef HAVE_SETRESUID
if (setresuid(details->uid, details->euid, details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"), details->uid,
details->euid);
goto done;
}
#elif defined(HAVE_SETREUID)
if (setreuid(details->uid, details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"),
(unsigned int)details->uid, (unsigned int)details->euid);
goto done;
}
#else
if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
sudo_warn(U_("unable to change to runas uid (%u, %u)"), details->uid,
details->euid);
goto done;
}
#endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
/* Restore previous value of RLIMIT_NPROC. */
restore_nproc();
rval = true;
done:
debug_return_bool(rval);
}
通过源码分析
其中有三个函数可以设置用户权限
setresuid
setreuid
seteuid
其中的函数在root权限时参数可以改变为任何ID,
sudo程序最初会调用了setuid(root_uid)使程序的进程获得的root权限,通过前面的ls -l /usr/bin/sudo已经检验过了
所以这三个函数都能修改进程的用户所获得的权限。因为默认情况下sudo会将权限提升为root
在出现整数溢出的时候,-1或者4294967295则被判断为0返回为真,则使得权限升级为root。
调用setuid将我们的恶意用户设置为root,从而执行任意命令。
0x03 修复建议
- 及时升级到 sudo 1.8.28 版本。
- 检索/etc/sudoers 是否存在 ALL 关键词的复合限制逻辑
0x04 参考
- 360CERT
- CVE-2019-14287: Potential bypass of Runas user restrictions
- 雷神众测