跳转到帖子

CVE-2024-4577:Windows PHP-CGI 参数注入的无害验证、日志排查与修复

精选回复

发布于
本文仅用于已授权环境中的漏洞确认。验证过程使用独立 canary 文件,不执行系统命令、不写入持久化内容,避免直接在业务入口测试。

一、漏洞背景

CVE-2024-4577 是 Windows 平台 PHP-CGI 参数注入漏洞。PHP 以 CGI 方式运行时,会在特定条件下把不包含等号的查询字符串解释为命令行参数。Windows 部分代码页存在 Best-Fit 字符映射,例如软连字符 0xAD 可能被转换为普通连字符 0x2D,从而绕过 PHP 过去针对 CGI 参数注入的检查。

常见风险环境包括 Windows、Apache、php-cgi.exe 组合,尤其是使用日文或中文代码页的主机。CP932、CP936、CP950 是排查时需要重点关注的代码页,但不应只根据系统语言判断是否安全。

PHP 官方在 8.1.29、8.2.20、8.3.8 中修复了该问题。PHP 7.x、8.0 等已停止维护的分支不应继续使用。实际处置时应升级到当前仍受支持分支的最新安全版本,而不是仅停留在最低修复版本。

二、漏洞成立条件

该漏洞不是“Windows 上安装 PHP 就一定可利用”,至少需要确认以下条件:

  • PHP 运行于 Windows。
  • Web 服务器将 PHP 请求交给 php-cgi.exe,并采用可能触发 CGI 参数解析的方式。
  • 系统代码页或字符转换链路可将特殊字符映射为命令行选项前缀。
  • 请求在进入 php-cgi.exe 前未被补丁或有效规则拦截。

Apache 配置中出现以下结构时需要重点核查。该配置仅作为风险特征示例,不建议直接部署:

ScriptAlias /php-cgi/ "C:/xampp/php/"
Action application/x-httpd-php "/php-cgi/php-cgi.exe"
AddHandler application/x-httpd-php .php

需要注意,安装目录中存在 php-cgi.exe 并不等于正在使用 CGI。部分 FastCGI 部署同样使用该文件,因此最终应以 Web 服务器处理器映射、实际请求链路和无害验证结果为准。

三、资产与配置排查

可以先在 Windows 主机上确认 PHP 版本、活动代码页以及 Apache 配置中的 CGI 映射:

$roots = @("C:\xampp", "C:\Apache24", "C:\php")

Get-ChildItem $roots -Filter php-cgi.exe -Recurse -ErrorAction SilentlyContinue |
  ForEach-Object {
    $version = & $_.FullName -v | Select-Object -First 1
    "{0} => {1}" -f $_.FullName, $version
  }

chcp

Get-ChildItem "C:\xampp\apache\conf" -Recurse -File -ErrorAction SilentlyContinue |
  Select-String -Pattern 'ScriptAlias|Action\s+application/x-httpd-php|php-cgi\.exe'

若使用 IIS,还应检查处理程序映射,区分 CGI 与 FastCGI,并确认实际引用的 PHP 路径:

%windir%\system32\inetsrv\appcmd.exe list config /section:system.webServer/handlers
%windir%\system32\inetsrv\appcmd.exe list config /section:system.webServer/fastCgi

资产盘点时不要只检查 PATH 中的 PHP。XAMPP、旧版运维面板、遗留站点目录中经常同时存在多套 php-cgi.exe,Web 服务实际加载的版本可能与命令行执行的 php.exe 不一致。

四、无害验证方法

推荐在测试虚拟主机中新建一个独立 canary 文件,通过 CGI 的 -s 源码显示选项验证参数是否被注入。该方法不执行操作系统命令,也不依赖外连。

首先创建仅用于验证的文件,其中注释标记不会在正常执行时输出:

Set-Content `
  -Path "C:\xampp\htdocs\cve4577_canary.php" `
  -Encoding Ascii `
  -Value '<?php /* SOURCE_ONLY_4577 */ echo "NORMAL"; ?>'

先发送普通请求建立基线,再发送保留原始路径编码的测试请求:

curl.exe -i --path-as-is "
curl.exe -i --path-as-is " NORMAL。
  • 如果第二个请求返回经过语法高亮或转义后的 PHP 源码,并能看到 SOURCE_ONLY_4577,说明特殊字符已被转换为 -s,CGI 参数注入成立。
  • 如果第二个请求返回 403,可能已经存在临时拦截规则,仍需确认 PHP 版本是否已升级。
  • 如果第二个请求仍只返回 NORMAL,不能仅凭这一点完全排除风险,还应核对处理器映射、代码页、前置代理是否改写请求以及测试请求是否原样到达源站。
  • 验证完成后删除 canary 文件:

    Remove-Item "C:\xampp\htdocs\cve4577_canary.php" -Force

    不建议在生产业务文件上使用 -s 测试。若漏洞成立,该选项可能直接暴露源码中的数据库口令、接口密钥或内部路径。

    五、技术影响与权限边界

    -s 只能用于低影响确认,但漏洞本身不仅限于源码泄露。攻击者还可能通过 CGI 的配置参数改变 PHP 运行行为,最终让请求体中的 PHP 内容被解释执行。

    执行权限通常继承自 Apache、IIS 应用程序池或对应服务账户,并不天然等于 SYSTEM。实际影响取决于该身份的权限边界:

    • 是否可写 Web 根目录、上传目录、临时目录及计划任务目录。
    • 是否能读取 wp-config.php、数据库连接配置、云服务密钥和部署凭据。
    • 服务账户是否拥有 SeImpersonatePrivilege、管理员组成员身份或网络共享权限。
    • 主机是否可访问数据库、域控管理接口、Redis、文件服务器等内网资源。

    在 WordPress 等 PHP 应用中,即使 Web 服务账户权限较低,读取 wp-config.php 后获得数据库凭据,也可能进一步导致管理员账户接管、插件写入或内网数据库访问。因此评估时不能只看是否获得本机管理员权限。

    六、访问日志排查

    Apache access.log 通常会保留原始请求目标,可优先搜索软连字符编码、CGI 配置参数和异常 PHP 输入特征:

    $log = "C:\xampp\apache\logs\access.log"
    
    Select-String -Path $log -Pattern `
      '%AD', '%ad', '%C2%AD', '%c2%ad', `
      'allow_url_include', 'auto_prepend_file', `
      'php://input', 'php-cgi'

    IIS 日志可使用类似方式检查:

    Get-ChildItem "C:\inetpub\logs\LogFiles" -Recurse -File -ErrorAction SilentlyContinue |
      Select-String -Pattern '%AD|%ad|%C2%AD|%c2%ad|auto_prepend_file|allow_url_include'

    排查时应同时检查反向代理、WAF、CDN 和源站日志。部分组件会解码、规范化或丢弃原始查询字符串,单一日志源没有命中不能作为未受攻击的依据。

    重点关注以下组合特征:

    • 查询字符串以 %AD 开头,且没有常规键值形式。
    • 同一来源先探测 -s,随后出现 POST 请求或其他 CGI 配置参数。
    • 请求返回 200、500 后,Web 目录或临时目录出现新文件。
    • php-cgi.exe、httpd.exe、w3wp.exe 随后启动 cmd.exe、powershell.exe、certutil.exe 或脚本解释器。

    七、主机侧留痕检查

    如果已部署 Sysmon,可检查进程创建事件中异常的父子关系:

    Get-WinEvent -FilterHashtable @{
      LogName = 'Microsoft-Windows-Sysmon/Operational'
      Id      = 1
    } -ErrorAction SilentlyContinue |
    Where-Object {
      $_.Message -match 'ParentImage:.*(php-cgi|httpd|w3wp)\.exe' -and
      $_.Message -match 'Image:.*(cmd|powershell|certutil|mshta|rundll32)\.exe'
    } |
    Select-Object TimeCreated, Id, Message

    没有 Sysmon 时,可在已启用进程创建审核的主机上检查 Security 4688。还应结合以下位置进行时间线分析:

    • 站点根目录、上传目录以及 Windows Temp 中近期新增或修改的 PHP、PHTML、BAT、PS1 文件。
    • Apache error.log、PHP error_log 和 Windows 应用程序日志中的异常报错。
    • 计划任务、服务、启动项及 IIS/Apache 配置的近期变更。
    • 数据库账户登录日志及来自 Web 主机的异常管理操作。

    若发现 Web 服务进程派生系统命令、配置凭据被读取或站点目录出现未知文件,应按已入侵事件处理,而不是仅完成补丁升级。需要隔离主机、保全日志、轮换数据库和应用密钥,并重新部署可信版本。

    八、修复与缓解

    首选方案是升级 PHP,并重启所有相关 Web 服务,确认新进程实际加载了升级后的文件。只替换 php.exe 而遗漏 php-cgi.exe,或者升级后未重启 Apache/IIS,都可能导致漏洞继续存在。

    • 升级到当前仍受支持 PHP 分支的最新安全版本。
    • 移除未使用的旧 PHP 目录及处理器映射,避免请求仍被转发到旧版 php-cgi.exe。
    • 没有业务需要时,取消 CGI 映射和可直接访问的 php-cgi ScriptAlias。
    • 以低权限专用账户运行 Web 服务,禁止其写入程序目录和系统目录。
    • 将站点代码设置为只读,仅允许上传目录和必要缓存目录写入。
    • 轮换可能已暴露的数据库密码、WordPress salts、API 密钥和部署凭据。

    无法立即升级时,可参考临时重写规则拒绝以相关编码开头的查询字符串:

    RewriteEngine On
    RewriteCond %{QUERY_STRING} ^%ad [NC]
    RewriteRule ^ - [F,L]

    该规则只能作为短期缓解措施。代理解码顺序、双重编码、不同日志规范化方式和服务器配置差异都可能影响效果。部署后必须使用原始编码请求从代理入口和源站分别复测,不能把 WAF 或 RewriteRule 当作永久修复。

    九、修复复核

    修复后建议完成以下闭环:

    • php-cgi.exe -v 显示的版本属于受支持且已修复的版本。
    • Apache/IIS 实际处理器映射指向升级后的唯一 PHP 路径。
    • 服务重启后,相关进程的启动时间和映像路径符合预期。
    • 无害 canary 测试不再显示源码。
    • 代理层和源站日志均能记录或拦截测试请求。
    • 历史日志中没有可疑参数、异常子进程和落地文件;如有命中,已转入事件响应。

    十、总结

    CVE-2024-4577 的关键不只是版本号,而是 Windows 字符映射、PHP-CGI 参数解析和 Web 服务器处理器配置共同形成的攻击面。排查时应先确认真实请求链路,再使用独立 canary 文件做低影响验证,并结合访问日志、进程事件和文件时间线判断是否已经被利用。修复应以升级受支持版本和移除不必要 CGI 映射为主,重写规则只能用于临时止血。

    服务器与云环境安全示意图
    服务器、云资产与权限边界梳理示意
    网络流量与边界分析示意图
    网络请求、日志与边界流量分析示意

    这类问题建议先确认“是否使用 Windows 下的 php-cgi.exe”,不要只看 PHP 版本。PHP-FPM、Apache 模块模式和 CLI 不属于同一暴露面,常见风险配置是 Apache 的 Action/ScriptAlias、XAMPP,或 IIS FastCGI 直接映射到 php-cgi.exe。

    可以先在服务器上确认版本和进程:

    where.exe php-cgi.exe
    php-cgi.exe -v
    
    Get-CimInstance Win32_Process |
      Where-Object { $_.Name -ieq "php-cgi.exe" } |
      Select-Object ProcessId, ParentProcessId, ExecutablePath, CommandLine

    修复版本至少为 PHP 8.1.29、8.2.20、8.3.8;更老且已停止维护的分支建议直接升级到当前受支持版本,而不是只改一两个 php.ini 参数。

    如果需要无害验证,建议仅在本机或隔离测试环境放一个临时脚本,用请求级、无副作用的配置项判断参数是否被注入:

    <?php
    header('Content-Type: text/plain');
    echo ini_get('memory_limit'), PHP_EOL;
    curl.exe --path-as-is "
    curl.exe --path-as-is "
    
    

    如果第二次响应变成 123M,说明软连字符经过 Windows 字符转换后被当成了 -d 参数,存在注入风险。测试后应立即删除脚本;不要在生产环境使用包含代码执行、文件包含或外连行为的 PoC。

    日志排查不能只搜明文 %AD,反向代理、WAF 和 Web 服务器可能记录原始值、二次编码值或 UTF-8 形式。可先查以下组合:

    $pattern = '(?i)(%ad|%c2%ad|%25ad).*(auto_prepend_file|allow_url_include|php://|data://|%ad[dns])'
    
    Get-ChildItem C:\Apache24\logs,C:\inetpub\logs\LogFiles -Recurse -File -ErrorAction SilentlyContinue |
      Select-String -Pattern $pattern

    重点关注查询串开头出现 %ADd、%ADs、%ADn,以及同一请求中出现 auto_prepend_file、allow_url_include、php://input 等内容。还要结合状态码、响应长度和后续访问判断,单条 403 不代表成功利用。

    主机侧建议同步查看 php-cgi 是否拉起了异常子进程。启用了进程创建审核时,可查询安全日志 4688;Sysmon 则看事件 1:

    Get-WinEvent -FilterHashtable @{
      LogName='Microsoft-Windows-Sysmon/Operational'
      Id=1
      StartTime=(Get-Date).AddDays(-7)
    } | Where-Object {
      $_.Message -match 'ParentImage:.*php-cgi\.exe' -or
      $_.Message -match 'ParentCommandLine:.*php-cgi\.exe'
    } | Format-List TimeCreated, Message

    如果发现 php-cgi.exe 派生 cmd.exe、powershell.exe、certutil.exe、curl.exe、mshta.exe 等进程,应按失陷处理,而不是升级后就结束:保留 Web/代理日志、隔离主机、检查 Web 目录新增文件、计划任务、服务、启动项和账号变更。

    临时无法升级时,优先移除不必要的 CGI/FastCGI 映射或限制到可信来源。Apache 可在补丁上线前临时拦截原始查询串中的软连字符编码:

    RewriteEngine On
    RewriteCond %{THE_REQUEST} "\?[^\ ]*(%ad|%c2%ad|%25ad)" [NC]
    RewriteRule ^ - [F,L]

    这类规则只能作为临时缓解,且需要确认代理层没有提前解码或规范化 URL。最终还是应升级 PHP、重启对应 Web/FastCGI 进程,并再次确认实际运行的 php-cgi.exe 路径和版本,避免机器上存在多套 PHP、升级了错误目录。

    创建帐户或登录后发表意见

    最近浏览 0

    • 没有会员查看此页面。

    SiteMap HACKHAT © 2026 All rights reserved.