只需某些特定的Nginx + PHP-FPM 配置,即可触发PHP 远程代码执行漏洞
9月26日,安全研究员d90pwn 在 Real World CTF 中发现了一个 PHP 远程代码执行漏洞 (CVE-2019-11043)。他和另外两名研究员 Emil Lerner 和 beched 研究发现,在某些 nginx +php-fpm 配置下,漏洞即可被触发。也就是说如果配置易受攻击,那么web 用户可执行代码。
漏洞概述
如果一台 web 服务器运行 nginx +php-fpm且 nginx 的配置如下:
location ~ [^/]\.php(/|$) {
...
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
...
}
且缺乏对脚本存在的检查(如 try_files),那么就可以触发漏洞。
d90pwn 指出,可以通过换行符 (%0a) 来攻破 fastcgi_split_path_info指令中的 regexp(正则表达式),从而导致 PATH_INFO 为空,进而触发该漏洞。该漏洞可引发代码执行后果。之后代码中path_info[0] 的值被设定为0 (https://github.com/php/php-src/blob/master/sapi/fpm/fpm/fpm_main.c#L1150),之后调用 FCGI_PUTENV 。使用精心选择的 URL 路径和查询字符串的长度,攻击者能够将 path_info 精确地指向 _fcgi_data_seg 结构的第一个字节。放入0后,`char*pos` 字段被向后移动,然后 FCGI_PUTENV 使用脚本路径覆盖某些数据(包括其它快速的cgi 变量)。它通过这种技术创建了一个伪造的PHP_VALUE fcgi 变量,然后使用一系列精心选择的配置值来执行代码。
因此,触发该漏洞的完整前提条件是:
1、 Nginx + php-fpm,必须将 location ~ [^/]\.php(/|$) 发送给 php-fpm(可能 regexp 可以更为严格一点)。
2、 指令 fastcgi_split_path_info 必须存在且包含以 ^ 开头且以 $ 结尾的一个 regexp,这样我们可以用换行符攻破。
3、 必须存在经由语句 fastcgi_param PATH_INFO$fastcgi_path_info; 获得的变量赋值PATH_INFO。最开始,我们以为它一直会出现在fastcgi_params 中,但事实上并非如此。
4、 缺乏文件存在检查如 try_files $uri =404 或 if (-f $uri)。如果 Nginx 在FastCGI 发送之前释放不存在的脚本请求,那么我们的请求永远无法触及 php-fpm。而这也是最容易修复的方式。
d90pwn 指出,之前php-fpm 并不会限制脚本的扩展,也就是说,/avatar.png/some-fake-shit.php 等就能作为PHP 脚本执行avatar.png。不过该问题在2010年左右修复。
测试脚本
复现需要采取如下步骤:
1、 通过 --enable-fpm 构建 php 并启用 ASAN。
2、下载https://www.dropbox.com/s/eio9zikkg1juuj7/reproducer.tar.xz?dl=0。如下步骤假设你位于“reproducer”目录。
3、 使用 `path/to/php-fpm -y./php-fpm.conf -F` 运行 php-fpm。
4、 使用一款工具如 curl $(cat crash_link.txt) 从crash_link.txt 访问一个(非常长的)链接。
最后得到的是崩溃结果。
==6629==ERROR: AddressSanitizer: SEGV on unknown address 0x620000005203 (pc 0x7efd1341a47f bp 0x7ffe980574e0 sp 0x7ffe98056c98 T0)
==6629==The signal is caused by a WRITE memory access.
#0 0x7efd1341a47e in memcpy /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:140
#1 0x4b7c57 in __asan_memcpy /home/emil/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#2 0x13a88df in fcgi_hash_strndup /home/emil/php-src/main/fastcgi.c:322:2
#3 0x13a88df in fcgi_hash_set /home/emil/php-src/main/fastcgi.c:359:11
#4 0x13c4121 in init_request_info /home/emil/php-src/sapi/fpm/fpm/fpm_main.c:1154:12
#5 0x13c4121 in main /home/emil/php-src/sapi/fpm/fpm/fpm_main.c:1864:4
#6 0x7efd13380b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
#7 0x440219 in _start (/home/emil/php-src/builded/sbin/php-fpm+0x440219)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:140 in memcpy
影响版本
具体而言,从 git blame 来看,错误假设env_path_info 非空的代码写于2013年。也就是说实际上所有版本中都存在单个字节越界读取和越界写入的问题。然而,他的 exploit 利用了 _fcgi_data_seg 结构存在的情况以及相关的哈希表优化。因此7.0、7.1、7.2、7.3 版本均受影响。
补丁
10月2日,PHP 官方发布补丁,如下:
https://bugs.php.net/patch-display.php?bug_id=78599&patch=0001-Fix-bug-78599-env_path_info-underflow-can-lead-to-RC.patch&revision=latest
https://bugs.php.net/bug.php?id=78599
https://github.com/neex/phuip-fpizdam
奇安信代码卫士 (codesafe)
国内首个专注于软件开发安全的产品线。