PHP-fpm 远程代码执行漏洞(CVE-2019-11043)分析

做者:LoRexxar'@知道创宇404实验室
时间:2019年10月25日
原文连接:https://paper.seebug.org/1063/php

国外安全研究员 Andrew Danau在解决一道 CTF 题目时发现,向目标服务器 URL 发送 %0a 符号时,服务返回异常,疑似存在漏洞。html

2019年10月23日,github公开漏洞相关的详情以及exp。当nginx配置不当时,会致使php-fpm远程任意代码执行。nginx

下面咱们就来一点点看看漏洞的详细分析,文章中漏洞分析部分感谢团队小伙伴@Hcamael#知道创宇404实验室git

漏洞复现

为了能更方便的复现漏洞,这里咱们采用vulhub来构建漏洞环境。github

https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043

git pulldocker-compose up -d正则表达式

访问http://{your_ip}:8080/docker

6.png
下载github上公开的exp(须要go环境)。ubuntu

go get github.com/neex/phuip-fpizdam

而后编译c#

go install github.com/neex/phuip-fpizdam

使用exp攻击demo网站api

phuip-fpizdam http://{your_ip}:8080/

7.png8.png

攻击成功

漏洞分析

在分析漏洞原理以前,咱们这里能够直接跟入看修复的commit

-https://github.com/php/php-src/commit/ab061f95ca966731b1c84cf5b7b20155c0a1c06a#diff-624bdd47ab6847d777e15327976a9227

9.png

从commit中咱们能够很清晰的看出来漏洞成因应该是path_info的地址可控致使的,再结合漏洞发现者公开的漏洞信息中提到

The regexp in `fastcgi_split_path_info` directive can be broken using the newline character (in encoded form, %0a). Broken regexp leads to empty PATH_INFO, which triggers the bug.

也就是说,当path_info被%0a截断时,path_info将被置为空,回到代码中我就不难发现问题所在了。

10.png
其中env_path_info就是变量path_info的地址,path_info为0则plien为0.

slen变量来自于请求后url的长度

int ptlen = strlen(pt);
    int slen = len - ptlen;

其中

int len = script_path_translated_len;

len为url路径长度
当请求url为http://127.0.0.1/index.php/123%0atest.php
script_path_translated来自于nginx的配置,为/var/www/html/index.php/123\ntest.php

ptlen则为url路径第一个斜杠以前的内容长度
当请求url为http://127.0.0.1/index.php/123%0atest.php
pt为/var/www/html/index.php

这两个变量的差就是后面的路径长度,因为路径可控,则path_info可控。

11.png

因为path_info可控,在1222行咱们就能够将指定地址的值置零,根据漏洞发现者的描述,经过将指定的地址的值置零,能够控制使_fcgi_data_seg结构体的char* pos置零。

12.png

其中script_name一样来自于请求的配置
13.png

而为何咱们使_fcgi_data_seg结构体的char* pos置零,就会影响到FCGI_PUTENV的结果呢?

这里咱们深刻去看FCGI_PUTENV的定义.

char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val);

跟入函数fcgi_quick_putenv

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L1703

14.png
函数直接操做request的env,而这个参数在前面被预约义。

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L908

15.png

继续跟进初始化函数fcgi_hash_init.

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L254

16.png

也就是说request->env就是前面提到的fcgi_data_seg结构体,而这里的request->env是nginx在和fastcgi通讯时储存的全局变量。

部分全局变量会在nginx的配置中定义
17.png

其中变量会在堆上相应的位置储存
18.png
回到利用过程当中,这里咱们经过控制path_info指向request->env来使request->env->pos置零。

继续回到赋值函数fcgi_hash_set函数
19.png

紧接着进入fcgi_hash_strndup

20.png

这里h->data-》pos的最低位被置为0,且str可控,就至关于咱们能够在前面写入数据。

而问题就在于,咱们怎么能向咱们想要的位置写数据呢?又怎么向咱们指定的配置写文件呢?

这里咱们拿exp发送的利用数据包作例子

GET /index.php/PHP_VALUE%0Asession.auto_start=1;;;?QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ HTTP/1.1
Host: ubuntu.local:8080
User-Agent: Mozilla/5.0
D-Gisos: 8=====================================D
Ebut: mamku tvoyu

在数据包中,header中的最后两部分就是为了完成这部分功能,其中D-Gisos负责位移,向指定的位置写入数据。

Ebut会转化为HTTP_EBUT这个fastcgi_param中的其中一个全局变量,而后咱们须要了解一下fastcgi中全局变量的获取数据的方法。

https://github.com/php/php-src/blob/5d6e923d46a89fe9cd8fb6c3a6da675aa67197b4/main/fastcgi.c#L328

21.png
能够看到当fastcgi想要获取全局变量时,会读取指定位置的长度字符作对比,而后读取一个字符串做为value.

也就是说,只要位置合理,var值相同,且长度相同,fastcgi就会读取相对应的数据

HTTP_EBUTPHP_VALUE刚好长度相同,咱们能够从堆上数据的变化来印证这一点。

在覆盖以前,该地址对应数据为
22.png

而后执行fcgi_quick_putenv
23.png

该地址对应数据变为
24.png

咱们成功写入了PHP_VALUE并控制其内容,这也就意味着咱们能够控制PHP的任意全局变量。

当咱们能够控制PHP的任意全局变量就有不少种攻击方式,这里直接以EXP中使用到的攻击方式来举例子。
25.png

exp做者经过开启自动包含,并设置包含目录为/tmp,以后设置log地址为/tmp/a并将payload写入log文件,经过auto_prepend_file自动包含/tmp/a文件构造后门文件。

漏洞修复

在通过对漏洞的深刻研究后,咱们推荐两种方案修复这个漏洞。

  • 临时修复:

修改nginx相应的配置,并在php相关的配置中加入

try_files $uri =404

在这种状况下,会有nginx去检查文件是否存在,当文件不存在时,请求都不会被传递到php-fpm。

漏洞影响

结合EXP github中提到的利用条件,咱们能够尽量的总结利用条件以及漏洞影响范围。

一、Nginx + php_fpm,且配置location ~ [^/]\.php(/|$)会将请求转发到php-fpm。
二、Nginx配置fastcgi_split_path_info而且以^开始以$,只有在这种条件下才能够经过换行符来打断正则表达式判断。 ps: 则容许index.php/321 -> index.php

fastcgi_split_path_info ^(.+?\.php)(/.*)$;

三、fastcgi_paramPATH_INFO会被定义经过fastcgi_param PATH_INFO $fastcgi_path_info;,固然这个变量会在fastcgi_params默认定义。
四、在nginx层面没有定义对文件的检查好比try_files $uri =404,若是nginx层面作了文件检查,则请求不会被转发给php-fmp。

这个漏洞在实际研究过程当中对真实世界危害有限,其主要缘由都在于大部分的nginx配置中都携带了对文件的检查,且默认的nginx配置不包含这个问题。

但也正是因为这个缘由,在许多网上的范例代码或者部分没有考虑到这个问题的环境,例如Nginx官方文档中的范例配置、NextCloud默认环境,都出现了这个问题,该漏洞也正真实的威胁着许多服务器的安全。

在这种状况下,这个漏洞也切切实实的陷入了黑暗森林法则,一旦有某个带有问题的配置被传播,其致使的可能就是大批量的服务受到牵连,确保及时的更新永远是对保护最好的手段:>

参考连接

如需转载请注明来源

相关文章
相关标签/搜索