0x01 前言

Qualys 研究团队在 polkit 的 pkexec 中发现了一个内存损坏漏洞,该 SUID 根程序默认安装在每个主要的 Linux 发行版上。这个易于利用的漏洞允许任何非特权用户通过在其默认配置中利用此漏洞来获得易受攻击主机上的完全 root 权限。

影响版本:影响自 2009 年 5 月第一个版本以来的所有 pkexec 版本

0x02 原理研究

  • 首先我们来看一下pkexec 的 main() 函数的开头处理命令行参数的部分。

    7OScmd.png

    上面这段代码中main函数中的两个参数,argc和argv,argc表示argv这个双重指针指向的范围内参数的个数。也就是说,如果argv[0]指向一个null,argc的值就为0。那么:

    在第 534 行,整数 n 永久设置为 1;

    在第 610 行,path就是从argv[1] 的值,也就是横向指针,这个指针可能指向某一个值;

    至于指向什么值呢,我们先来研究下execve() 这个函数:

  • execve()函数

    int execve(const char *filename, char *const argv[], char *const envp[]);

    这个函数filename必须是一个二进制的可执行文件,或者是一个脚本以#!格式开头的解释器参数参数。如果是后者,这个解释器必须是一个可执行的有效的路径名,比如/bin/bash,/bin/ls等等。

    argv是要调用的程序执行的参数序列,也就是我们要调用的程序需要传入的参数。

    envp 同样也是参数序列,一般来说他是一种键值对的形式 key=value. 作为我们是新程序的环境。

    所以,当我们 execve() 一个新程序时,内核将我们的参数、环境字符串和指针(argv 和 envp)复制到新程序堆栈的末尾;例如:

    7O9Bse.png

    显然,因为 argv 和 envp 指针在内存中是连续的,如果 argc 为 0,那么越界 argv[1] 实际上是 envp[0],指向我们的第一个环境变量“value”的指针。

  • 所以在610行将envp[0]指向的值赋给path后,在判定path值开头不以绝对路径为首的时候(629行)就进入并取得其绝对路径(632行),之后再将其赋值给argv[1]所指向的内存。

    所以,更准确地说:

    如果我们的 PATH 环境变量是“PATH=name”,并且如果目录“name”存在(在当前工作目录中)并且包含一个

    名为“value”的可执行文件,则写入一个指向字符串“name/value”的指针越界到 envp[0];

    或者:

    如果我们的 PATH 是“PATH=name=.”,并且目录是“name=.” 存在并包含一个名为“value”的可执行文件,然后

    将指向字符串“name=./value”的指针越界写入 envp[0]。

  • 换句话说,这种越界写入允许我们将一个“不安全”的环境变量(例如,LD_PRELOAD)重新引入 pkexec 的环境。这些“不安全”变量通常在调用 main() 函数之前从 SUID 程序的环境中删除(通过 ld.so)。这样不就可以利用啥呢,利用这个pkexec的suid进行权限提升。

0x03 漏洞复现

  • 首先我们找到了一个某个网站上的poc,点击这里

  • 将代码下载下来,放到虚拟机中某个目录下,我这边放在/tmp目录下:

    7OnFMR.png

  • 输入cc blasty-vs-pkexec.c进行编译,输出a.out可执行文件。

    7OuXNT.png

  • ./a.out执行,获得root权限。

    7OKE4O.png

结语:

利用代码可以自行分析。

如果您的操作系统没有可用的补丁(可等待官方发布补丁),您可以从 pkexec 中删除 SUID 位作为临时缓解措施;例如:

*# chmod 0755 /usr/bin/pkexec*

0x04 参考

1、C语言 execve()函数使用方法

2、PwnKit: Local Privilege Escalation Vulnerability Discovered in polkit’s pkexec (CVE-2021-4034)