Sudo堆溢出漏洞(CVE-2021-3156)复现
2023-06-28 16:09
5350
背景介绍
2021 年 1 月 26 日,Qualys Research Labs在 sudo 发现了一个缺陷。sudo 解析命令行参数的方式时,错误的判断了截断符,从而导致攻击者可以恶意构造载荷,使得sudo发生堆溢出,该漏洞在配合环境变量等分配堆以及释放堆的原语下,可以致使本地提权。
环境搭建
环境版本
• ubuntu 20.04
• sudo-1.8.31p2
采用下述命令进行编译安装
cd ./sudo-SUDO_1_8_31p2
mkdir build
./configure --prefix=/home/pwn/sudo CFLAGS=”-O0 -g"
make && make install漏洞验证
#poc
./sudoedit -s '\' 11111111111111111111111111111111111111111111111111111111111111111111执行上述POC执行sudoedit会出现malloc():invalid size的字样,这是典型的堆溢出后导致的异常。

漏洞分析
源码分析
set_cmnd函数
File: plugins\sudoers\sudoers.c
800: static int
801: set_cmnd(void)
802: {
...
819: if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { //需要满足标志位的设置才能进入转义的流程
...
845:
846: /* set user_args */
847: if (NewArgc > 1) {
848: char *to, *from, **av;
849: size_t size, n;
850:
851: /* Alloc and build up user_args. */
852: for (size = 0, av = NewArgv + 1; *av; av++) //遍历每一个参数
853: size += strlen(*av) + 1; //计算每一个参数的长度
854: if (size == 0 || (user_args = malloc(size)) == NULL) { //通过malloc动态分配一段内存,用于存放参数内容
855: sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
856: debug_return_int(-1);
857: }
858: if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { //需要满足标志位的设置才能进入转义的流程
859: /*
860: * When running a command via a shell, the sudo front-end
861: * escapes potential meta chars. We unescape non-spaces
862: * for sudoers matching and logging purposes.
863: */
864: for (to = user_args, av = NewArgv + 1; (from = *av); av++) { //遍历每个环境变量,并将内容拷贝到内存中
865: while (*from) {
/*
漏洞点,当扫描参数内容时,遇到\需要进行转义处理,例如'\t'、'\n'等,因此sudo只判断\后是否跟随着空格字符,即用isspace函数进行判 断。
isspace包括的字符如下:
' ' (0x20) space (SPC) 空格符
'\t' (0x09) horizontal tab (TAB) 水平制表符
'\n' (0x0a) newline (LF) 换行符
'\v' (0x0b) vertical tab (VT) 垂直制表符
'\f' (0x0c) feed (FF) 换页符
'\r' (0x0d) carriage return (CR) 回车符
以上不包括'\0'。
而参数之间是使用'\0'作为分隔符的,因此当'\\'后跟随的'\0'会使得from++从而导致将后一个参数也被拷贝进来,最后致使堆块溢出。
*/
866: if (from[0] == '\\' && !isspace((unsigned char)from[1]))
867: from++;
868: *to++ = *from++;
869: }
870: *to++ = ' ';
871: }
872: *--to = '\0';
使用POC的例子对漏洞进行说明

漏洞原理图
因此漏洞点在于在进入set_cmnd函数时需要对转义字符进行转义,但是函数却没有判断转义字符作为参数末尾的情况,即\ + \x00
parse_args函数
parse_args函数用于反转义,即参数中若存在转义字符,会在每个转义字符之前增加一个\
File: src\parse_args.c
592: if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { //需要满足标志位的设置才会进入反转义流程
593: char **av, *cmnd = NULL;
594: int ac = 1;
595:
596: if (argc != 0) {
597: /* shell -c "command" */
598: char *src, *dst;
599: size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
600: strlen(argv[argc - 1]) + 1;
601:
602: cmnd = dst = reallocarray(NULL, cmnd_size, 2);
603: if (cmnd == NULL)
604: sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
605: if (!gc_add(GC_PTR, cmnd))
606: exit(1);
607:
608: for (av = argv; *av != NULL; av++) {
609: for (src = *av; *src != '\0'; src++) {
610: /* quote potential meta characters */
611: if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '