PHP2Shell:关于 CVE-2026-109618 和 CVE-2026-109619, 您需要了解的一切

一、背景

近期,JFrog Security 一直在追踪两个“严重”(CRITICAL)级别的 PHP 核心漏洞(CVE-2026-109618 和 CVE-2026-109619)。这些漏洞于近日披露,并被统称为“PHP2Shell”。该漏洞允许攻击者在无需身份验证的情况下执行远程代码,并能引发类似于“心脏出血”(Heartbleed)的内存信息泄露;这些问题存在于 PHP 自身之中,与运行在 PHP 之上的具体应用程序无关。本文将深入探讨这些漏洞、其利用方式、运作机制,以及实现漏洞利用所需的前提条件。

10月20日,PHP安全团队宣布将于协调世界时(UTC)10月21日14:00发布一个安全更新版本。该版本旨在修复两个严重漏洞——CVE-2026-109618和CVE-2026-109619,这两个漏洞的CVSS v4.0评分均为9.2。

这一时间节点给防御方带来了棘手的局面。各机构已获悉,互联网上应用最广泛的服务器端技术之一存在严重漏洞,但官方尚未发布具体的技术细节或修复版本。与此同时,研究人员一直在对这些问题进行调查,且已有关于潜在漏洞利用的报告浮现。

二、哪些对象会受到 PHP2Shell CVE 漏洞的影响:

PHP 版本:

  • PHP 8.2.x(2.35 之前的版本)
  • 3.x(8.3.36 之前的版本)
  • 4.x(8.4.27 之前的版本)
  • 5.x(8.5.12 之前的版本)

配置:

  • upload_progress.enabled=On(出厂默认值),且支持多部分(multipart)上传。

服务器环境:

  • 对于已确认的、与具体应用程序无关的远程代码执行(RCE)漏洞,涉及Apache mod_php(以及可能的 FrankenPHP)。若使用 FPM,则可能因系统成功启动后所暴露的状态信息而面临风险。

三、修复指南

  • 升级至 PHP 8.5.12、8.4.27、8.3.36 或 8.2.35。
  • 如果无法进行升级,根据 PHP 相关安全公告,您可以采取以下措施进行部分缓解:通过管理设置upload_progress.enabled=0(若使用 PHP 8.5.x,还需设置fatal_error_backtraces=0),并在修改配置后重启所有工作进程(worker);同时,设置 memory_limit=-1(注意不要使用 RLIMIT_AS 或 RLIMIT_DATA )。此外,设置 pm.max_requests=1 可以解除“必须使用同一工作进程”的限制。请注意,关闭 upload_progress 并不影响 OPcache 或回溯跟踪(backtrace)相关机制;同时,设置 memory_limit=-1 会移除针对资源耗尽的保护措施。上述各项措施单独使用时,均只能起到部分缓解作用。
  • JFrog Xray 用户可获得保护。Xray 能够识别出扫描对象(制品及依赖项)中处于受影响范围内的 PHP 版本。

四、技术细节

