freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

漏洞分析与实战分享 | 深入探讨pkexec提权漏洞
2024-08-23 15:52:30

前言

在一次学习研究漏洞中碰到了一个Linux提权漏洞CVE-2021-4034,引起了我的兴趣,便开始对这个漏洞细节展开分析,在学习的同时,我也想借此与各位师傅一起分享我的学习成果,我将详细分析介绍这一漏洞的工作原理、影响范围及其复现步骤。希望通过本篇文章的分享,能够帮助读者更全面地理解这个漏洞的风险,以期提高读者在安全防护与漏洞利用方面的认知

漏洞背景

此漏洞位于pkexec这一重要的组件中,它是Polkit的一部分,通过SUID二进制文件执行特定任务。

pkexec:一个允许以其他用户身份执行特定程序的程序(SUID 二进制)

1724398665_66c83c49ed4f90ad998e4.png!small?1724398667075

在QUALYS通告中,可以明确以下几点:

  • 当argc为0时触发该漏洞
  • argv和envp在内存中是连续的
  • 越界写操作使我们能够写入envp[0](引入一个新的环境变量)

因此,首先需要理解C语言中的参数是如何工作的:

  • argv:这是一个以空字符终止的字符串数组,其元素是传递给程序的命令行参数。当通过命令行执行时,第一个(0)参数是程序本身
  • argc:一个整数,表示传递给 main() 函数的参数数组 argv 的大小。数组 argv 的长度为 argc,且 argv[argc] == NULL
  • envp:这个参数为函数提供对程序环境变量的访问,例如PATH变量

了解漏洞

为了能够完整的学习该漏洞,我将使用Ubuntu 18.04(2021年中)默认版本的pkexec,版本号为 0.105。

从相应的库中获取源代码:

git -c http.sslVerify=false clone https://gitlab.freedesktop.org/polkit/polkit.git

git checkout tags/0.105

这个漏洞问题出在 pkexec.c文件中,特别是处理参数的for循环。这个 for 循环检查传递给 pkexec 的每个参数,代码如下:

for (n = 1; n < (guint) argc; n++)

{

if (strcmp (argv[n], "--help") == 0)

{

opt_show_help = TRUE;

}

else if (strcmp (argv[n], "--version") == 0)

{

opt_show_version = TRUE;

}

else if (strcmp (argv[n], "--user") == 0 || strcmp (argv[n], "-u") == 0)

{

n++;

if (n >= (guint) argc)

{

usage (argc, argv);

goto out;

}


opt_user = g_strdup (argv[n]);

}

else if (strcmp (argv[n], "--disable-internal-agent") == 0)

{

opt_disable_internal_agent = TRUE;

}

else

{

break;

}

}


如果我们传递的argc == 0会发生什么呢?由于n从1开始,for循环会立即终止,意味着n == 1。这样n的值会传播到537行的以下代码:

path = g_strdup (argv[n]);

g_strdup的目标将是envp[0],因为sizeof(argv) == 1,这个值为NULL(argv[0] == NULL)。

考虑到这一点,通过调用execve系统调用,我们能够控制argv[0]使其不再是程序名称,而是传递一个空数组。

为了理解这个参数处理,我做了一个微小的示例,使用了两个基本程序,彼此通过execve() 调用。

ubuntu@ubuntu:~/pwn/tests/args$ cat one.c

#include <stdlib.h>

#include <stdio.h>

#include <string.h>


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

printf("Value of argc: %d\n", argc);

char **s = argv;

while(*s != NULL){

printf("Value: %s\n", *s);

s++;

}

return 0;

}

ubuntu@ubuntu:~/pwn/tests/args$ cat two.c

#include <stdlib.h>

#include <stdio.h>

#include <string.h>


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

char *args[] = {NULL};

char *envp[] = {"1","2", NULL};

execve("./one", args,  envp);

return 0;

}


编译并运行这个示例,很容易注意到,与直接从 shell 调用程序相比,使用execve时envp[0]在我们的控制之下。

1724398701_66c83c6db827d3bc31e4c.png!small?1724398702914

重新引入环境变量

如我们所知,调用一个SUID二进制程序是特殊的,因为它会清理传递给它的环境变量,以避免使用诸如 LD_PRELOAD 环境变量的攻击,但这正是此漏洞的亮点。

在越界读取的循环之后,我们达到以下代码片段:

/* Now figure out the command-line to run - argv is guaranteed to be NULL-terminated, see

*

*  http://lkml.indiana.edu/hypermail/linux/kernel/0409.2/0287.html

*

* but do check this is the case.

*

* We also try to locate the program in the path if a non-absolute path is given.

*/

# 漏洞 # 系统安全 # linux安全 # 漏洞分析 # linux提权
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录