分析一下这个PHP在Windows平台上的原生RCE,PHP CGI 参数注入漏洞绕过
CVE-2024-4577
原作者文章,此漏洞影响安装在 Windows 操作系统上的 PHP 版本:
1 | PHP Windows版 8.3.0 < 8.3.8 |
漏洞利用条件:
1 | Windows |
早在 2012 年,php 就出现过CVE-2012-1823,其原理是,如果请求 URL 类似:
1 | /index.php?-d+allow_url_include=1+-d+auto_prepend_file=php://input |
PHP CGI 会把 ? 后内容当成命令行参数。于是可以使用php-cgi修改php配置:
1 | php-cgi.exe -d allow_url_include=1 ... |
最终实现:任意php代码执行
后来 PHP 加了防护,会检查:
1 | if (query_string[0] == '-') |
只要发现 query_string 以 - 开头,就拒绝。看似修好了。但 CVE-2024-4577 的关键在于:
“Windows 字符编码转换导致攻击者能构造一个不是 -,但转换后又变成 - 的字符。”
这就绕过了修复,所以本质是对patch的一次再绕过。
CVE-2012-1823
CGI(Common Gateway Interface)本质:Web Server 启动一个外部程序处理请求。
例如:Apache 收到:
1 | GET /index.php HTTP/1.1 |
然后Apache 实际会执行:
1 | php-cgi.exe index.php |
并通过:环境变量、stdin、argv传递请求信息。CGI 本质是命令行程序,这是后面所有问题根源。
核心原理
核心问题在于:
Windows 的 Best-Fit 字符转换机制。Windows 在:
1 | Unicode <-> ANSI |
转换时,会进行“近似映射”。
某些 Unicode 字符虽然不是 ASCII -,但转换到特定 code page 时:
会被自动映射成:0x2D ('-')
于是HTTP 请求:
1 | /%ADd+auto_prepend_file=php://input |
中的 %AD经过 Windows 转换后变成:
1 | -d auto_prepend_file=php://input |
源码分析
CGI模式本质是apache启动cgi.exe去处理请求,所以先看apache的源码,分析请求里面的参数是怎么走的,
假设http请求收到GET /index.php?-d+foo=bar HTTP/1.1,定位到/modules/generators/mod_cgi.c
static void add_ssi_vars(request_rec *r)将请求参数写入环境变量QUERY_STRING:

然后看default_build_command,这里在解析请求参数,相当于请求被拆分为了-d、foo=bar
最终argv为
1 | argv[1] = "-d" |

static int cgi_handler(request_rec *r)逻辑中,解析了参数之后,传入run_cgi_child运行cgi

到这里Apache 结束,进程切换到php-cgi部分。php8.0.28漏洞存在的版本:sapi/cgi/cgi_main.c
搜索query_string,定位到下面的部分,仅对字符-做了检查,如果存在-,那就启用skip_getopt,后续不会继续执行

如果skip_getopt没被触发,那就进入main\getopt.c#php_getopt()进行实际的cgi参数解析,最后会返回输入的参数进入switch,

-d动态修改 php.ini 配置,php-cgi -d auto_prepend_file=php://input;
-b让php-cgi作为独立FastCGI Server,php-cgi -b 127.0.0.1:9000
环境配置
搭建一个Windows虚拟机,下载一个受影响的XAMPP版本并安装

开启Apache即可,无需额外配置。
复现
修改原利用脚本,增加webshell写入能力:
1 | def write_file(host, http_port, cgi_bind_port, php_file, target_file, content): |
命令执行:

写入webshell,

建议哥斯拉连接:
