----已搬运------浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法--------学习子自大佬的教程 ftp那里没做完

本文深入探讨了FastCGI协议及其与PHP-FPM的关系,介绍了CGI的背景、FastCGI的高效特性以及Web服务器处理静态/动态网页的过程。文章详细阐述了FastCGI协议的Record结构、Type类型,同时讲解了PHP-FPM的工作原理和配置,特别是未授权访问漏洞的形成和利用方法。此外,还讨论了如何利用SSRF攻击PHP-FPM/FastCGI以及FTP-SSRF场景下的攻击策略。
摘要由CSDN通过智能技术生成

学习自:WHOAMI大佬的。
过程中有报错,也能够解决了。很棒很棒的教程。!!!

1. 前言

2. CGI

早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器。随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能够直接运行动态脚本。了解决Web服务器外部应用程序(CGI程序)a之间数据互通,于是出现了 CGI (Common Gateway Interface) 通用网关接口。简理理解,可以认为 CGI 是 Web 服务器和运行在其上的应用程序进行 “交流”的一种约定,

当遇到动态脚本请求时,Web服务器主进程就会Fork创建出一个新的进程启动CGI程序,运行外部C程序或Perl、PHP脚本等,也就是将动态脚本交给CGI程序来处理。启动CGI程序需要一个过程,如读取配置文件、加载扩展等。当CGI程序启动后会去解析动态脚本,然后将结果返回给Web服务器,最后由Web服务器将结果返回给客户端,之前Fork出来的进程也随之关闭。这样,每次用户请求动态脚本,Web服务器都要重新Fork创建一个新进程去启动CGI程序,由CGI程序来处理动态脚本,处理完成后进程随之关闭,其效率是非常低下的。

而对于Mod CGI,Web服务器可以内置Perl解释器或PHP解释器。也就是说将这些解释器做成模块的方式,Web服务器会在启动的时候就启动这些解释器。 当有新的动态请求进来时,Web服务器就是自己解析这些动态脚本,省得重新Fork一个进程,效率提高了。

3. FastCGI

有了CGI,自然就解决了Web服务器与PHP解释器的通信问题,但是Web服务器有一个问题,就是它每收到一个请求,都会去Fork一个CGI进程,请求结束再kill掉这个进程,这样会很浪费资源。于是,便出现了CGI的改良版本——Fast-CGI。

维基百科对 FastCGI 的解释是:快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,Fast-CGI每次处理完请求后,不会kill掉这个进程,而是保留这个进程,从而使服务器可以同时处理更多的网页请求。这样就会大大的提高效率。

4. 浏览器处理静态/动态网页过程

众所周知,在网站分类中存在一种分类就是静态网站和动态网站,两者的区别就是静态网站只需要通过浏览器进行解析,其中的页面是一对一的(一个内容对应一个页面),
而动态网站需要一个额外的编译解析的过程,网页上的数据是从数据库中或者其他地方调用,页面会随着数据的变化而改变,就产生了一定的交互性。

4.1 浏览器访问静态网页过程

在整个网页的访问过程中,Web容器(例如Apache、Nginx)只担任着内容分发者的身份,当访问静态网站的主页时,Web容器会到网站的相应目录中查找主页文件,然后发送给用户的浏览器。
在这里插入图片描述

4.2浏览器访问动态网页过程

当访问动态网站的主页时,根据容器的配置文件,它知道这个页面不是静态页面,Web容器就会去找PHP解析器来进行处理(这里以Apache为例),它会把这个请求进行简单的处理,然后交给PHP解释器。

在这里插入图片描述当Apache收到用户对 index.php 的请求后,如果使用的是CGI,会启动对应的 CGI 程序,对应在这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境。然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。Web Server 再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程。(这里的web server就是 apache )

这里说的是使用CGI,而FastCGI就相当于高性能的CGI,与CGI不同的是它像一个常驻的CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次,所以这里引出下面这句概念,FastCGI是语言无关的、可伸缩架构的CGI开放扩展 , 其主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能

