CVE-2021-4034 Pkexec LPE原理精析

Posted 鸿渐之翼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CVE-2021-4034 Pkexec LPE原理精析相关的知识,希望对你有一定的参考价值。


敬告:
《中华人民共和国刑法》第三百八十六条【破坏计算机系统罪;网络服务渎职罪】违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机系统不能正常运行,后果严重的,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。
违反国家规定,对计算机系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,依照前款规定处罚。
故意制作、传播计算机病毒等破坏性程序,影响计算机系统正常运行,后严重的,依照第一款规定处罚。
单位犯前三款罪的,对单位判处罚金,对其直接负责的主管人员和其他直接负责人员,依照第一款的规定处罚。

声明:本文仅供开发者与信息安全从业者学习参考,请遵守行业道德底线。

前言:
CVE-2021-4034这个漏洞的复现已经烂全网了,流量已经被凑够了。我们还是本着学习的心态来看漏洞。我参考看雪论坛的文章,重新对该漏洞分析,用坛主的一句话来讲还是值得我们深思吧。欢迎各位专家学者指正与提意。
本文参考 看雪论坛https://bbs.pediy.com/thread-271423-1.htm#1710498
摘要:
国外Qualys安全团队在CVE平台披露了Linux系统Polkit中的pkexec组件存在的本地权限提升漏洞(CVE-2021-4034)。Polkit默认安装在各个主要的 Linux 发行版本上(诸如Ubuntu、Debian、Fedora等知名Linux发型版本),pkexec程序对传入参数未过滤,攻击者可以将环境变量bash作为命令执行,从而诱导 pkexec 执行任意代码,利用成功可导致非特权用户获得管理员root权限。[1]
关键字 安全漏洞;网络安全;信息安全;漏洞挖掘;操作系统
1.引言:
国外Qualys安全团队披露了Linux系统Polkit中的pkexec组件存在的本地权限提升漏洞(CVE-2021-4034)。Polkit(原名PolicyKit)是一个用于在类Unix操作系统中控制系统范围权限的组件。它为非特权进程与特权进程通信提供了一种有组织的方式。还可以使用polkit以提升的权限执行命令,先使用命令pkexec,然后是要执行的命令(前提是具有root权限)。Polkit默认安装在各个主要的 Linux 发行版本上(诸如Ubuntu、Debian、Fedora等知名Linux发型版本),pkexec程序对传入参数未过滤,攻击者可以将环境变量bash作为命令执行,从而诱导 pkexec 执行任意代码,利用成功可导致非特权用户获得管理员root权限。
2.漏洞复现
我们首先利用Github开源POC完成了Linux操作系统的本地提权限攻击(LPE)。
漏洞复现环境是Kali-Linux-2021.2-vmware-amd64,pkexec版本为0.105,通过普通⽤户zhicrsec进⾏复现,获取到root权限,如图2所示。
下载EXP并且编译源代码:

3.漏洞分析

3.1 漏洞分析基础环境

实验使用的操作系统是Kali-Linux-2021.2-vmware-amd64 ,polkit源码版本:polkit-0.105,设置GDB汇编风格为Intel,set disassembly-flavor intel,设置gdb的SUID位,避免调试pkexec时执⾏到geteuid函数失败,报错“pkexec must be setuid root”,使用命令sudo chmod 4755 /usr/bin/gdb为GDB提高执行权限.

3.2 漏洞原理

3.2.1 argv中存在的漏洞

我们通过C程序中argc传参程序进行分析,通过GCC命令编译程序。
argc表示参数的个数,argv存放着具体的参数,argv[0]指向程序本身,
argv[1]指向第⼀个参数,argv[2]指向第⼆个参数,…,argv[argc]存放0 表示结束。
如图3,图4所示。


当我们在命令⾏中执⾏程序时,必须向argc传参,argc的取值⾄少为1,如果不传参数,argv[0]也要指向程序路径本身。而execve函数原型定义如下:
#include<unistd.h>
int execve(const char *pathname, char *const argv[],char *const envp[]);
在特殊情况下,如使⽤execve来调⽤程序,并给 argv传值 NULL,则argc为0。
图5图6为测试execve函数执行流程。


