php-fpm RCE的POC的理解剖析(CVE-2019-11043)
字数 1717 2025-08-26 22:11:28
PHP-FPM RCE漏洞分析(CVE-2019-11043)深度剖析
漏洞概述
CVE-2019-11043是一个PHP-FPM(FastCGI进程管理器)中的远程代码执行漏洞,影响PHP 7.x版本。该漏洞源于PHP-FPM在处理某些特殊构造的HTTP请求时存在路径解析问题,导致攻击者可以通过精心构造的请求实现任意代码执行。
漏洞环境要求
- PHP版本:7.x
- PHP-FPM配置要求:
[global] error_log = /proc/self/fd/2 daemonize = no [www] access.log = /proc/self/fd/2 clear_env = no listen = 127.0.0.1:9000 pm = dynamic pm.max_children = 5 pm.start_servers = 1 pm.min_spare_servers = 1 pm.max_spare_servers = 1 - Nginx配置要求:
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
漏洞原理分析
异常行为触发
当发送如下请求时:
GET /index.php/test%0atest HTTP/1.1
Host: 192.168.15.166
正常情况下,nginx传给php-fpm的变量中:
SCRIPT_NAME应为/index.php/test\ntestPATH_INFO应为空
但实际$_SERVER["PATH_INFO"]却显示有值,这表明存在异常解析行为。
调试分析
在fcgi_accept_request函数中,init_request_info函数负责初始化请求全局变量。关键问题出现在路径自动修复的代码中:
script_path_translated指向SCRIPT_FILENAME(如/var/www/html/index.php/test\ntest)tsrm_realpath尝试获取绝对路径,因路径不存在返回NULL- 进入while循环尝试路径修复:
while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) { *ptr = 0; if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) { // 路径修复逻辑 } }
关键问题在于:
path_info = env_path_info ? env_path_info + pilen - slen : NULL;
tflag = (orig_path_info != path_info);
其中:
pilen是PATH_INFO长度(初始为0)slen是/test\ntest的长度(攻击者可控)
这导致path_info指向env_path_info前slen字节的位置,造成内存越界访问。
漏洞利用点
漏洞本质是一个任意地址单字节置NULL的漏洞:
- 只能在
env_path_info之前的某个位置修改一个字节为\x0 - 修改的影响必须在字节被改回前触发
利用的关键在于:
- 通过控制
slen(/test\ntest的长度)来精确控制修改位置 - 利用
FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name)或类似函数触发内存修改的影响
内存布局分析
PHP-FPM使用以下关键结构存储环境变量:
struct _fcgi_hash {
fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE];
fcgi_hash_bucket *list;
fcgi_hash_buckets *buckets;
fcgi_data_seg *data;
};
typedef struct _fcgi_data_seg {
char *pos;
char *end;
struct _fcgi_data_seg *next;
char data[1]; // 实际存储环境变量的空间
} fcgi_data_seg;
环境变量存储在fcgi_data_seg.data中,布局如下:
+---------------------+
| pos |--------------------------------------------+
+---------------------+ |
| end | |
+---------------------+ |
| next = 0 | |
+---------------------+-------------------------|------------------+-------——+
| data = xxxx |SCRIPT_NAME\0/index.php\0|PATH_INFO\0/test\0|未使用空间 |
+---------------------+-------------------------|------------------+---------+
精妙的漏洞利用
利用步骤
-
触发malloc:通过发送大量查询参数使现有
fcgi_data_seg空间不足,触发新chunk分配GET /index.php/PHP%0Ais_the_shittiest_lang.php?QQQQ...QQQ HTTP/1.1 -
精确控制偏移:当服务器返回404时,表明已触发malloc且PATH_INFO存储在新chunk首部
-
计算精确偏移:
env_path_info与fcgi_data_seg.pos的固定偏移为34字节- 通过控制
/PHP%0Ais_the_shittiest_lang.php长度为30,使修改pos的第5字节
-
覆盖关键数据:
- 伪造
HTTP_EBUT变量(与PHP_VALUE长度和hash相同) - 通过修改
pos指针,使后续写入操作覆盖HTTP_EBUT为PHP_VALUE
- 伪造
-
实现RCE:
- 设置
PHP_VALUE为恶意配置,如:PHP_VALUE%0Aauto_prepend_file=php://input PHP_VALUE%0Asession.auto_start=1
- 设置
完整利用请求示例
GET /index.php/PHP_VALUE%0Asession.auto_start=1;;;?QQQQ...QQQ HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0
D-Pisos: 8===========================================================D
Ebut: mamku tvoyu
其中:
D-Pisos头部用于调整内存布局,将HTTP_EBUT推到合适位置- 当响应中出现
Set-Cookie头时,表明PHP_VALUE覆盖成功
漏洞修复
PHP官方在以下commit中修复了该漏洞:
https://github.com/php/php-src/commit/ab061f95ca966731b1c84cf5b7b20155c0a1c06a
修复方法:
- 在路径修复逻辑中添加了更严格的边界检查
- 防止了路径解析时的内存越界问题
总结
CVE-2019-11043是一个极其精巧的漏洞,其利用过程展示了:
- 如何将一个看似无害的单字节NULL写入转化为完整的RCE
- 如何通过精确控制内存布局实现漏洞利用
- Web漏洞与二进制漏洞的结合利用技巧
该漏洞的发现和利用过程体现了安全研究人员对PHP-FPM内部机制的深刻理解,以及创造性的漏洞利用思路。