5. Fastcgi 协议分析

5.1 Fastcgi Record

Fastcgi 其实是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。

HTTP 是 浏览器服务器中间件,是一个进行数据交换的协议,浏览器
将 HTTP头 和 HTTP体 用某个规则组装成数据包, 以 TCP 的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以 HTTP 协议的规则打包返回给服务器。

服务器,中间件的一些讲解

类比 HTTP 协议来说, Fastcgi协议则是 服务器中间件某个语言后端进行数据交换的协议. 。 Fastcgi 协议由多个 Record 组成,Record 也有 Header 和 Body 一说,服务器中间件将这二者按照 Fastcgi的规则封装好发送给语言后端
语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照Fastcgi 协议封装好后返回给服务器中间件

和 HTTP 头不同,Fastcgi Record 的头固定8个字节,Body 是由头中的 contentLength 指定,其结构如下:

typedef struct {
   
  /* Header 消息头信息 */
  unsigned char version; // 用于表示 FastCGI 协议版本号
  unsigned char type; // 用于标识 FastCGI 消息的类型, 即用于指定处理这个消息的方法
  unsigned char requestIdB1; // 用ID值标识出当前所属的 FastCGI 请求
  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;

头由8个 uchar 类型的变量组成,每个变量一个字节。其中,requestId 占两个字节,一个唯一的标志id,以避免多个请求之间的影响;contentLength 占两个字节,表示 Body 的大小。可见,一个 Fastcgi Record 结构最大支持的 Body 大小是2^16,也就是 65536 字节。

后端语言解析了 Fastcgi 头以后,拿到 contentLength,然后再在请求的 TCP 流里读取大小等于 contentLength 的数据,这就是 Body 体。

Body 后面还有一段额外的数据(Padding),其长度由头中的 paddingLength 指定,起保留作用。不需要该Padding的时候,将其长度设置为0即可。

5.2 Fastcgi Type

刚才我们介绍了 Fastcgi 协议中Record部分中各个结构的含义,其中第二个字节为 type,我们将对其进行详细讲解。

type 就是指定该 Record 的作用。因为 Fastcgi 中一个 Record 的大小是有限的,作用也是单一的,所以我们需要在一个TCP流里传输多个 Record,通过 type 来标志每个 Record 的作用,并用 requestId 来标识同一次请求的id。也就是说,每次请求,会有多个 Record,他们的 requestId 是相同的。

下面给出一个表格,列出最主要的几种 type:

在这里插入图片描述看了这个表格就很清楚了,服务器中间件和后端语言通信,第一个数据包就是 type 为1的 Record,后续互相交流,发送 type 为4、5、6、7的 Record,结束时发送 type 为2、3的 Record。

当后端语言接收到一个 type 为4的 Record 后,就会把这个 Record 的 Body 按照对应的结构解析成 key-value 对,这就是环境变量。环境变量的结构如下:

typedef struct {
   
  unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
  unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
  unsigned char nameData[nameLength];
  unsigned char valueData[valueLength];
} FCGI_NameValuePair11;

typedef struct {
   
  unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
  unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
  unsigned char valueLengthB2;
  unsigned char valueLengthB1;
  unsigned char valueLengthB0;
  unsigned char nameData[nameLength];
  unsigned char valueData[valueLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair14;

typedef struct {
   
  unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
  unsigned char nameLengthB2;
  unsigned char nameLengthB1;
  unsigned char nameLengthB0;
  unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
  unsigned char nameData[nameLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
  unsigned char valueData[valueLength];
} FCGI_NameValuePair41;

typedef struct {
   
  unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
  unsigned char nameLengthB2;
  unsigned char nameLengthB1;
  unsigned char nameLengthB0;
  unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
  unsigned char valueLengthB2;
  unsigned 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值