漏洞分析
问题出在 pkexec 命令上
命令基本格式
pkexec [--user username] program
其含义,以指定用户身份 运行 文件,默认则为root
正常使用,是需要输入密码的。
在 main 函数里,如果传入的命令不是绝对路径,就会在 PATH 环境变量的目录里搜索。
在 534 行的 for 循环从下标 1 开始遍历 argv[]。但 Linux 里 argv 允许只包含一个元素,就是使用 execve 并给 argv 传入 {0},此时 argc 就是 0。
这个 for 循环结束之后,变量 n 为 1。第 610 行产生了第一个越界读,而第 639 行产生了一个越界写,把 g_find_program_in_path 返回的指针尝试写回 argv[1]。
调用 execve 之后,新进程的栈如下:
argv[0] | "program" |
argv[1] | "option" |
… | |
argv[argc] | NULL |
envp[0] | "value" |
envp[1] | "PATH=name" |
… | |
envp[envc] | NULL |
巧合的是 argv 之后正好马上接的是 envp 的第一个元素,完全可控。只要不是 / 开头的字符串就会进入越界写的分支。如果能控制 g_find_program_in_path 返回的字符串,就可以注入任意的环境变量。
这个内存安全(memory safety)问题瞬间变成了一个逻辑漏洞。
作者传入 PATH=folder,然后在 folder 目录里放置一个可执行文件 value,那么字符串 folder/value 就会写回 envp[0]。再进一步,让这个组合的文件名里包含等号 "="。传入 PATH=name=. ,创建一个 name=. 目录,并在其中放一个可执行文件 value,最终 envp[0] 就会被篡改为 name=./value,也就是注入了一个新的环境变量进去。
回到这个命令本身,pkexec 具有 suid,在执行入口程序之前不安全的环境变量都会被过滤一遍;由于这个漏洞的存在,这个过滤措施可以直接绕过。
这个漏洞将一个内存安全的 off-by-one 问题转化成了参数注入,关键出错的代码不长,颇有 ctf 的味道。
值得一提的是 GitHub Security Labs 在去年就发过一篇 Polkit 的本地提权漏洞 CVE-2021-3560,只是利用条件有一定限制。Qualys 很有可能是看了这篇分析然后开始审计代码。
POC
mrm@test:~/桌面$ cd CVE-2021-4034-main/
mrm@test:~/桌面/CVE-2021-4034-main$ make
cc -Wall --shared -fPIC -o pwnkit.so pwnkit.c
cc -Wall cve-2021-4034.c -o cve-2021-4034
echo "module UTF-8// PWNKIT// pwnkit 1" > gconv-modules
mkdir -p GCONV_PATH=.
cp /usr/bin/true GCONV_PATH=./pwnkit.so:.
mrm@test:~/桌面/CVE-2021-4034-main$ ./cve-2021-4034
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),117(bluetooth),131(scanner),1000(mrm)
#
文章转自:非尝咸鱼贩。