Intro
从实时监控的安全信息流推送发现Orange师傅的博客更新了,看到发布了CVE-2024-4577 - Yet Another PHP RCE: Make PHP-CGI Argument Injection Great Again!
[1],几行文字介绍了对于 PHP-CGI远程代码执行漏洞(CVE-2012-1823)的绕过,提到了对Windows平台下的影响,后面分析得知是利用Windows平台下字符编码的Best Fit特性,对于前身CVE-2012-1823漏洞的绕过,从而Bypass了转义继续参数拼接,导致RCE。
漏洞通报[2]中还提到别的信息,可知此漏洞有特殊的条件限制,就是:
-
直接用PHP-CGI启动 -
要求Windows操作系统需为:繁体中文、简体中文、日文,这三个其中之一的语言版本(就目前来说)。
影响的版本为:
-
8.3 ≤ version < 8.3.8 -
8.2 ≤ version < 8.2.20 -
8.1 ≤ version < 8.1.29
Background
为了更好的分析这个漏洞的成因,找到并阅读了之前p神对于PHP-CGI远程代码执行漏洞(CVE-2012-1823)分析
[3]这篇文章,接下来让我们enjoy!!!
PHP-CGI(Common Gateway Interface)是一种在服务器上处理 PHP 脚本的模式。它将 Web 服务器与 PHP 解释器连接,使得 Web 服务器能够将 HTTP 请求传递给 PHP 脚本进行处理。
假设有一个 HTTP 请求 URL:
http://host/cgi.php?foo=bar
这个请求被 Web 服务器接收到之后,解析为调用 PHP CGI 解释器的命令会是:
php.cgi.exe cgi.php foo=bar
以下是 PHP-CGI 工作的基本流程:
-
接收请求:Web 服务器接收一个 HTTP 请求,例如 http://host/cgi.php?foo=bar
。 -
解析请求:Web 服务器解析请求的 URL 和查询字符串,将其转换为可以传递给 PHP 脚本的格式。 -
调用 PHP 解释器:Web 服务器通过 CGI 接口调用 PHP 解释器(例如 php-cgi
),并将请求的信息(包括查询字符串)传递给 PHP 解释器。 -
执行脚本:PHP 解释器接收请求信息并执行指定的 PHP 脚本(例如 cgi.php
)。 -
处理查询字符串:PHP 解释器解析查询字符串,将参数(例如 foo=bar
)传递给 PHP 脚本。 -
返回结果:PHP 脚本处理完成后,生成的输出(通常是 HTML 内容)通过 PHP 解释器返回给 Web 服务器。 -
响应客户端:Web 服务器将 PHP 脚本的输出作为 HTTP 响应返回给客户端浏览器。
对于上面的流程,如果命令行参数可控,能做些什么事呢?对头,原因就在这哈哈哈哈。通过-h
,我们可以发现cgi模式下有如下一些参数可用:
Usage: php [-q] [-h] [-s] [-v] [-i] [-f <file>]
php <file> [args...]
-a Run interactively // 交互式运行
-b <address:port>|<port> Bind Path for external FASTCGI Server mode // 绑定路径用于外部 FASTCGI 服务器模式
-C Do not chdir to the script's directory // 不要切换到脚本的目录
-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 // 显示已编译的模块
-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> 次
最简单的利用方式,当然就是-s
,可以直接显示源码:
当然,我们也可以通过使用-d
指定auto_prepend_file
来制造任意文件包含漏洞,执行任意代码:
前因漏洞最后官方的修复方式就是跳过所有空白符(小于等于空格的所有字符),再判断第一个字符是否是-
,具体sapi/cgi/cgi_main.c
代码如下:
if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
/* we've got query string that has no = - apache CGI will pass it to command line */
unsigned char *p;
decoded_query_string = strdup(query_string);
php_url_decode(decoded_query_string, strlen(decoded_query_string));
for (p = decoded_query_string; *p && *p <= ' '; p++) {
/* skip all leading spaces */
}
if(*p == '-') {
skip_getopt = 1;
}
free(decoded_query_string);
}
Analyse
根据PHP漏洞修复提交的Commit[4], 发现官方对前面我们提到的sapi/cgi/cgi_main.c
文件新增了Windows环境下单独的逻辑判断,具体代码如下:
#ifdef PHP_WIN32
if (*p >= 0x80) {
wchar_t wide_buf[1];
wide_buf[0] = *p;
char char_buf[4];
size_t wide_buf_len = sizeof(wide_buf) / sizeof(wide_buf[0]);
size_t char_buf_len = sizeof(char_buf) / sizeof(char_buf[0]);
if (WideCharToMultiByte(CP_ACP, 0, wide_buf, wide_buf_len, char_buf, char_buf_len, NULL, NULL) == 0
|| char_buf[0] == '-') {
skip_getopt = 1;
}
}
#endif
这段代码检查输入字符是否为非 ASCII 字符(ASCII 码 >= 0x80),如果是,则将其转换为宽字符并尝试转换为多字节字符。如果转换失败或者转换后的第一个字符是 '-'
,则设置一个标志 skip_getopt
为 1,以跳过后续的选项处理。这样确保了处理非 ASCII 字符时的正确行为。并且其中对请求参数使用了WideCharToMultiByte
函数,即宽字节的转换。
所以漏洞的绕过就在对宽字节的处理不足上,此漏洞利用的是%AD
进行绕过,对应的编码表参考如下:
https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit936.txt
肉眼看起来都一样,我们放到16进制编辑器中,就能够发现端疑了,在中文的操作系统显示如下:
你是不是很难分辨,肉眼来看都一样,但实际上对于操作系统来说含义却大不一样。而英文操作系统下,显示如下:
这里也就解释了,为什么这个漏洞利用对Windows操作系统语言有要求。同时也就Bypass了导致RCE。
因此我们原来的CVE-2012-1823
利用的payload,变身如下:
# raw
-d allow_url_include=1 -d auto_prepend_file=php://input
--
---> -d+allow_url_include%3don+-d+auto_prepend_file%3dphp%3a//input # urlencode
----> %ADd+allow_url_include%3don+%ADd+auto_prepend_file%3dphp://input
End...
Exploit
接下来我们来复现一下,此漏洞利用场景有以下两个:
场景一:在 CGI 模式下运行 PHP
在配置Action指令将相应的 HTTP 请求映射到 Apache HTTP Server 中的 PHP-CGI 可执行二进制文件时,可直接利用此漏洞。受影响的常见配置包括但不限于:
AddHandler cgi-script .php
Action cgi-script "/cgi-bin/php-cgi.exe"
或者
<FilesMatch ".php$">
SetHandler application/x-httpd-php-cgi
</FilesMatch>
Action application/x-httpd-php-cgi "/php-cgi/php-cgi.exe"
场景 2:公开 PHP 二进制文件(也是默认的 XAMPP 配置)
即使 PHP 没有配置为 CGI 模式,仅仅将 PHP 可执行文件暴露在 CGI 目录中也会受到此漏洞的影响。常见场景包括但不限于:
-
将php.exe或复制php-cgi.exe到/cgi-bin/目录。 -
通过指令公开 PHP 目录ScriptAlias,例如:
ScriptAlias /php-cgi/ "C:/xampp/php/"
利用场景一来进行复现,下载安装XAMPP软件,PHP Version 8.2.12。链接如下:https://www.apachefriends.org/zh_cn/index.html
修改Apache的配置文件httpd-xampp.conf
将上面演示CVE-2012-1823
的poc中的-替换为%ad
Conclusion
分析到这里,我感到非常惊喜(wow)。通过此次漏洞的分析,让我对漏洞挖掘有了更深的理解,尤其是从历史漏洞和底层源码开始的挖掘之道,Bypass techniques are ubiquitous.
References
-
https://blog.orange.tw/2024/06/cve-2024-4577-yet-another-php-rce.html -
https://devco.re/blog/2024/06/06/security-alert-cve-2024-4577-php-cgi-argument-injection-vulnerability/ -
https://www.leavesongs.com/PENETRATION/php-cgi-cve-2012-1823.html -
https://github.com/php/php-src/commit/4dd9a36c16?diff=split&w=0 -
https://labs.watchtowr.com/no-way-php-strikes-again-cve-2024-4577/ -
https://mp.weixin.qq.com/s/UMz2WR7fztvEWtFx43anvQ
原文始发于微信公众号(山石网科安全技术研究院):PHP CGI Windows平台远程代码执行漏洞(CVE-2024-4577)分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论