为了验证execve传参问题,即要验证argv与envp在内存分布是连续的。由此更改execve.c与test.c程序,并且通过gcc -g -O0 -o test test.c与gcc -g -O0 -o execve execve.c重新编译程序,如图7与图8图9图10所示。




如图10所示,程序设定argc指为4,加上NULL,argc值为5,这里程序直接输出了10个值,显然是存在缓冲区溢出漏洞。通过此实验证明到argv与envp在内存上布局是连续的,envp内存空间紧跟argv内存空间后面。

3.2.2复现pwnkit.c与argv漏洞的利用

程序main.c

编译程序main.c

编译程序时提示, warning: implicit declaration of function ‘g_printerr’,因为g_printer函数是隐式函数,所以需要包含一个.h头文件,func.h内容为void g_printer(void);


如图14所示,此时成功运行新生成的test程序,运行run.sh,此时设定两个系统环境变量。

运行run.sh程序,成功在本地获取一个新的普通用户权限。

在本实验中,当环境变量设置了CHARSET,且不为UTF-8时, g_printerr会进⾏编码转换,⽽转换的⽅法就是根据GCONV_PATH⾥的配置⽂gconv-modules的说明,调⽤ pwnkit.so,从⽽得到shell。此处的pwnkit.sh文件是由cve-2021-4034.sh文件联合cve-2021-4034.c与pwnkit.c编译得到。
pwnkit.c

CVE-2021-4034.c

CVE-2021-4034.sh

3.2.3通过IDA对pwnkit.so层逆向分析


IDA打开pwnkit.so文件后,我们观察到以上两个程序使用了execve,_setgid,_exit,_setuid等几个关键函数。got段中定义的execve原型:

aPathUsrLocalSb db 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'

执行shellcode汇编代码:

var_28          = qword ptr -28h
envp            = qword ptr -20h
var_18          = qword ptr -18h
path            = qword ptr -10h
var_8           = qword ptr -8
; __unwind 
push ebp;开辟新的栈桢
mov rbp,rsp
sub rsp,30h
mov [rbp+var_20],rdi;
lea  rax,aBinsh ;准备写入’/bin/sh’
mov     [rbp+path], rax
mov     [rbp+var_8], 0
lea     rax, aPathUsrLocalSb ; "PATH=/usr/local/sbin:/usr/local
mov     [rbp+envp], rax
mov     [rbp+var_18], 0
mov     edi, 0          ; uid uid=0 设置uid权限
call    _setuid
mov     edi, 0          ; gid gid=0 设置gid权限
call    _setgid
mov     rax, [rbp+path]
lea     rdx, [rbp+envp] ; envp
lea     rcx, [rbp+path]
mov     rsi, rcx        ; argv
mov     rdi, rax        ; path
call    _execve
mov     edi, 0          ; status
call    _exit
;  // starts at 113C

3.2.4探索SUID权限

当⼀个程序被设置了SUID权限,则其他⽤户执⾏该程序时,可以临时切换到程序所有者的权限去执⾏⼀些功能。因为涉及到权限变更,所以在执⾏操作系统⾃带的此类程序时需要被授权。

SUID授权,授权之后可以使用root权限。

已完成授权
编写程序main.c与func.h,并且使用gcc -g -O0 test main.c编译,sudo chmod 4755提升test执行权限为root级别。继续编辑新的bash环境变量,如图23,24所示。


编辑bash变量
分别以root与zhicrsec用户执行bash run.sh|grep AAAAA,用来判断环境变量导入情况。

root用户执行后结果

普通用户执行结果
通过实验可以知道,当以普通⽤户身份去执⾏所有者为root且具有SUID权限的程序时,GCONV_PATH、LD_PRELOAD 等不安全的环境变量并不会被引⼊。

3.2.3利用原理进行漏洞分析:

下载源代码poltik-0.105,在Linux上安装编译环境后编译。

以上为Linux系统需要安装的packet
通过./configure&&sudo make编译得到


./configure与make编译文件

此时我们需要设置SUID权限,以root用户赋权。


通过以上的探索,我们不难发现”./pkexec bash”是正常用法,pkexec会进行授权,接着使用root权限运行bash命令。但我们可以跳过授权认证,直接在本地运行root命令。此时我们修改cve-2021-4034.c源代码,进行GDB调试阶段。从源码层面分析授权认证是如何被绕过的?

