FastCgi协议&PHP-FPM未授权导致RCE
参考: https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html 离别歌
Fastcgi
要说PHP-FPM,首先就要说一下Fastcgi协议.
Fastcgi其实是一个和HTTP本质一样的通信协议。
HTTP用于浏览器和服务器中间件通信,Fastcgi用于服务器中间件与某个语言后端通信。
Fastcgi协议由多个record组成,record由header和body组成。
服务器中间件将body和header按照fastcgi规则封装好发送给语言后端,后端解码后拿到具体数据进行指定的操作,再按fastcgi协议封装号结果返回给服务器
record Header固定8个字节,每个变量一个字节
Body分为两类:真正的内容数据,和额外数据(非必须)
一个fastcgi record结构最大支持2^16=65536字节的body
typedef struct { |
Fastcgi type
也就是一个record的type变量。type用于表明该record的作用,以下是type主要的一些值type
就是指定该record的作用。因为fastcgi一个record的大小是有限的,作用也是单一的,所以我们需要在一个TCP流里传输多个record。通过type
来标志每个record的作用,用requestId
作为同一次请求的id。
其中type=4 对我们接下来讲PHP-FPM有重要作用,他有四个不同的结构
typedef struct { |
- key、value均小于128字节,用
FCGI_NameValuePair11
- key大于128字节,value小于128字节,用
FCGI_NameValuePair41
- key小于128字节,value大于128字节,用
FCGI_NameValuePair14
- key、value均大于128字节,用
FCGI_NameValuePair44
PHP-FPM
FPM是Fastcgi协议的解析器.中间件以fastcgi协议把用户传来的数据封装传给FPM。下面这个图就是fastcgi协议的模样
键值对.即fastcgi的type=4,这就是上面专门说这个的目的.
其中script_filename只向要执行的php文件
Nginx(IIS7)解析漏洞深入
以前记录过这个中间件漏洞,但没有详细的去了解为什么。这里就说说
在php fix_pathinfo开启的情况下,传入 url/1.txt/.php时,1.txt会被当作php文件解析.
究其原因,是因为配置文件中 security.limit_extensions默认限定了.php后缀文件才交给php-fpm处理,传入给fpm的数据是类似这样的
按理说应该报错404吧,但是fix_pathinfo会判断这个SCRIPT_FILENAME是否存在,若不存在就会去掉最后一个/后面的内容再次判断,知道文件存在为止,再把该文件当作PHP文件执行
RCE
上面那个解析漏洞只是个题外话。
我们来讲讲RCE。服务器默认PHP-FPM端口是9000,如果这个端口暴露在公网,我们就可以自己构造fastcgi协议与fpm通信.
此时我们就能想出rce的雏形,控制SCRIPT_FILENAME去执行我们的shell,反弹个shell什么的,但是前提是我们必须得上传一个shell上去,太笨比,于是我们继续思考.
上面提到我们可以通过fastcgi协议临时更改PHP的一些配置项(环境参数).我们不如把 auto_prepend_file
或auto_append_file
(自动包含某文件) 设置为php://input(需allow_url_include=on),然后SCRIPT_FILENAME设置为任意一个服务器上存在的PHP文件(PHP文件不仅仅在服务器目录才会有,PHP程序目录下也会有PHP文件),即可通过控制POST的包体来实现RCE。就像这样
exp: https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75