1. 项目概述最近在巡检一批老旧的CentOS 8.5服务器时一个熟悉又刺眼的名字再次跳了出来CVE-2021-4034也就是那个大名鼎鼎的“PwnKit”漏洞。这个漏洞虽然已经过去几年但因其影响深远、利用简单至今仍是安全渗透测试和红蓝对抗中的“常客”。对于运维和安全工程师来说它就像一颗埋藏在系统深处的“定时炸弹”尤其是对于那些已经停止官方支持、无法通过常规渠道更新的系统比如我们手头这批CentOS 8.5。这个漏洞的核心在于polkit框架中的pkexec工具一个设计用于以高权限执行命令的程序。攻击者可以构造一个特殊的调用让pkexec错误地处理参数从而将攻击者控制的环境变量当作命令来执行最终实现从普通用户到root权限的“惊险一跃”。整个过程无需用户交互也无需任何特殊权限危害极大。这篇文章我就结合最近一次为CentOS 8.5集群修复此漏洞的实战经历从头到尾拆解一遍。不仅会讲清楚漏洞的原理更重要的是会详细分享在官方仓库已关闭的“绝境”下如何手动寻找、验证并安装安全补丁包的全过程。同时我也会聊聊在修复过程中遇到的那些“坑”以及一些确保修复彻底、不留后患的检查技巧。无论你是正在管理类似遗留系统的运维还是想深入了解Linux权限机制和安全攻防的安全爱好者相信这篇来自一线的复盘都能给你带来直接的帮助。2. 漏洞原理深度剖析与影响评估2.1 Polkit与pkexec的角色定位要理解这个漏洞首先得知道polkit和pkexec是干什么的。polkit原名PolicyKit是Linux系统中一个非常重要的授权管理框架。它的核心作用是解决一个经典问题如何让普通用户进程安全地请求并执行需要高权限通常是root权限的操作。比如普通用户想用networkmanager修改网络配置或者挂载一个USB磁盘这些操作都涉及系统层面的改动不能直接放行。polkit提供了一套基于策略的精细控制。系统管理员可以通过编写策略文件通常放在/usr/share/polkit-1/actions/和/usr/share/polkit-1/rules.d/目录下明确规定“哪个用户”、“在什么条件下”、“可以执行哪个需要特权的操作”。当用户程序比如图形界面下的一个设置按钮发起特权请求时它会通过D-Bus消息总线与polkit守护进程通信polkit根据策略判断是否授权可能还会弹出一个密码对话框让用户认证。而pkexec则是polkit提供的一个命令行工具。你可以把它看作sudo的一个“近亲”但机制不同。sudo依赖于/etc/sudoers文件的静态配置而pkexec在运行时动态地咨询polkit守护进程根据当前策略决定是否允许执行。它的基本用法是pkexec [command]如果策略允许command就会以root身份运行。2.2 CVE-2021-4034的核心漏洞机制漏洞的根源在于pkexec的代码在处理命令行参数时存在一个经典的“边界错误”Off-by-one Error。在C语言中main函数接收两个参数来获取命令行参数int argc参数个数和char **argv参数向量数组。按照C语言惯例argv[0]是程序名本身argv[1]是第一个真正的参数以此类推并且argv[argc]是一个空指针NULL用来标记数组的结束。pkexec的漏洞代码大致逻辑是这样的当它被调用时比如pkexec bash那么argc2argv[0]”pkexec”argv[1]”bash”argv[2]NULL。问题出在如果攻击者以某种方式调用pkexec并且让argc为0即没有任何命令行参数。那么按照逻辑argv数组应该只包含argv[0]程序名和argv[1]NULL。然而在某些特定的调用方式下例如通过execve()系统调用并精心控制参数和环境变量攻击者可以制造出一种局面argc为0但argv这个内存数组的边界之外紧接着的内存区域恰好是进程的环境变量envp数组。由于argc为0pkexec在遍历参数时错误地越界读取了argv[1]而argv[1]实际上指向了第一个环境变量字符串例如PATH/usr/bin。pkexec后续的代码逻辑没有充分校验这种异常情况它误将这个环境变量argv[1]当作是用户想要执行的命令或参数来处理。更致命的是在构造最终要执行的命令路径时它会使用一个名为PATH的环境变量来查找可执行文件。如果攻击者能够控制传入的环境变量他们就可以精心设置PATH等环境变量诱导pkexec加载并执行一个由攻击者控制的、位于特定路径下的恶意共享库.so文件或可执行脚本从而以root权限执行任意代码。简单来说这个漏洞绕过了pkexec的所有策略检查因为它发生在策略检查逻辑之前。攻击者无需知道任何密码也无需在polkit策略中有任何特殊配置只要能在系统上以一个普通用户身份运行程序就有可能直接获得完整的root shell。2.3 漏洞影响范围与严重性这个漏洞的可怕之处在于其广泛性和隐蔽性。影响范围极广自2009年pkexec引入相关代码以来几乎所有主流的Linux发行版包括RHEL、CentOS、Ubuntu、Debian、Fedora、SUSE等的默认安装都受影响。只要系统安装了polkit包这几乎是图形界面和许多服务器服务的标配就存在风险。利用门槛极低漏洞利用程序早已公开在互联网上随手可得。攻击者只需将一段简单的C代码编译后在目标机器上以普通用户身份运行瞬间就能获得root权限。整个过程安静快速不会产生明显的日志告警在默认配置下。修复紧迫性高对于互联网上暴露的服务器任何一处低权限的入口例如一个Web应用的文件上传漏洞、一个配置不当的数据库连接都可能被攻击者作为跳板利用此漏洞迅速“拿下”整台服务器。对于我们的CentOS 8.5环境官方支持已于2021年底终止这意味着通过yum或dnf的常规更新通道已经关闭无法自动获取补丁。这使得修复工作从简单的命令升级变成了需要手动介入的“外科手术”这也是本文重点要解决的问题。3. 修复方案选择与准备工作3.1 修复路径评估面对CVE-2021-4034通常有几种修复思路官方升级首选通过系统包管理器如dnf update polkit直接升级到已修复漏洞的版本。这是最安全、最规范的方式。手动安装补丁包当官方源不可用如CentOS 8.5或内网环境无法连接外网时需要手动下载对应版本的安全补丁RPM包进行离线安装。临时缓解措施如果短期内无法升级可以移除pkexec的SUID权限作为临时应对。命令是chmod 0755 /usr/bin/pkexec。但这会破坏所有依赖pkexec进行特权操作的功能如图形界面的软件更新器、网络管理器等属于“伤敌一千自损八百”的临时方案仅用于应急。源码编译下载打好补丁的polkit源码包自行编译安装。这种方式最灵活但步骤繁琐依赖复杂且容易引入不一致性不适合生产环境批量部署。对于我们的CentOS 8.5集群路径1被堵死路径3影响业务路径4过于复杂。因此手动安装补丁包路径2成为了唯一可行的选择。这要求我们精确找到适用于CentOS 8.5的、已修复漏洞的polkitRPM包。3.2 环境探查与信息收集在动手之前必须对当前系统状态有清晰的了解。盲目安装错误的包版本可能导致依赖冲突甚至系统不稳定。首先检查当前系统版本和已安装的polkit包信息cat /etc/redhat-release rpm -qa | grep polkit rpm -qi polkit关键信息包括polkit和polkit-libs的具体版本号例如polkit-0.115-11.el8以及系统架构x86_64或aarch64。其次验证漏洞是否存在。虽然我们心里清楚它存在但有时需要给报告一个证据。可以使用一个无害的检测脚本非利用脚本来检查或者直接尝试寻找公开的漏洞检测命令。一个简单的思路是检查pkexec的--version输出但更可靠的是检查包版本是否低于安全版本。对于CentOS 8.5安全版本是polkit-0.115-13.el8_5.3及更高。注意切勿在正式生产环境直接运行从不明来源下载的漏洞利用EXP程序进行“测试”这本身就是极高的安全风险。检测应以信息收集和版本比对为主。3.3 寻找可靠的安全补丁包来源既然CentOS官方仓库停了我们就需要寻找替代的、可信的软件源。这里有几个选择AlmaLinux / Rocky Linux仓库作为RHEL/CentOS的继承者它们的仓库通常兼容性很好并且会持续提供安全更新。这是我们的首选来源。CentOS Vault这是CentOS官方存档旧版本软件包的地方。但对于像CVE-2021-4034这种在支持终止后才爆出的漏洞Vault里很可能没有对应的安全更新包。EPEL仓库Extra Packages for Enterprise Linux但它主要提供额外的软件不一定包含核心系统包的安全更新。从其他已更新的同版本系统中提取如果你有另一台已经通过其他方式更新了的CentOS 8.5机器可以用rpm -qa polkit polkit-libs --qf “%{name}-%{version}-%{release}.%{arch}.rpm\n”列出包名然后用dnf download或yumdownloader工具下载对应的RPM包。经过评估我们决定从AlmaLinux 8的BaseOS仓库获取补丁包。因为AlmaLinux与RHEL 8二进制兼容且其8版本仍在维护期内必然包含了针对此漏洞的修复。4. 手动下载与安装补丁包实操详解4.1 定位并下载正确的RPM包AlmaLinux的软件包仓库结构清晰。我们需要找到适用于x86_64架构的、版本号大于等于安全版本的polkit和polkit-libs包。安全版本信息参考CentOS 8.5 安全版本:polkit-0.115-13.el8_5.3实际上AlmaLinux后续可能提供了更新的版本例如polkit-0.115-15.el8_10.2这个版本也包含了该漏洞的修复并且兼容性更好。访问AlmaLinux 8 BaseOS仓库https://repo.almalinux.org/almalinux/8/BaseOS/x86_64/os/Packages/这是一个标准的HTTP目录列表页面。我们需要在页面中或通过wget递归下载索引后查找找到以下两个包polkit-0.115-15.el8_10.2.x86_64.rpmpolkit-libs-0.115-15.el8_10.2.x86_64.rpm实操心得在浏览器中按CtrlF搜索“polkit”可以快速定位。务必确认架构是x86_64并且版本号中的el8表示适用于Enterprise Linux 8系列。el8_10.2表示这是AlmaLinux 8.10的更新但它提供的二进制包在CentOS 8.5上通常是可安装的因为用户空间的主要库版本在RHEL 8的大版本生命周期内保持兼容。下载包到服务器上。可以使用wget直接下载# 在服务器上执行假设当前目录为 /tmp wget https://repo.almalinux.org/almalinux/8/BaseOS/x86_64/os/Packages/polkit-0.115-15.el8_10.2.x86_64.rpm wget https://repo.almalinux.org/almalinux/8/BaseOS/x86_64/os/Packages/polkit-libs-0.115-15.el8_10.2.x86_64.rpm如果服务器无法直接访问外网就需要先在能上网的机器上下载好再通过scp、sftp或者内部文件共享服务上传到目标服务器。4.2 执行手动安装与依赖处理拿到RPM包后不要急着直接安装。先检查一下包的依赖关系避免安装失败。rpm -qpR polkit-0.115-15.el8_10.2.x86_64.rpm rpm -qpR polkit-libs-0.115-15.el8_10.2.x86_64.rpm重点关注是否有类似glib2 2.56.0这样的关键库依赖。在同一个大版本EL8内基础库的版本通常满足要求。如果遇到缺失的依赖也需要从AlmaLinux仓库下载对应的包。安装时使用rpm -Uvh命令。-U表示升级如果旧版本存在则升级否则安装-v显示详细信息-h显示进度条。强烈建议将polkit和polkit-libs两个包同时安装因为它们之间版本必须严格匹配。sudo rpm -Uvh polkit-0.115-15.el8_10.2.x86_64.rpm polkit-libs-0.115-15.el8_10.2.x86_64.rpm如果系统提示缺少依赖例如libsomething.so.0你需要根据缺少的库名去仓库里找到对应的rpm包通常是something-libs包一并下载并安装。有时dnf或yum本地安装可以自动解决依赖但在离线环境下rpm命令不会自动处理需要手动补全。踩坑记录在一次安装中系统提示依赖libglib-2.0.so.0()(64bit)版本不满足。检查发现是glib2库版本过低。CentOS 8.5自带的glib2版本是2.56.4而AlmaLinux 8.10的polkit可能要求2.56.0或更高理论上是满足的。但错误信息可能是由于RPM数据库中的版本字符串比较方式导致的。此时一个比较稳妥的方法是从AlmaLinux仓库也下载对应版本的glib2和glib2-libs包进行升级。但升级基础库风险较高需要谨慎评估。在我们的案例中经过核实实际版本是满足的这个警告可以忽略通过加上--nodeps参数强制安装不推荐除非你非常确定。更好的做法是寻找一个与当前系统glib2版本匹配的polkit补丁包或者接受升级glib2。4.3 安装后验证与重启服务安装完成后立即验证安装的版本rpm -q polkit polkit-libs输出应显示为新安装的版本例如polkit-0.115-15.el8_10.2.x86_64。仅仅安装包还不够因为pkexec是一个SUID二进制文件它的权限和内容已经更新。我们需要确保相关的服务也使用新的库文件。重启polkit服务是最直接的方式sudo systemctl restart polkit为了确保所有可能缓存了旧pkexec进程的会话完全刷新最彻底的方法是重启服务器。尤其是在关键生产环境安排一次合规的重启窗口是值得的。如果无法立即重启至少需要确保所有用户会话特别是那些可能已经调用了pkexec的会话都已注销。验证漏洞是否修复可以再次检查版本并尝试搜索是否有公开的、针对该版本pkexec的漏洞检测方法注意不是利用方法。一个间接但有效的方法是检查pkexec的--help输出中是否包含相关的安全补丁信息或者查看RPM包的更新日志rpm -q polkit --changelog | head -20在更新日志中你应该能看到关于CVE-2021-4034的修复记录。5. 修复过程中的常见问题与排查实录5.1 依赖冲突与版本兼容性问题问题描述执行rpm -Uvh时报错“Error: Failed dependencies: … conflicts with file from package …”。排查思路这是最典型的问题。冲突可能来自两个方面文件冲突新包要安装的文件已经被其他包占用了。这在不兼容的第三方仓库混用时常见。对于polkit这样的核心系统包很少见。依赖不满足新包要求的某个库或工具的版本高于当前系统已安装的版本。解决方案对于依赖不满足首先用yum provides或dnf provides命令查找哪个包提供了所需的文件或库。例如dnf provides “libglib-2.0.so.0()(64bit)”。在离线环境这需要你有一个本地的仓库镜像或者手动搜索。如果冲突的包是系统基础组件且版本差距不大可以考虑从同一来源AlmaLinux仓库下载并升级这些依赖包。务必注意升级顺序先升级底层依赖如glib2再升级上层应用如polkit。如果无法升级依赖可能需要寻找一个与当前系统环境版本更匹配的polkit补丁包。可以尝试在https://vault.centos.org/或https://repo.almalinux.org/vault/中寻找CentOS 8.5生命周期内发布的最后一个更新版本如果有的话。最后手段在充分评估风险并做好回滚准备后可以使用rpm -Uvh --nodeps --force命令强制安装。这可能导致系统不稳定或某些功能异常仅适用于紧急且可控的环境并需明确记录。5.2 安装成功但服务无法启动问题描述polkit服务重启失败systemctl status polkit显示错误可能与新的库文件或配置有关。排查思路检查日志第一时间查看journalctl -u polkit --since “5 minutes ago”获取详细的错误信息。验证文件完整性使用rpm -V polkit验证安装的文件是否完整是否有配置文件被意外修改。如果发现配置文件标记为c被修改RPM会提示。新的RPM包可能会用.rpmnew文件提供新的默认配置需要管理员手动合并。检查依赖库使用ldd /usr/bin/pkexec检查pkexec二进制文件依赖的动态库是否能正确链接。确保没有出现“not found”的库。检查SELinux上下文在启用SELinux的系统上新安装的二进制文件可能需要恢复正确的安全上下文。使用restorecon -v /usr/bin/pkexec进行修复。解决方案根据journalctl的日志错误进行针对性修复。常见的是配置语法错误或权限问题。如果是因为配置文件冲突对比/etc/polkit-1/rules.d/和/etc/polkit-1/localauthority/目录下的.rpmnew或.rpmsave文件与现有配置文件合并。运行sudo systemctl daemon-reload重新加载systemd配置再尝试启动。5.3 验证漏洞是否真正修复问题描述如何确信漏洞已经补上仅仅版本号对了就行吗排查思路与解决方案版本比对法这是最基本的方法。确认rpm -q polkit输出的版本号大于等于已知的安全版本对于CentOS 8.5是0.115-13.el8_5.3。我们安装的0.115-15.el8_10.2显然更高。补丁检查法查看软件包更新日志确认包含了CVE-2021-4034的修复。rpm -q --changelog polkit | grep -i “CVE-2021-4034”如果输出中包含相关记录则是强有力的证据。行为检测法谨慎操作可以编写一个简单的测试程序模拟漏洞触发的前置条件如调用execve并设置argc0然后观察pkexec的行为。一个安全的版本应该会输出错误信息并拒绝执行而不是崩溃或执行任意代码。网上有一些开源的非破坏性检测脚本可以在隔离的测试环境如虚拟机中运行验证。漏洞扫描工具使用专业的漏洞扫描工具如OpenVAS, Nessus, Qualys等对修复后的系统进行扫描确认CVE-2021-4034的风险状态已变为“已修复”或“低风险”。5.4 批量部署的自动化考虑如果面临成百上千台CentOS 8.5服务器需要修复手动操作是不现实的。解决方案制作本地仓库在一台内部服务器上下载所有必需的RPM包polkit,polkit-libs及其依赖使用createrepo命令创建一个本地YUM/DNF仓库。然后在所有目标机器上修改repo文件指向这个本地仓库最后执行sudo dnf update polkit即可。这是最规范、最接近原生体验的方式。使用配置管理工具如果你使用了Ansible、SaltStack、Puppet等工具可以编写一个Playbook或模块。任务包括将补丁RPM包分发到目标节点的临时目录。使用rpm或yum模块进行安装。重启polkit服务。验证安装版本。编写分发脚本一个简单的Shell脚本结合ssh和scp也可以实现小规模批量部署。脚本逻辑遍历主机列表上传RPM包远程执行安装命令收集结果。务必做好错误处理和回滚预案。无论采用哪种方式务必先在少数几台测试机上充分验证确认安装过程平滑安装后系统核心功能特别是需要特权授权的操作如网络管理、用户管理、软件包安装等均正常再进行全网推广。6. 长期维护与安全加固建议修复一个高危漏洞只是安全运维的一环。对于像CentOS 8.5这样已经EOL生命周期终止的系统真正的挑战在于如何可持续地保障其安全。1. 制定迁移计划CentOS 8.5已于2021年底停止维护这意味着今后不会再收到任何安全更新。CVE-2021-4034只是其中一个已知漏洞未来必然会有新的漏洞出现而无法得到官方修复。最根本的解决方案是制定并执行向受支持系统的迁移计划例如迁移到RHEL需订阅、AlmaLinux、Rocky Linux或CentOS Stream。2. 建立内部补丁仓库如果迁移无法立即进行需要建立一个内部流程持续关注上游社区如AlmaLinux、Rocky Linux的安全公告及时将重要安全补丁包同步到内部仓库并推送到存量CentOS 8.5主机。这需要持续的人力投入。3. 实施最小权限原则即使修复了此漏洞也应审视系统上的SUID/SGID程序。使用命令find / -type f -perm /6000 2/dev/null列出所有此类程序评估每个程序是否业务必需。非必要的应移除特殊权限。4. 加强监控与审计配置审计规则如auditd监控对/usr/bin/pkexec等关键特权二进制文件的执行。同时集中收集和分析系统日志关注异常的用户切换、权限提升行为。5. 考虑应用层防护对于运行在服务器上的应用确保其以最小必要权限运行。避免使用root用户直接运行应用服务。使用容器技术如Docker在一定程度上可以隔离漏洞的影响范围。手动修复CVE-2021-4034的过程更像是一次对老旧系统维护现状的“压力测试”。它暴露出在缺乏官方支持后安全维护成本急剧上升的问题。这次经历让我更加坚信对于生产环境运行在仍有全面支持的生命周期内的系统不是一种选择而是一条必须遵守的底线。在完成本次紧急修复后我们团队立刻启动了将这批服务器向AlmaLinux 8迁移的项目计划这才是治本之策。