3.2.4:继续探索pkexec的授权认证是如何被绕过的

我们用python编写GDB插件,并在GDB运行时调用。

step_trace.py

import sys
import gdb
import os
import re
 
def in_frames(needle):
    """ Check if the passed frame is still on the current stack """
    hay = gdb.newest_frame()
    while hay:
        if hay == needle:
            return True
        hay = hay.older()
    return False
 
# Use this to reduce any kind of unwanted noise
def filter_step(output):
    output = re.sub(r'^.*No such file or directory\\.\\n', r'', output, flags=re.M)
    output = re.sub(r'^\\d+\\s+in\\s+.*\\n', r'', output, flags=re.M)
    return output
 
def step_trace(filename=None, step="step"):
    counter = 0
    if filename:
        output = ""
    frame = gdb.newest_frame()
    print("Stepping until end of  @ :".format(frame.name(), frame.function().symtab, frame.function().line))
    while in_frames(frame):
        counter += 1
        if filename:
            output += filter_step(gdb.execute(step, to_string=True))
        else:
            gdb.execute(step)
 
    if filename:
        with open(filename, "w") as file:
            file.write(output)
    print("Done stepping through  lines.".format(counter))

使用命令source set_trace.py,编写bplist GDB辅助调试程序。


gdb./pkexec -x bplist调试代码,GDB代码停在了705行,需要进行身份验证。
pkexec.c 705行验证身份程序如下:

result=polkit_authority_check_authorization_sync(authority,subject,action_id,details,POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,NULL,&error);


通过GDB调试发现,我们向系统更改path,将path设置成”GCONV_PATH=./pwnkit.so:.”再将path传如argv[1]中,接下来构造错误调用g_printerr函数,触发漏洞利用,最后执行

execve("/bin/sh", args, environ)

完成未经授权代码执行。

process 11852 is executing new program: /usr/bin/dash
Error in re-setting breakpoint 1: Function "main" not defined.
Error in re-setting breakpoint 2: No source file named /home/kali/software/release/polkit-
0.105/src/programs/pkexec.c.
#whoami
#root

3.3 漏洞复现与分析总结

本次实验通过从argc危险传参操作,再到系统变量分析与源代码调试,我们发现了当使⽤普通⽤户权限执⾏pkexec时,GCONV_PATH、LD_PRELOAD等不安全的环境变量会被删除,攻击者可以通过参数数组的越界读写漏洞,重新引⼊不安全的环境变量,进⽽构造利⽤链获取root权限。CVE-2021-4034漏洞是程序设计者未考虑到的参数问题,由于pkexec大部分代码开源,并且由C语言编写,
可以让攻击者通过源码调试的方式找到漏洞。LPE漏洞风险较低,但也需要我嫩引起重视,防止RCE的出现。

参考文献:

[1]http://app.myzaker.com/news/article.php?pk=6200974e8e9f0975af5d9eec
[2]https://github.com/berdav/CVE-2021-4034.git
[3]https://blog.csdn.net/ani_di/article/details/7893802?utm_source=blogxgwz0
[4]https://blog.csdn.net/water_cow/article/details/7174881
[5]https://blog.csdn.net/passerby_unnamed/article/details/51073296
[6]https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
[7]https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalationvulnerability-discovered-in-polkits-pkexec-cve-2021-4034
[8]https://stackoverflow.com/questions/39602306/tracing-program-function-execution-on-source-line-level
[9]https://www.xiebruce.top/1387.html
[10]https://bbs.pediy.com/thread-271345.htm

以上是关于CVE-2021-4034 Pkexec LPE原理精析的主要内容,如果未能解决你的问题,请参考以下文章

CVE-2021-4034 pkexec存在本地权限提升漏洞

CVE-2021-4034 pkexec存在本地权限提升漏洞

CVE-2021-4034 pkexec存在本地权限提升漏洞

Linux 系统安全 - 近期发现的 polkit pkexec 本地提权漏洞(CVE-2021-4034)修复方案

关于 Linux Polkit 权限提升漏洞(CVE-2021-4034)的修复方法

CVE-2021-4034_Polkit提权漏洞复现与修复