当天晚些时候,Wiz 证实(https://www.wiz.io/blog/php2shell-php-rce),这些 CVE 是他们与 Calif(一家安全研究实验室)及 OpenAI 共同发现的,属于“Patch the Planet”(修补地球)计划的一部分。

在这些漏洞被公开之前,JFrog 团队成功探明了其大致的触发流程,并构建出了多个可行的概念验证(POC)。我们推断,该漏洞的触发流程大致如下:

  1. PHP 具备处理多部分(multipart)POST 请求的机制;它会在磁盘上创建一个会话文件,用于追踪上传进度。用户可以控制会话 ID(通过普通的 POST 字段 PHPSESSID 发送)、会话文件内部键名的后缀(PHP_SESSION_UPLOAD_PROGRESS),以及上传的文件名。由于文件名会按原样存储,这使得用户能够将潜在的恶意数据插入到该文件中。
  2. 会话(session)文件始终以序列化格式存储。当 PHP 引擎打开会话时,会在任何脚本运行之前对文件内容进行反序列化。解码器会全盘接受其读取到的内容:它并不验证这些字节是否确实源自 PHP 自身,因此,用户通过上传操作所干预的数据,会被解码为实际生效的会话数据。
  3. 反序列化过程并非简单的解析:在执行期间,PHP 会构建 zval(这是 PHP 用于存储动态值的数据结构,在此过程中涉及内存的分配与释放),并通过一个专门的记录结构对其进行追踪。该结构记录了已构建的各项数值,以便后续处理引用和析构函数。这种记录机制的前提是:在同一个请求周期内,每一次初始化操作都必然对应着相应的清理操作。
  4. 由同一个工作进程(worker)处理的另一次分段上传——该进程会在任何脚本运行前开启会话——会再次对该文件进行解码。第一次解码破坏了前述(#3)中的关联:由于报错导致清理步骤被跳过,PHP 的全局变量依然指向那块已被释放的、用于记录状态的内存区域。第二次上传所分配的内存恰好占用了该区域;随后进行的解码操作所处理的是攻击者可控的字节数据,而会话回写(session write back)过程会调用伪造对象的序列化函数,最终导致攻击者能够控制 RIP 寄存器。

五、程序错误(Bugs)

这些漏洞使得创建以下利用程序成为可能:

  • 远程代码执行
  • 信息泄露

而这两种漏洞均可被用于实现其中任一目标。

PHP 的两份公告从两个角度描述了同一个缺陷:

CVE-2026-109618:将属于特定请求的内存指针保存在持久化变量中;当发生致命错误时,若后续请求信任该已失效的指针,则可能导致“释放后使用”(use-after-free)漏洞,进而引发远程代码执行(RCE)或内存泄漏。

CVE-2026-109619:该漏洞涉及其他持久化变量,表现出相同的模式,包括 PHP 8.5 中保存的致命错误回溯信息,以及 OPcache 共享内存状态在不同工作进程(worker)间发生损坏的情况。

六、修复方案

以下是 PHP 团队发布的修复方案:

此修复解决了以下问题:

  • 内存不足(OOM)不再导致操作中途异常终止:此前之所以会出现“释放后使用”(UAF)问题,是因为严重的 OOM 触发了 longjmp,直接跳出了本应负责清除指针的代码段。现在,OOM 在虚拟机(VM)的下一个安全点会被处理为“软错误”(若超过更高阈值,则会触发强制的 _exit());因此,清理逻辑总能得到执行,确保在内存释放前指针已被清除。
  • 纵深防御:即便请求以其他方式终止,所有长期存在的状态(如回溯信息、OPcache、会话、错误状态等)也都会在关闭时被重置,从而确保不会有任何陈旧数据残留到下一个请求中。
  • 在上传进度(upload-progress)解码过程中,不会执行任何用户代码:由上传进度处理程序解码得到的对象会保持为 __PHP_IncompleteClass 状态——既不会触发自动加载(autoload),也不会调用析构函数(__destruct()),因此本文所验证的对象注入攻击途径在此也被封堵了。

七、虚假的概念验证(PoC)

在调查 GitHub 上发布的 PoC(概念验证)时,我们发现一些用户借机散布虚假的 PoC,以此进行恶作剧。

其中一个代码仓库中隐藏着一段经过 Base64 编码的字符串;乍看之下,它似乎会解密并执行恶意软件载荷。然而,经我们调查确认,这并非真正的恶意软件。实际上,该脚本的作用是将受害者的桌面壁纸更改为不宜在工作场合展示(NSFW)的内容。

八、JFrog 能提供什么帮助?

JFrog 正在持续追踪各类漏洞,并协助各组织做好应对准备。

JFrog Xray 和 Catalog 的漏洞情报中已包含 CVE-2026-109618 和 CVE-2026-109619,能够帮助团队通过现有的安全工作流程识别出这些问题。

此外,我们的安全团队已部署上下文扫描程序,旨在确定这些 CVE 对于 JFrog Advanced Security 客户的适用性。

九、入侵指标(IoCs)

JFrog XRAY IDs:

OS / Platform CVE-2026-109618 CVE-2026-109619
Oracle XRAY-1141250 XRAY-1141258
Amazon XRAY-1141248 XRAY-1141256
Suse XRAY-1141245 XRAY-1141253
Ubuntu XRAY-1141247 XRAY-1141255
Alpine XRAY-1141243 XRAY-1141251
RedHat XRAY-1141249 XRAY-1141257
Minimos XRAY-1141246 XRAY-1141254
Debian XRAY-1141737 XRAY-1141738
Cpp XRAY-1141244 XRAY-1141252