fastcgi(9000端口):
以下这段话摘自p神
Fastcgi其实是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。 HTTP协议是浏览器和服务器中间件进行数据交换的协议,浏览器将HTTP头和HTTP体用某个规则组装成数据包,以TCP的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以HTTP协议的规则打包返回给服务器。 类比HTTP协议来说,fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议。Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件
http数据实际上是由客户端浏览器到达服务器中间件,中间件再将数据再次封装打包给后端语言进行处理,处理结束再返回给服务器中间件。
php-fpm(fastcgi进程管理器):
FPM其实是一个fastcgi协议解析器,Nginx等服务器中间件将用户请求按照fastcgi的规则打包好通过TCP传给FPM。FPM按照fastcgi的协议将TCP流解析成真正的数据。
用户访问http://127.0.0.1/index.php?a=1&b=2
,如果web目录是/var/www/html
,那么Nginx会将这个请求变成如下key-value对:
PHP-FPM拿到fastcgi的数据包后,进行解析,得到上述这些环境变量。然后,执行SCRIPT_FILENAME
的值指向的PHP文件,也就是/var/www/html/index.php
。
比如解析漏洞就和PHP-FPM的处理配置有关系,主要在于其对script_filename的处理方式,比如/ppp.png/.php,从右向左解析,不存在/.php,则去除/.php继续向左,存在ppp.png,拿来当作php解析,这种为客户考虑的想法却导致了漏洞的存在
正确的解决方法有两种:
一是在Nginx端使用fastcgi_split_path_info将path info信息去除后,用tryfiles判断文件是否存在;
二是借助PHP-FPM的security.limit_extensions配置项,避免其他后缀文件被解析。
PHP-FPM未授权访问漏洞主要利用security.limit_extensions配置,PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造fastcgi协议,和fpm进行通信。当然如果在内网中开放的此端口,如果存在SSRF,就能通过SSRF去打fastcgi。
php-fpm根据script_filename的值来执行php文件,如果该文件不存在,则返回404
其限定了只有某些后缀的文件允许被fpm执行,默认是.php,由于这个配置项的限制,如果想利用PHP-FPM的未授权访问漏洞,首先就得找到一个已存在的PHP文件。
通常使用源安装php的时候,服务器上都会附带一些php后缀的文件,可以使用find / -name "*.php"
来全局搜索一下默认环境就能找到一些。接下来已经知道服务器可以来执行我们要指定的php,但只是执行服务器上的文件,不能执行我们的恶意payload,那么接下来就要想办法构造代码执行:
PHP.INI中有两个重要配置项: auto_prepend_file: auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件; auto_append_file: auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。 假设我们设置auto_prepend_file为php://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在Body中,他们就能被执行了。
(需要开启远程文件包含选项allow_url_include) 但是上面两个值是在服务器端,我们可以通过PHP-FPM来设置auto_prepend_file的值,这又涉及到PHP-FPM的两个环境变量,PHP_VALUE和PHP_ADMIN_VALUE。
这两个环境变量就是用来设置PHP配置项的,PHP_VALUE可以设置模式为PHP_INI_USER和PHP_INI_ALL的选项,PHP_ADMIN_VALUE可以设置所有选项。
(disable_functions除外,这个选项是PHP加载的时候就确定了,在范围内的函数直接不会被加载到PHP上下文中)
比如给php-fpm传递如下变量:
{ 'GATEWAY_INTERFACE': 'FastCGI/1.0', 'REQUEST_METHOD': 'GET', 'SCRIPT_FILENAME': '/var/www/html/index.php', 'SCRIPT_NAME': '/index.php', 'QUERY_STRING': '?a=1&b=2', 'REQUEST_URI': '/index.php?a=1&b=2', 'DOCUMENT_ROOT': '/var/www/html', 'SERVER_SOFTWARE': 'php/fcgiclient', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '12345', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': "localhost", 'SERVER_PROTOCOL': 'HTTP/1.1' 'PHP_VALUE': 'auto_prepend_file = php://input', 'PHP_ADMIN_VALUE': 'allow_url_include = On' }
设置auto_prepend_file = php://input
且allow_url_include = On
,然后将我们需要执行的代码放在Body中,即可执行任意代码。
用gopherus就可以直接生成payload,需要指定一个script_filename,即一个服务器上已经存在的文件,如果没有指定将使用默认的一个php,/usr/share/php/PERL.php,实际渗透过程中可能需要通过网站报错,phpinfo信息泄露等去找其存在的php文件,还要输入我们要执行的系统命令,这个payload实际上利用的正是p牛说的,auto_prepend_file一个php://input,从而在http body中去传递我们的payload,如前文所说动态地修改了php.ini里的配置:
env["REQUEST_METHOD"] = "POST" env["PHP_VALUE"] = "auto_prepend_file = php://input" env["PHP_ADMIN_VALUE"] = "allow_url_include = On disable_functions = safe_mode = Off"
测试:
环境:
https://github.com/vulhub/vulhub/tree/master/fpm
路径改为p牛环境中配的路径,直接用gopherus生成gopher的payload打,ubuntu16.04的curl的liburl为7.47,不存在%00截断,因此直接通过gopher发送payload到本机的9000端口
也可以直接用p牛的脚本直接打,直接向127.0.0.1的9000端口发送payload,这里假设是公网的fastcgi端口未授权访问:
脚本链接地址:
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
注意:
curl/libcurl 7.43
上 gopher 协议存在 bug(%00 截断),经测试 7.49 可用
并不限于 PHP 的 SSRF。当存在 XXE、ffmepg SSRF 等漏洞的时候,也可以进行利用
FPM模式:
一般来说,apache服务器常用module方式起php,nginx服务器常用fastcgi模式起php,php-fpm下还可以继续分,如果使用fastcgi模式,nginx与php-fpm通信可以通过两种模式,一种是TCP模式,一种是unix 套接字(socket)模式
TCP模式即是php-fpm进程会监听本机上的一个端口(默认9000),然后nginx会把客户端数据通过fastcgi协议传给9000端口,php-fpm拿到数据后会调用cgi进程解析
unix socket其实严格意义上应该叫unix domain
socket,它是unix系统进程间通信(IPC)的一种被广泛采用方式,以文件(一般是.sock)作为socket的唯一标识(描述符),需要通信的两个进程引用同一个socket描述符文件就可以建立通道进行通信了。
$fp = stream_socket_client("/var/run/php/U_wi11_nev3r_kn0w.sock", $errno, $errstr,30);$out = urldecode("这里填写payload"); stream_socket_sendto($fp,$out); while (!feof($fp)) {echo htmlspecialchars(fgets($fp, 10)); }fclose($fp);//'
利用以上代码就可以与unix套接字进行通信,来进行rce
PHP_ADMIN_VALUE['extension'] = /tmp/tr1ple.so 让php加载扩展
memcache(监听11211端口):
memcached是一套分布式的高速缓存系统。它以Key-Value(键值对)形式将数据存储在内存中,这些数据通常是应用读取频繁的。正因为内存中数据的读取远远大于硬盘,因此可以用来加速应用的访问。
漏洞成因:
由于memcached安全设计缺陷,客户端连接memcached服务器后无需认证就可读取、修改服务器缓存内容。
漏洞影响:
除memcached中数据可被直接读取泄漏和恶意修改外,由于memcached中的数据像正常网站用户访问提交变量一样会被后端代码处理,当处理代码存在缺陷时会再次导致不同类型的安全问题。 不同的是,在处理前端用户直接输入的数据时一般会接受更多的安全校验,而从memcached中读取的数据则更容易被开发者认为是可信的,或者是已经通过安全校验的,因此更容易导致安全问题。
具体案例分析见:
DiscuzX 两处 SSRF 挖掘及利用
https://zhuanlan.zhihu.com/p/51907363
Jenkins(默认8080) 未授权访问
默认情况下 Jenkins 面板中用户可以选择执行脚本界面来操作一些系统层命令,攻击者可通过未授权访问漏洞或者暴力破解用户密码等进脚本执行界面从而获取服务器权限。
详情见:https://paper.seebug.org/409/#0x03-jenkins
MongoDB未授权访问
开启 MongoDB 服务时不添加任何参数时,默认是没有权限验证的,而且可以远程访问数据库,登录的用户可以通过默认端口无需密码对数据库进行增、删、改、查等任意高危操作。
详情见:https://paper.seebug.org/409/#0x04-mongodb
ZooKeeper (默认端口2181)未授权访问
Zookeeper 的默认开放端口是2181。Zookeeper 安装部署之后默认情况下不需要任何身份验证,造成攻击者可以远程利用 Zookeeper,通过服务器收集敏感信息或者在 Zookeeper 集群内进行破坏(比如:kill命令)。攻击者能够执行所有只允许由管理员运行的命令。
详情见:https://paper.seebug.org/409/#0x05-zookeeper
Elasticsearch(默认9200端口)未授权访问
Elasticsearch 是一款 java 编写的企业级搜索服务。越来越多的公司使用 ELK 作为日志分析,启动此服务默认会开放9200端口,可被非法操作数据。
详情见:https://paper.seebug.org/409/#0x06-elasticsearch
CouchDB (默认5984端口)未授权访问
CouchDB 默认在 5984 端口开放 Restful 的 API 接口,用于数据库的管理功能。其 HTTP Server 默认开启时没有进行验证,而且绑定在0.0.0.0,所有用户均可通过 API 访问导致未授权访问。任何连接到服务器端口上的人,都可以调用相关 API 对服务器上的数据进行任意的增删改查,其中通过 API 修改 local.ini 配置文件,可进一步导致执行任意系统命令,获取服务器权限
详情见:https://paper.seebug.org/409/#0x09-couchdb
Docker 未授权访问
Docker Remote API 是一个取代远程命令行界面(rcli)的REST API。通过 docker client 或者 http 直接请求就可以访问这个 API,通过这个接口,我们可以新建 container,删除已有 container,甚至是获取宿主机的 shell
详情见:https://paper.seebug.org/409/#0x010-docker
参考:
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html
https://paper.seebug.org/409/