polkit的pkexec中的本地权限提升漏洞 (CVE-2021-4034)

漏洞简介

漏洞编号: CVE-2021-4034

漏洞产品: PolKit (pkexec)

影响版本: 影响2009年 - 今的版本(当前0.105)

源码获取:

​ 或 https://launchpad.net/ubuntu/bionic/+package/policykit-1

docker环境

docker 环境: chenaotian/cve-2021-4034

我自己搭建的docker,提供了:

  1. 自己编译的可源码调试的pkexec
  2. 有调试符号的glibc(貌似没啥用)
  3. gdb 和gdb 插件pwngdb & pwndbg(貌似没必要)
  4. 调试环境中的exp

所有东西都在 /root/ 目录中:

  • exp 目录就是exp 和run.sh 所在目录,可以直su test 切换到test 用户然后跑
  • glibc-2.27 是glibc 源码目录,大概率用不上,用到的时候方便gdb 源码调试
  • polkit-0.105 是 policykit 源码包

启动docker:

测试exp:

漏洞原理

漏洞发生的产品是polkit 下的 pkexec 命令。pkexec 和sudo 类似都是能够让我们以其他用户身份(通常是root) 来执行命令的工具。通过dpkg 命令可以查看pkexec 所属包:

然后获取源码包(我的docker 之中也有),之后根据源码包自己编译可以调试的版本方便调试。

漏洞触发点

漏洞触发原理非常简单

根据代码中我的注释分析:

  1. 首先main函数中会根据用户输入的命令行参数进行一些变量设置,但这里for 循环的起始值是1,也就是说他默认我们至少会附带一个参数(需要pkexec 执行的命令)
  2. 如果匹配到非--开头的命令行参数,认为是需要执行的命令,则认为该参数是要用pkexec 执行的命令,而跳出循环进行下面逻辑。
  3. 调用g_find_program_in_path 函数搜索命令的绝对路径。g_find_program_in_path 函数会根据 PATH 环境变量来寻找传入参数(命令)的绝对路径。如传入cat 返回/bin/cat。
  4. 将返回的绝对路径写回该命令行参数的位置。(可以理解为从命令转换为命令对应文件的绝对路径)

还是很好理解的,但问题在于:

  • linux 二进制程序运行时会将命令行参数argv[]和环境变量environ[]放到栈底部,并且argv[] 和 environ[] 是连着的。其中argv[] 最后一项是null。

  • 如果是命令行启动的pkexec 切不带任何其他参数,那么argv[0] 为"pkexec" ,argv[1] 为\x00 没啥问题。但如果是用execve 函数启动的pkexec ,不带任何其他参数,那么argv[0] 为\x00 ,argv[1] 就到了环境变量了!当读取 argv[1] 的时候就会越界读取到 environ[0]

    命令行直接启动 pkexec 的argc 为1,argv[0] 就是pkexec 路径:

    用execve 函数启动pkexec,argc 为0:

那这会造成什么影响呢? 就是当以execve 启动时,不接任何其他参数,那么argv[] 长度为0,那么argv[1] 就是environ[0] 这样上面分析的逻辑就变成了,**获取第一个环境变量的值,并且从PATH 环境变量中寻找其绝对路径。如果寻找到则写回第一个环境变量。**那么利用方式如下:

漏洞利用

首先要明确的一点就是,pkexec 是一个特权(suid) 文件:

如何在特权文件中利用环境变量搞点事情呢?首先先了解一个小细节:

一个小细节

linux 的动态连接器ld-linux-x86-64.so.2 会在特权程序执行的时候清除敏感环境变量:

危险环境变量列表 UNSECURE_ENVVARS 定义如下:

当检测到程序是特权文件(suid) 的时候,会清空上面的这些环境变量,可以看到,绝大部分是LD_ 系列的环境变量,他们都有能指定动态库加载路径的能力。这是防止低权限用户通过这些环境变量让suid 程序加载不可信的so,造成的恶意代码执行进而提权的情况。

而在该漏洞场景下,我们拥有一次任意环境变量写得机会,我们的利用思路就是尝试从上面那些本来没法传入suid 程序中的环境变量中找点东西。

利用原理

由于已经公布了poc,这里直接看答案就很简单了,这里参考了arthepsy的poc。内容很简单,但通过该poc 得知利用关键环境变量是**GCONV_PATH**。确实是上面危险环境变量列表中的一员,甚至是第一个!

关于GCONV_PATH 与 iconv_open() 函数:

iconv_open() 函数申请一个转换描述符,转换字符序列从编码 fromcode 到编码 tcode 转换描述符包含转换状态。iconv_open() 函数首先会找到系统提供的 gconv-modules 文件,这个文件中包含了各个字符集的相关信息存储的路径,每个字符集的相关信息存储在一个.so文件中。然后再根据 gconv-modules 文件的指示去链接参数对应的.so文件执行具体操作。如果存在环境变量 GCONV_PATH ,则 iconv_open() 函数依照GCONV_PATH 找到gconv-modules 文件,后续操作不变。

也就是说,这里GCONV_PATH 环境变量也有相当于 LD_LIBRARY_PATH 的功能。他可以指定 iconv_open() 函数搜索so库的文件。我们如果可以伪造GCONV_PATH 然后进一步伪造 gconv-modules 最后在伪造一个 so 就可以完成任意so加载以及任意代码执行。

大体思路如下:

  1. 创建一个 名为 GCONV_PATH=. 目录
  2. 在 GCONV_PATH=. 目录中创建一个 名为 pwnkitdir 的文件,权限带x
  3. 创建一个 名为 pwnkitdir 的目录
  4. 在 pwnkit 目录中创建 gconv-modules 文件,依照格式写入如下内容:
  5. 在 pwnkit 目录中放入恶意so pwnkit.so 里面是获取shell的代码。
  6. 设置相关环境变量
    1. 第一个环境变量 pwnkitdir
    2. 第二个环境变量 PATH=GCONV_PATH=. 这样 g_find_program_in_path 函数组合出的路径就是GCONV_PATH=./pwnkitdir 正好是环境变量的格式,而且 ./pwnkitdir 目录存在,GCONV_PATH=./pwnkitdir 文件也存在。
    3. CHARSET=PWNKIT 环境变量,在走到 iconv_open 前的路径中会用到,用来从 gconv-modules 中搜索so
    4. SHELL=xxx ,,在走到 iconv_open 前的路径中会用到
  7. 通过 execve 启动 pkexec 参数为空,环境变量为上面设置的值

然后就成功,具体exp如下:

exp

利用成功:

参考

漏洞https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034

arthepsy‘s pochttps://github.com/arthepsy/CVE-2021-4034

参考链接


发布者

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注